diff options
Diffstat (limited to 'framework/Data')
37 files changed, 1288 insertions, 327 deletions
diff --git a/framework/Data/ActiveRecord/Exceptions/TActiveRecordException.php b/framework/Data/ActiveRecord/Exceptions/TActiveRecordException.php index 45a575dd..8654c35c 100644 --- a/framework/Data/ActiveRecord/Exceptions/TActiveRecordException.php +++ b/framework/Data/ActiveRecord/Exceptions/TActiveRecordException.php @@ -18,7 +18,7 @@ * @package System.Data.ActiveRecord
* @since 3.1
*/
-class TActiveRecordException extends TException
+class TActiveRecordException extends TDbException
{
/**
* @return string path to the error message file
diff --git a/framework/Data/ActiveRecord/Exceptions/messages.txt b/framework/Data/ActiveRecord/Exceptions/messages.txt index 9ab1693e..b3d567e4 100644 --- a/framework/Data/ActiveRecord/Exceptions/messages.txt +++ b/framework/Data/ActiveRecord/Exceptions/messages.txt @@ -16,4 +16,5 @@ ar_pk_value_count_mismatch = Composite key value count mismatch in forming I ar_must_copy_from_array_or_object = $data in {0}::copyFrom($data) must be an object or an array.
ar_mismatch_column_names = In dynamic __call() method '{0}', no matching columns were found, valid columns for table '{2}' are '{1}'.
ar_invalid_table = Missing, invalid or no permission for table/view '{0}'.
-ar_invalid_finder_class_name = Class name for finder($className) method must not be 'TActiveRecord', you should override the finder() method in your record class or pass in a valid record class name.
\ No newline at end of file +ar_invalid_finder_class_name = Class name for finder($className) method must not be 'TActiveRecord', you should override the finder() method in your record class or pass in a valid record class name.
+ar_invalid_criteria = Invalid criteria object, must be a string or instance of TSqlCriteria.
diff --git a/framework/Data/ActiveRecord/Scaffold/InputBuilder/TIbmScaffoldInput.php b/framework/Data/ActiveRecord/Scaffold/InputBuilder/TIbmScaffoldInput.php index ada2091e..2c315468 100644 --- a/framework/Data/ActiveRecord/Scaffold/InputBuilder/TIbmScaffoldInput.php +++ b/framework/Data/ActiveRecord/Scaffold/InputBuilder/TIbmScaffoldInput.php @@ -13,7 +13,7 @@ class TIbmScaffoldInput extends TScaffoldInputCommon {
protected function createControl($container, $column, $record)
{
- switch(strtolower($column->getType()))
+ switch(strtolower($column->getDbType()))
{
case 'date':
return $this->createDateControl($container, $column, $record);
@@ -34,7 +34,7 @@ class TIbmScaffoldInput extends TScaffoldInputCommon protected function getControlValue($container, $column, $record)
{
- switch(strtolower($column->getType()))
+ switch(strtolower($column->getDbType()))
{
case 'date':
return $container->findControl(self::DEFAULT_ID)->getDate();
@@ -43,7 +43,7 @@ class TIbmScaffoldInput extends TScaffoldInputCommon case 'timestamp':
return $this->getDateTimeValue($container, $column, $record);
default:
- return $this->getDefaultControlValue($container,$column, $record);
+ return $this->getDefaultControlValue($container,$column, $record);
}
}
}
diff --git a/framework/Data/ActiveRecord/Scaffold/InputBuilder/TMssqlScaffoldInput.php b/framework/Data/ActiveRecord/Scaffold/InputBuilder/TMssqlScaffoldInput.php new file mode 100644 index 00000000..840e5b63 --- /dev/null +++ b/framework/Data/ActiveRecord/Scaffold/InputBuilder/TMssqlScaffoldInput.php @@ -0,0 +1,43 @@ +<?php
+
+Prado::using('System.Data.ActiveRecord.Scaffold.InputBuilder.TScaffoldInputCommon');
+
+class TMssqlScaffoldInput extends TScaffoldInputCommon
+{
+ protected function createControl($container, $column, $record)
+ {
+ switch(strtolower($column->getDbType()))
+ {
+ case 'bit':
+ return $this->createBooleanControl($container, $column, $record);
+ case 'text':
+ return $this->createMultiLineControl($container, $column, $record);
+ case 'smallint': case 'int': case 'bigint': case 'tinyint':
+ return $this->createIntegerControl($container, $column, $record);
+ case 'decimal': case 'float': case 'money': case 'numeric': case 'real': case 'smallmoney':
+ return $this->createFloatControl($container, $column, $record);
+ case 'datetime': case 'smalldatetime':
+ return $this->createDateTimeControl($container, $column, $record);
+ default:
+ $control = $this->createDefaultControl($container,$column, $record);
+ if($column->getIsExcluded())
+ $control->setEnabled(false);
+ return $control;
+ }
+ }
+
+ protected function getControlValue($container, $column, $record)
+ {
+ switch(strtolower($column->getDbType()))
+ {
+ case 'boolean':
+ return $container->findControl(self::DEFAULT_ID)->getChecked();
+ case 'datetime': case 'smalldatetime':
+ return $this->getDateTimeValue($container,$column, $record);
+ default:
+ return $this->getDefaultControlValue($container,$column, $record);
+ }
+ }
+}
+
+?>
\ No newline at end of file diff --git a/framework/Data/ActiveRecord/Scaffold/InputBuilder/TMysqlScaffoldInput.php b/framework/Data/ActiveRecord/Scaffold/InputBuilder/TMysqlScaffoldInput.php index 89df0f2e..42f8cead 100644 --- a/framework/Data/ActiveRecord/Scaffold/InputBuilder/TMysqlScaffoldInput.php +++ b/framework/Data/ActiveRecord/Scaffold/InputBuilder/TMysqlScaffoldInput.php @@ -6,7 +6,8 @@ class TMysqlScaffoldInput extends TScaffoldInputCommon {
protected function createControl($container, $column, $record)
{
- switch(strtolower($column->getType()))
+ $dbtype = trim(str_replace(array('unsigned', 'zerofill'),array('','',),strtolower($column->getDbType())));
+ switch($dbtype)
{
case 'date':
return $this->createDateControl($container, $column, $record);
@@ -34,7 +35,8 @@ class TMysqlScaffoldInput extends TScaffoldInputCommon protected function getControlValue($container, $column, $record)
{
- switch(strtolower($column->getType()))
+ $dbtype = trim(str_replace(array('unsigned', 'zerofill'),array('','',),strtolower($column->getDbType())));
+ switch($dbtype)
{
case 'date':
return $container->findControl(self::DEFAULT_ID)->getDate();
@@ -57,7 +59,7 @@ class TMysqlScaffoldInput extends TScaffoldInputCommon protected function createIntegerControl($container, $column, $record)
{
- if($column->getLength()==1)
+ if($column->getColumnSize()==1)
return $this->createBooleanControl($container, $column, $record);
else
parent::createIntegerControl($container, $column, $record);
@@ -65,7 +67,7 @@ class TMysqlScaffoldInput extends TScaffoldInputCommon protected function getIntBooleanValue($container,$column, $record)
{
- if($column->getLength()==1)
+ if($column->getColumnSize()==1)
return (int)$container->findControl(self::DEFAULT_ID)->getChecked();
else
return $this->getDefaultControlValue($container,$column, $record);
diff --git a/framework/Data/ActiveRecord/Scaffold/InputBuilder/TPgsqlScaffoldInput.php b/framework/Data/ActiveRecord/Scaffold/InputBuilder/TPgsqlScaffoldInput.php index f5e11eae..a024c73b 100644 --- a/framework/Data/ActiveRecord/Scaffold/InputBuilder/TPgsqlScaffoldInput.php +++ b/framework/Data/ActiveRecord/Scaffold/InputBuilder/TPgsqlScaffoldInput.php @@ -6,7 +6,7 @@ class TPgsqlScaffoldInput extends TScaffoldInputCommon {
protected function createControl($container, $column, $record)
{
- switch(strtolower($column->getType()))
+ switch(strtolower($column->getDbType()))
{
case 'boolean':
return $this->createBooleanControl($container, $column, $record);
@@ -29,7 +29,7 @@ class TPgsqlScaffoldInput extends TScaffoldInputCommon protected function getControlValue($container, $column, $record)
{
- switch(strtolower($column->getType()))
+ switch(strtolower($column->getDbType()))
{
case 'boolean':
return $container->findControl(self::DEFAULT_ID)->getChecked();
diff --git a/framework/Data/ActiveRecord/Scaffold/InputBuilder/TScaffoldInputBase.php b/framework/Data/ActiveRecord/Scaffold/InputBuilder/TScaffoldInputBase.php index 05e83157..f9ca9180 100644 --- a/framework/Data/ActiveRecord/Scaffold/InputBuilder/TScaffoldInputBase.php +++ b/framework/Data/ActiveRecord/Scaffold/InputBuilder/TScaffoldInputBase.php @@ -27,6 +27,9 @@ class TScaffoldInputBase case 'pgsql':
require_once(dirname(__FILE__).'/TPgsqlScaffoldInput.php');
return new TPgsqlScaffoldInput($conn);
+ case 'mssql':
+ require_once(dirname(__FILE__).'/TMssqlScaffoldInput.php');
+ return new TMssqlScaffoldInput($conn);
case 'ibm':
require_once(dirname(__FILE__).'/TIbmScaffoldInput.php');
return new TIbmScaffoldInput($conn);
@@ -39,7 +42,7 @@ class TScaffoldInputBase public function createScaffoldInput($parent, $item, $column, $record)
{
$this->_parent=$parent;
- $item->setCustomData($column->getProperty());
+ $item->setCustomData($column->getColumnId());
$this->createControl($item->_input, $column, $record);
if($item->_input->findControl(self::DEFAULT_ID))
$this->createControlLabel($item->_label, $column, $record);
@@ -47,7 +50,7 @@ class TScaffoldInputBase protected function createControlLabel($label, $column, $record)
{
- $fieldname = ucwords(str_replace('_', ' ', $column->getProperty())).':';
+ $fieldname = ucwords(str_replace('_', ' ', $column->getColumnId())).':';
$label->setText($fieldname);
$label->setForControl(self::DEFAULT_ID);
}
@@ -57,7 +60,7 @@ class TScaffoldInputBase $this->_parent=$parent;
if($this->getIsEnabled($column, $record))
{
- $prop = $column->getProperty();
+ $prop = $column->getColumnId();
$record->{$prop} = $this->getControlValue($item->_input, $column, $record);
}
}
@@ -70,7 +73,11 @@ class TScaffoldInputBase protected function getRecordPropertyValue($column, $record)
{
- return $record->{$column->getProperty()};
+ $value = $record->{$column->getColumnId()};
+ if($column->getDefaultValue()!==TDbTableColumn::UNDEFINED_VALUE && $value===null)
+ return $column->getDefaultValue();
+ else
+ return $value;
}
protected function setRecordPropertyValue($item, $record, $input)
diff --git a/framework/Data/ActiveRecord/Scaffold/InputBuilder/TScaffoldInputCommon.php b/framework/Data/ActiveRecord/Scaffold/InputBuilder/TScaffoldInputCommon.php index 049ceb1f..e1d57124 100644 --- a/framework/Data/ActiveRecord/Scaffold/InputBuilder/TScaffoldInputCommon.php +++ b/framework/Data/ActiveRecord/Scaffold/InputBuilder/TScaffoldInputCommon.php @@ -12,7 +12,7 @@ class TScaffoldInputCommon extends TScaffoldInputBase protected function setNotNullProperty($container, $control, $column, $record)
{
$this->setDefaultProperty($container, $control, $column, $record);
- if($column->getNotNull() && !$column->hasSequence())
+ if(!$column->getAllowNull() && !$column->hasSequence())
$this->createRequiredValidator($container, $column, $record);
}
@@ -32,7 +32,7 @@ class TScaffoldInputCommon extends TScaffoldInputBase $control = new TTextBox();
$control->setText($value);
$control->setCssClass('default-textbox scaffold_input');
- if(($len=$column->getLength())!==null)
+ if(($len=$column->getColumnSize())!==null)
$control->setMaxLength($len);
$this->setNotNullProperty($container, $control, $column, $record);
return $control;
@@ -89,6 +89,14 @@ class TScaffoldInputCommon extends TScaffoldInputBase $val = $this->createTypeValidator($container, $column, $record);
$val->setDataType(TValidationDataType::Float);
$val->setErrorMessage('Please entery a decimal number.');
+ if(($max= $column->getMaxiumNumericConstraint())!==null)
+ {
+ $val = $this->createRangeValidator($container,$column,$record);
+ $val->setDataType(TValidationDataType::Float);
+ $val->setMaxValue($max);
+ $val->setStrictComparison(true);
+ $val->setErrorMessage('Please entery a decimal number strictly less than '.$max.'.');
+ }
return $control;
}
@@ -117,6 +125,18 @@ class TScaffoldInputCommon extends TScaffoldInputBase return $val;
}
+ protected function createRangeValidator($container, $column, $record)
+ {
+ $val = new TRangeValidator();
+ $val->setControlCssClass('required-input3');
+ $val->setCssClass('required');
+ $val->setControlToValidate(self::DEFAULT_ID);
+ $val->setValidationGroup($this->getParent()->getValidationGroup());
+ $val->setDisplay(TValidatorDisplayStyle::Dynamic);
+ $container->Controls[] = $val;
+ return $val;
+ }
+
protected function createTimeControl($container, $column, $record)
{
$value = $this->getRecordPropertyValue($column, $record);
@@ -222,10 +242,10 @@ class TScaffoldInputCommon extends TScaffoldInputBase $value = $this->getRecordPropertyValue($column, $record);
$selectedValues = preg_split('/\s*,\s*/', $value);
$control = new TCheckBoxList();
- $values = $column->getTypeValues();
+ $values = $column->getDbTypeValues();
$control->setDataSource($values);
$control->dataBind();
- $control->setSelectedIndices($this->getMatchingIndices($selectedValues, $values));
+ $control->setSelectedIndices($this->getMatchingIndices($values,$selectedValues));
$control->setID(self::DEFAULT_ID);
$control->setCssClass('set-checkboxes');
$this->setNotNullProperty($container, $control, $column, $record);
@@ -248,10 +268,10 @@ class TScaffoldInputCommon extends TScaffoldInputBase $value = $this->getRecordPropertyValue($column, $record);
$selectedValues = preg_split('/\s*,\s*/', $value);
$control = new TRadioButtonList();
- $values = $column->getTypeValues();
+ $values = $column->getDbTypeValues();
$control->setDataSource($values);
$control->dataBind();
- $index = $this->getMatchingIndices($selectedValues, $values);
+ $index = $this->getMatchingIndices($values,$selectedValues);
if(count($index) > 0)
$control->setSelectedIndex($index[0]);
$control->setID(self::DEFAULT_ID);
diff --git a/framework/Data/ActiveRecord/Scaffold/InputBuilder/TSqliteScaffoldInput.php b/framework/Data/ActiveRecord/Scaffold/InputBuilder/TSqliteScaffoldInput.php index b26d44fd..a61da1f3 100644 --- a/framework/Data/ActiveRecord/Scaffold/InputBuilder/TSqliteScaffoldInput.php +++ b/framework/Data/ActiveRecord/Scaffold/InputBuilder/TSqliteScaffoldInput.php @@ -6,7 +6,7 @@ class TSqliteScaffoldInput extends TScaffoldInputCommon {
protected function createControl($container, $column, $record)
{
- switch(strtolower($column->getType()))
+ switch(strtolower($column->getDbType()))
{
case 'boolean':
return $this->createBooleanControl($container, $column, $record);
@@ -32,7 +32,7 @@ class TSqliteScaffoldInput extends TScaffoldInputCommon protected function getControlValue($container, $column, $record)
{
- switch(strtolower($column->getType()))
+ switch(strtolower($column->getDbType()))
{
case 'boolean':
return $container->findControl(self::DEFAULT_ID)->getChecked();
@@ -53,7 +53,7 @@ class TSqliteScaffoldInput extends TScaffoldInputCommon {
$control = parent::createDateControl($container, $column, $record);
$value = $this->getRecordPropertyValue($column, $record);
- if(!empty($value) && preg_match('/timestamp/i', $column->getType()))
+ if(!empty($value) && preg_match('/timestamp/i', $column->getDbType()))
$control->setTimestamp(intval($value));
return $control;
}
@@ -62,7 +62,7 @@ class TSqliteScaffoldInput extends TScaffoldInputCommon {
$value = $this->getRecordPropertyValue($column, $record);
$time = parent::createDateTimeControl($container, $column, $record);
- if(!empty($value) && preg_match('/timestamp/i', $column->getType()))
+ if(!empty($value) && preg_match('/timestamp/i', $column->getDbType()))
{
$s = Prado::createComponent('System.Util.TDateTimeStamp');
$date = $s->getDate(intval($value));
@@ -75,7 +75,7 @@ class TSqliteScaffoldInput extends TScaffoldInputCommon protected function getDateTimeValue($container, $column, $record)
{
- if(preg_match('/timestamp/i', $column->getType()))
+ if(preg_match('/timestamp/i', $column->getDbType()))
{
$time = $container->findControl(self::DEFAULT_ID)->getTimestamp();
$s = Prado::createComponent('System.Util.TDateTimeStamp');
diff --git a/framework/Data/ActiveRecord/Scaffold/TScaffoldBase.php b/framework/Data/ActiveRecord/Scaffold/TScaffoldBase.php index 266d4346..a9bf2d59 100644 --- a/framework/Data/ActiveRecord/Scaffold/TScaffoldBase.php +++ b/framework/Data/ActiveRecord/Scaffold/TScaffoldBase.php @@ -36,23 +36,15 @@ abstract class TScaffoldBase extends TTemplateControl * @var TActiveRecord record instance (may be new or retrieved from db)
*/
private $_record;
- /**
- * @var TDbMetaData table/view information.
- */
- private $_meta;
/**
* @return TDbMetaData table/view information
*/
- protected function getTableMetaData()
+ protected function getTableInfo()
{
- if($this->_meta===null)
- {
- $finder = $this->getRecordFinder();
- $gateway = $finder->getRecordManager()->getRecordGateWay();
- $this->_meta = $gateway->getMetaData($finder);
- }
- return $this->_meta;
+ $finder = $this->getRecordFinder();
+ $gateway = $finder->getRecordManager()->getRecordGateWay();
+ return $gateway->getRecordTableInfo($finder);
}
/**
@@ -62,7 +54,7 @@ abstract class TScaffoldBase extends TTemplateControl protected function getRecordPropertyValues($record)
{
$data = array();
- foreach($this->getTableMetaData()->getColumns() as $name=>$column)
+ foreach($this->getTableInfo()->getColumns() as $name=>$column)
$data[] = $record->{$name};
return $data;
}
@@ -73,7 +65,7 @@ abstract class TScaffoldBase extends TTemplateControl */
protected function getRecordPkValues($record)
{
- foreach($this->getTableMetaData()->getColumns() as $name=>$column)
+ foreach($this->getTableInfo()->getColumns() as $name=>$column)
{
if($column->getIsPrimaryKey())
$data[] = $record->{$name};
@@ -106,7 +98,6 @@ abstract class TScaffoldBase extends TTemplateControl protected function copyFrom(TScaffoldBase $obj)
{
$this->_record = $obj->_record;
- $this->_meta = $obj->_meta;
$this->setRecordClass($obj->getRecordClass());
}
@@ -116,7 +107,6 @@ abstract class TScaffoldBase extends TTemplateControl protected function clearRecordObject()
{
$this->_record=null;
- $this->_meta=null;
}
/**
diff --git a/framework/Data/ActiveRecord/Scaffold/TScaffoldEditView.php b/framework/Data/ActiveRecord/Scaffold/TScaffoldEditView.php index efc356f5..0550864b 100644 --- a/framework/Data/ActiveRecord/Scaffold/TScaffoldEditView.php +++ b/framework/Data/ActiveRecord/Scaffold/TScaffoldEditView.php @@ -114,7 +114,7 @@ class TScaffoldEditView extends TScaffoldBase $classPath = $this->getEditRenderer();
if($classPath === '')
{
- $columns = $this->getTableMetaData()->getColumns();
+ $columns = $this->getTableInfo()->getColumns();
$this->getInputRepeater()->setDataSource($columns);
$this->getInputRepeater()->dataBind();
}
@@ -199,7 +199,7 @@ class TScaffoldEditView extends TScaffoldBase $record = $this->getCurrentRecord();
if($this->_editRenderer===null)
{
- $table = $this->getTableMetaData();
+ $table = $this->getTableInfo();
$builder = $this->getScaffoldInputBuilder($record);
foreach($this->getInputRepeater()->getItems() as $item)
{
diff --git a/framework/Data/ActiveRecord/Scaffold/TScaffoldEditView.tpl b/framework/Data/ActiveRecord/Scaffold/TScaffoldEditView.tpl index 884ec2a3..b3289c09 100644 --- a/framework/Data/ActiveRecord/Scaffold/TScaffoldEditView.tpl +++ b/framework/Data/ActiveRecord/Scaffold/TScaffoldEditView.tpl @@ -3,7 +3,7 @@ <com:TRepeater ID="_repeater" onItemCreated="createRepeaterEditItem">
<prop:ItemTemplate>
<div class="edit-item item_<%# $this->ItemIndex % 2 %>
- input_<%# $this->ItemIndex %> property_<%# $this->DataItem->Property %>">
+ input_<%# $this->ItemIndex %> property_<%# $this->DataItem->ColumnId %>">
<com:TLabel ID="_label" CssClass="item-label"/>
<span class="item-input">
<com:TPlaceHolder ID="_input" />
diff --git a/framework/Data/ActiveRecord/Scaffold/TScaffoldListView.php b/framework/Data/ActiveRecord/Scaffold/TScaffoldListView.php index 6cb490e3..71dc83cd 100644 --- a/framework/Data/ActiveRecord/Scaffold/TScaffoldListView.php +++ b/framework/Data/ActiveRecord/Scaffold/TScaffoldListView.php @@ -65,7 +65,7 @@ class TScaffoldListView extends TScaffoldBase */
protected function initializeSort()
{
- $table = $this->getTableMetaData();
+ $table = $this->getTableInfo();
$sorts = array('Sort By', str_repeat('-',15));
$headers = array();
foreach($table->getColumns() as $name=>$colum)
diff --git a/framework/Data/ActiveRecord/Scaffold/TScaffoldSearch.php b/framework/Data/ActiveRecord/Scaffold/TScaffoldSearch.php index fdfddd4f..d1a5980f 100644 --- a/framework/Data/ActiveRecord/Scaffold/TScaffoldSearch.php +++ b/framework/Data/ActiveRecord/Scaffold/TScaffoldSearch.php @@ -93,7 +93,7 @@ class TScaffoldSearch extends TScaffoldBase */
protected function createSearchCondition()
{
- $table = $this->getTableMetaData();
+ $table = $this->getTableInfo();
if(strlen($str=$this->getSearchText()->getText()) > 0)
return $table->getSearchRegExpCriteria($this->getFields(), $str);
}
@@ -106,7 +106,7 @@ class TScaffoldSearch extends TScaffoldBase if(strlen(trim($str=$this->getSearchableFields()))>0)
$fields = preg_split('/\s*,\s*/', $str);
else
- $fields = array_keys($this->getTableMetaData()->getColumns());
+ $fields = array_keys($this->getTableInfo()->getColumns());
return $fields;
}
diff --git a/framework/Data/ActiveRecord/Scaffold/style.css b/framework/Data/ActiveRecord/Scaffold/style.css index a1fc7a16..864ddb6f 100644 --- a/framework/Data/ActiveRecord/Scaffold/style.css +++ b/framework/Data/ActiveRecord/Scaffold/style.css @@ -117,7 +117,7 @@ body font-weight: bold;
padding: 0.2em;
}
-.edit-inputs .required-input, .edit-inputs .required-input2
+.edit-inputs .required-input, .edit-inputs .required-input2 .required-input3 .required-input4
{
border: 1px solid red;
background-color: #FFF5EE;
diff --git a/framework/Data/ActiveRecord/TActiveRecord.php b/framework/Data/ActiveRecord/TActiveRecord.php index 70fd7b23..0400661d 100644 --- a/framework/Data/ActiveRecord/TActiveRecord.php +++ b/framework/Data/ActiveRecord/TActiveRecord.php @@ -189,7 +189,7 @@ abstract class TActiveRecord extends TComponent $registry = $this->getRecordManager()->getObjectStateRegistry(); $gateway = $this->getRecordManager()->getRecordGateway(); if(!$this->_readOnly) - $this->_readOnly = $gateway->getMetaData($this)->getIsView(); + $this->_readOnly = $gateway->getRecordTableInfo($this)->getIsView(); if($this->_readOnly) throw new TActiveRecordException('ar_readonly_exception',get_class($this)); return $registry->commit($this,$gateway); @@ -247,12 +247,8 @@ abstract class TActiveRecord extends TComponent */ public function deleteAll($criteria, $parameters=array()) { - if(is_string($criteria)) - { - if(!is_array($parameters) && func_num_args() > 1) - $parameters = array_slice(func_get_args(),1); - $criteria=new TActiveRecordCriteria($criteria,$parameters); - } + $args = func_num_args() > 1 ? array_slice(func_get_args(),1) : null; + $criteria = $this->getCriteria($criteria,$parameters, $args); $gateway = $this->getRecordManager()->getRecordGateway(); return $gateway->deleteRecordsByCriteria($this, $criteria); } @@ -278,13 +274,25 @@ abstract class TActiveRecord extends TComponent $obj->{$name} = $value; $gateway = $this->getRecordManager()->getRecordGateway(); - $obj->_readOnly = $gateway->getMetaData($this)->getIsView(); + $obj->_readOnly = $gateway->getRecordTableInfo($this)->getIsView(); //cache it return $registry->addCachedInstance($data,$obj); } /** + * @param TDbDataReader data reader + */ + protected function collectObjects($reader) + { + $result=array(); + $class = get_class($this); + foreach($reader as $data) + $result[] = $this->populateObject($class, $data); + return $result; + } + + /** * Find one single record that matches the criteria. * * Usage: @@ -303,12 +311,8 @@ abstract class TActiveRecord extends TComponent */ public function find($criteria,$parameters=array()) { - if(is_string($criteria)) - { - if(!is_array($parameters) && func_num_args() > 1) - $parameters = array_slice(func_get_args(),1);
- $criteria=new TActiveRecordCriteria($criteria,$parameters); - } + $args = func_num_args() > 1 ? array_slice(func_get_args(),1) : null; + $criteria = $this->getCriteria($criteria,$parameters, $args); $gateway = $this->getRecordManager()->getRecordGateway(); $data = $gateway->findRecordsByCriteria($this,$criteria); return $this->populateObject(get_class($this), $data); @@ -323,18 +327,12 @@ abstract class TActiveRecord extends TComponent */ public function findAll($criteria=null,$parameters=array()) { - if(is_string($criteria)) - { - if(!is_array($parameters) && func_num_args() > 1) - $parameters = array_slice(func_get_args(),1);
- $criteria=new TActiveRecordCriteria($criteria,$parameters); - } + $args = func_num_args() > 1 ? array_slice(func_get_args(),1) : null; + if($criteria!==null) + $criteria = $this->getCriteria($criteria,$parameters, $args); $gateway = $this->getRecordManager()->getRecordGateway(); - $results = array(); - $class = get_class($this); - foreach($gateway->findRecordsByCriteria($this,$criteria,true) as $data) - $results[] = $this->populateObject($class,$data); - return $results; + $result = $gateway->findRecordsByCriteria($this,$criteria,true); + return $this->collectObjects($result); } /** @@ -380,11 +378,8 @@ abstract class TActiveRecord extends TComponent if(func_num_args() > 1) $keys = func_get_args(); $gateway = $this->getRecordManager()->getRecordGateway(); - $results = array(); - $class = get_class($this); - foreach($gateway->findRecordsByPks($this,(array)$keys) as $data) - $results[] = $this->populateObject($class,$data); - return $results; + $result = $gateway->findRecordsByPks($this,(array)$keys); + return $this->collectObjects($result); } /** @@ -397,15 +392,11 @@ abstract class TActiveRecord extends TComponent */ public function findBySql($sql,$parameters=array()) { - if(!is_array($parameters) && func_num_args() > 1) - $parameters = array_slice(func_get_args(),1);
+ $args = func_num_args() > 1 ? array_slice(func_get_args(),1) : null; + $criteria = $this->getCriteria($sql,$parameters, $args); $gateway = $this->getRecordManager()->getRecordGateway(); - $data = $gateway->findRecordsBySql($this,$sql,$parameters); - $results = array(); - $class = get_class($this); - foreach($gateway->findRecordsBySql($this,$sql,$parameters) as $data) - $results[] = $this->populateObject($class,$data); - return $results; + $result = $gateway->findRecordsBySql($this,$criteria); + return $this->collectObjects($result); } /** @@ -416,12 +407,9 @@ abstract class TActiveRecord extends TComponent */ public function count($criteria=null,$parameters=array()) { - if(is_string($criteria)) - { - if(!is_array($parameters) && func_num_args() > 1)
- $parameters = array_slice(func_get_args(),1); - $criteria=new TActiveRecordCriteria($criteria,$parameters); - } + $args = func_num_args() > 1 ? array_slice(func_get_args(),1) : null; + if($criteria!==null) + $criteria = $this->getCriteria($criteria,$parameters, $args); $gateway = $this->getRecordManager()->getRecordGateway(); return $gateway->countRecords($this,$criteria); } @@ -467,7 +455,8 @@ abstract class TActiveRecord extends TComponent else return null;//throw new TActiveRecordException('ar_invalid_finder_method',$method); - $criteria = $this->createCriteriaFromString($method, $condition, $args); + $gateway = $this->getRecordManager()->getRecordGateway(); + $criteria = $gateway->getCommand($this)->createCriteriaFromString($method, $condition, $args); if($delete) return $this->deleteAll($criteria); else @@ -475,51 +464,25 @@ abstract class TActiveRecord extends TComponent } /** - * @param string __call method name - * @param string criteria conditions - * @param array method arguments - * @return TActiveRecordCriteria criteria created from the method name and its arguments. - */ - private function createCriteriaFromString($method, $condition, $args) - { - $fields = $this->extractMatchingConditions($method, $condition); - $args=count($args) === 1 && is_array($args[0]) ? $args[0] : $args; - if(count($fields)>count($args)) - { - throw new TActiveRecordException('ar_mismatch_args_exception', - $method,count($fields),count($args)); - } - return new TActiveRecordCriteria(implode(' ',$fields),$args); - } - - /** - * Calculates the AND/OR condition from dynamic method substrings using - * table meta data, allows for any AND-OR combinations. - * @param string dynamic method name - * @param string dynamic method search criteria - * @return array search condition substrings + * Create a new TSqlCriteria object from a string $criteria. The $args + * are additional parameters and are used in place of the $parameters + * if $parameters is not an array and $args is an arrary. + * @param string|TSqlCriteria sql criteria + * @param mixed parameters passed by the user. + * @param array additional parameters obtained from function_get_args(). + * @return TSqlCriteria criteria object. */ - private function extractMatchingConditions($method, $condition) + protected function getCriteria($criteria, $parameters, $args) { - $meta = $this->getRecordManager()->getRecordGateway()->getMetaData($this); - $search = implode('|', $meta->getColumnNames()); - $regexp = '/('.$search.')(and|_and_|or|_or_)?/i'; - $matches = array(); - if(!preg_match_all($regexp, strtolower($condition), $matches,PREG_SET_ORDER)) - { - throw new TActiveRecordException('ar_mismatch_column_names', - $method, implode(', ', $meta->getColumnNames()), $meta->getTableName()); - } - $fields = array(); - foreach($matches as $match) + if(is_string($criteria)) { - $column = $meta->getColumn($match[1]); - $sql = $column->getName() . ' = ? '; - if(count($match) > 2) - $sql .= strtoupper(str_replace('_', '', $match[2])); - $fields[] = $sql; + $useArgs = !is_array($parameters) && is_array($args); + return new TActiveRecordCriteria($criteria,$useArgs ? $args : $parameters); } - return $fields; + else if($criteria instanceof TSqlCriteria) + return $criteria; + else + throw new TActiveRecordException('ar_invalid_criteria'); } } diff --git a/framework/Data/ActiveRecord/TActiveRecordGateway.php b/framework/Data/ActiveRecord/TActiveRecordGateway.php index be9731c5..9c480ad0 100644 --- a/framework/Data/ActiveRecord/TActiveRecordGateway.php +++ b/framework/Data/ActiveRecord/TActiveRecordGateway.php @@ -22,8 +22,9 @@ class TActiveRecordGateway extends TComponent
{
private $_manager;
- private $_tables=array(); //meta data cache.
-
+ private $_tables=array(); //table cache
+ private $_meta=array(); //meta data cache.
+ private $_commandBuilders=array();
/**
* Constant name for specifying optional table name in TActiveRecord.
*/
@@ -52,7 +53,7 @@ class TActiveRecordGateway extends TComponent * @param TActiveRecord active record instance
* @return string table name for the given record class.
*/
- public function getTableName(TActiveRecord $record)
+ protected function getRecordTableName(TActiveRecord $record)
{
$class = new ReflectionClass($record);
if($class->hasConstant(self::TABLE_CONST))
@@ -68,70 +69,65 @@ class TActiveRecordGateway extends TComponent }
/**
- * @param TActiveRecord active record.
- * @return TDbMetaData table meta data, null if not found.
+ * Returns table information, trys the application cache first.
+ * @param TActiveRecord $record
+ * @return TDbTableInfo table information.
*/
- protected function getCachedMetaData($record)
+ public function getRecordTableInfo(TActiveRecord $record)
{
- $type=get_class($record);
- if(isset($this->_tables[$type]))
- return $this->_tables[$type];
- if(($cache=$this->getManager()->getCache())!==null)
- {
- //force loading of the table inspector to load the required classes
- // before unserializing cached meta data.
- $this->getManager()->getTableInspector($record->getDbConnection());
- $data = $cache->get($this->getMetaDataCacheKey($record));
- if($data !== false && $data !== null)
- {
- $this->_tables[$type] = $data;
- return $data;
- }
- }
+ $tableName = $this->getRecordTableName($record);
+ return $this->getTableInfo($record->getDbConnection(), $tableName);
}
/**
- * @param TActiveRecord active record.
- * @return string cache key, using connection string + record class name
+ * Returns table information for table in the database connection.
+ * @param TDbConnection database connection
+ * @param string table name
+ * @return TDbTableInfo table details.
*/
- protected function getMetaDataCacheKey($record)
+ public function getTableInfo($connection, $tableName)
{
- $conn = $record->getDbConnection()->getConnectionString();
- return $conn.':'.get_class($record);
- }
-
- /**
- * Cache the meta data, tries the application cache if applicable.
- * @param TActiveRecord active record.
- * @param TDbMetaData table meta data
- * @return TDbMetaData table meta data.
- */
- protected function cacheMetaData($record,$data)
- {
- $type = get_class($record);
- if(($cache=$this->getManager()->getCache())!==null)
- $cache->set($this->getMetaDataCacheKey($record), $data);
- $this->_tables[$type] = $data;
- return $data;
+ $connStr = $connection->getConnectionString();
+ $key = $connStr.$tableName;
+ if(!isset($this->_tables[$key]))
+ {
+ $tableInfo = null;
+ if(($cache=$this->getManager()->getCache())!==null)
+ $tableInfo = $cache->get($key);
+ if($tableInfo===null)
+ {
+ if(!isset($this->_meta[$connStr]))
+ {
+ Prado::using('System.Data.Common.TDbMetaData');
+ $this->_meta[$connStr] = TDbMetaData::getMetaData($connection);
+ }
+ $tableInfo = $this->_meta[$connStr]->getTableInfo($tableName);
+ }
+ $this->_tables[$key] = $tableInfo;
+ if($cache!==null)
+ $cache->set($key, $tableInfo);
+ }
+ return $this->_tables[$key];
}
/**
- * Gets the meta data for given database and table.
+ * @param TActiveRecord $record
+ * @return TDataGatewayCommand
*/
- public function getMetaData(TActiveRecord $record)
+ public function getCommand(TActiveRecord $record)
{
- $type=get_class($record);
- if(!($data = $this->getCachedMetaData($record)))
+ $conn = $record->getDbConnection();
+ $connStr = $conn->getConnectionString();
+ $tableInfo = $this->getRecordTableInfo($record);
+ if(!isset($this->_commandBuilders[$connStr]))
{
- $conn = $record->getDbConnection();
- $inspector = $this->getManager()->getTableInspector($conn);
- $table = $this->getTableName($record);
- $meta = $inspector->getTableMetaData($table);
- if(count($meta->getColumns()) == 0)
- throw new TActiveRecordException('ar_invalid_table', $table);
- $data = $this->cacheMetaData($record,$meta);
+ $builder = $tableInfo->createCommandBuilder($record->getDbConnection());
+ Prado::using('System.Data.DataGateway.TDataGatewayCommand');
+ $this->_commandBuilders[$connStr] = new TDataGatewayCommand($builder);
}
- return $data;
+ $this->_commandBuilders[$connStr]->getBuilder()->setTableInfo($tableInfo);
+
+ return $this->_commandBuilders[$connStr];
}
/**
@@ -143,11 +139,7 @@ class TActiveRecordGateway extends TComponent */
public function findRecordByPK(TActiveRecord $record,$keys)
{
- $meta = $this->getMetaData($record);
- $command = $meta->getFindByPkCommand($record->getDbConnection(),$keys);
- $this->raiseCommandEvent(TActiveRecordStatementType::Select,$command,$record,$keys);
- Prado::trace(get_class($record).'::FindRecordByPk('.var_export($keys,true).')', 'System.Data.ActiveRecord');
- return $meta->postQueryRow($command->queryRow());
+ return $this->getCommand($record)->findByPk($keys);
}
/**
@@ -158,11 +150,7 @@ class TActiveRecordGateway extends TComponent */
public function findRecordsByPks(TActiveRecord $record, $keys)
{
- $meta = $this->getMetaData($record);
- $command = $meta->getFindInPksCommand($record->getDbConnection(), $keys);
- $this->raiseCommandEvent(TActiveRecordStatementType::Select,$command,$record,$keys);
- Prado::trace(get_class($record).'::FindRecordsByPks('.var_export($keys,true).')', 'System.Data.ActiveRecord');
- return $meta->postQuery($command->query());
+ return $this->getCommand($record)->findAllByPk($keys);
}
@@ -176,27 +164,21 @@ class TActiveRecordGateway extends TComponent */
public function findRecordsByCriteria(TActiveRecord $record, $criteria, $iterator=false)
{
- $meta = $this->getMetaData($record);
- $command = $meta->getFindByCriteriaCommand($record->getDbConnection(),$criteria);
- $this->raiseCommandEvent(TActiveRecordStatementType::Select,$command,$record,$criteria);
- Prado::trace(get_class($record).'::FindRecordsByCriteria('.((string)$criteria).')', 'System.Data.ActiveRecord');
- return $iterator ? $meta->postQuery($command->query()) : $meta->postQueryRow($command->queryRow());
+ if($iterator)
+ return $this->getCommand($record)->findAll($criteria);
+ else
+ return $this->getCommand($record)->find($criteria);
}
/**
* Return record data from sql query.
* @param TActiveRecord active record finder instance.
- * @param string SQL string
- * @param array query parameters.
+ * @param TActiveRecordCriteria sql query
* @return TDbDataReader result iterator.
*/
- public function findRecordsBySql(TActiveRecord $record, $sql,$parameters=array())
+ public function findRecordsBySql(TActiveRecord $record, $criteria)
{
- $meta = $this->getMetaData($record);
- $command = $meta->getFindBySqlCommand($record->getDbConnection(),$sql,$parameters);
- $this->raiseCommandEvent(TActiveRecordStatementType::Select,$command,$record,$parameters);
- Prado::trace(get_class($record).'::FindRecordsBySql('.var_export($parameters,true).')', 'System.Data.ActiveRecord');
- return $meta->postQuery($command->query());
+ return $this->getCommand($record)->findBySql($criteria);
}
/**
@@ -207,11 +189,7 @@ class TActiveRecordGateway extends TComponent */
public function countRecords(TActiveRecord $record, $criteria)
{
- $meta = $this->getMetaData($record);
- $command = $meta->getCountRecordsCommand($record->getDbConnection(),$criteria);
- $this->raiseCommandEvent(TActiveRecordStatementType::Select,$command,$record,$criteria);
- Prado::trace(get_class($record).'::CountRecords('.((string)$criteria).')', 'System.Data.ActiveRecord');
- return intval($command->queryScalar());
+ return $this->getCommand($record)->count($criteria);
}
/**
@@ -221,14 +199,46 @@ class TActiveRecordGateway extends TComponent */
public function insert(TActiveRecord $record)
{
- $meta = $this->getMetaData($record);
- $command = $meta->getInsertCommand($record->getDbConnection(),$record);
- $this->raiseCommandEvent(TActiveRecordStatementType::Insert,$command,$record);
- Prado::trace(get_class($record).'::Insert()', 'System.Data.ActiveRecord');
- $rowsAffected = $command->execute();
- if($rowsAffected===1)
- $meta->updatePostInsert($record->getDbConnection(),$record);
- return $rowsAffected;
+ $result = $this->getCommand($record)->insert($this->getInsertValues($record));
+ if($result)
+ $this->updatePostInsert($record);
+ return $result;
+ }
+
+ protected function updatePostInsert($record)
+ {
+ $command = $this->getCommand($record);
+ $tableInfo = $command->getTableInfo();
+ foreach($tableInfo->getColumns() as $name => $column)
+ {
+ if($column->hasSequence())
+ $record->{$name} = $command->getLastInsertID($column->getSequenceName());
+ }
+ }
+
+ /**
+ * @param TActiveRecord record
+ * @return array insert values.
+ */
+ protected function getInsertValues(TActiveRecord $record)
+ {
+ $values=array();
+ $tableInfo = $this->getCommand($record)->getTableInfo();
+ foreach($tableInfo->getColumns() as $name=>$column)
+ {
+ if($column->getIsExcluded())
+ continue;
+ $value = $record->{$name};
+ if(!$column->getAllowNull() && $value===null && !$column->hasSequence())
+ {
+ throw new TActiveRecordException(
+ 'ar_value_must_not_be_null', get_class($record),
+ $tableInfo->getTableFullName(), $name);
+ }
+ if($value!==null)
+ $values[$name] = $value;
+ }
+ return $values;
}
/**
@@ -238,11 +248,32 @@ class TActiveRecordGateway extends TComponent */
public function update(TActiveRecord $record)
{
- $meta = $this->getMetaData($record);
- $command = $meta->getUpdateCommand($record->getDbConnection(),$record);
- $this->raiseCommandEvent(TActiveRecordStatementType::Update,$command,$record);
- Prado::trace(get_class($record).'::Update()', 'System.Data.ActiveRecord');
- return $command->execute();
+ list($data, $keys) = $this->getUpdateValues($record);
+ return $this->getCommand($record)->updateByPk($data, $keys);
+ }
+
+ protected function getUpdateValues(TActiveRecord $record)
+ {
+ $values=array();
+ $tableInfo = $this->getCommand($record)->getTableInfo();
+ $primary=array();
+ foreach($tableInfo->getColumns() as $name=>$column)
+ {
+ if($column->getIsExcluded())
+ continue;
+ $value = $record->{$name};
+ if(!$column->getAllowNull() && $value===null)
+ {
+ throw new TActiveRecordException(
+ 'ar_value_must_not_be_null', get_class($record),
+ $tableInfo->getTableFullName(), $name);
+ }
+ if($column->getIsPrimaryKey())
+ $primary[] = $value;
+ else
+ $values[$name] = $value;
+ }
+ return array($values,$primary);
}
/**
@@ -252,11 +283,19 @@ class TActiveRecordGateway extends TComponent */
public function delete(TActiveRecord $record)
{
- $meta = $this->getMetaData($record);
- $command = $meta->getDeleteCommand($record->getDbConnection(),$record);
- $this->raiseCommandEvent(TActiveRecordStatementType::Delete,$command,$record);
- Prado::trace(get_class($record).'::Delete()', 'System.Data.ActiveRecord');
- return $command->execute();
+ return $this->getCommand($record)->deleteByPk($this->getPrimaryKeyValues($record));
+ }
+
+ protected function getPrimaryKeyValues(TActiveRecord $record)
+ {
+ $tableInfo = $this->getCommand($record)->getTableInfo();
+ $primary=array();
+ foreach($tableInfo->getColumns() as $name=>$column)
+ {
+ if($column->getIsPrimaryKey())
+ $primary[$name] = $record->{$name};
+ }
+ return $primary;
}
/**
@@ -266,11 +305,7 @@ class TActiveRecordGateway extends TComponent */
public function deleteRecordsByPk(TActiveRecord $record, $keys)
{
- $meta = $this->getMetaData($record);
- $command = $meta->getDeleteByPkCommand($record->getDBConnection(),$keys);
- $this->raiseCommandEvent(TActiveRecordStatementType::Delete,$command,$record,$keys);
- Prado::trace(get_class($record).'::DeleteRecordsByPk('.var_export($keys,true).')', 'System.Data.ActiveRecord');
- return $command->execute();
+ return $this->getCommand($record)->deleteByPk($keys);
}
/**
@@ -281,11 +316,7 @@ class TActiveRecordGateway extends TComponent */
public function deleteRecordsByCriteria(TActiveRecord $record, $criteria)
{
- $meta = $this->getMetaData($record);
- $command = $meta->getDeleteByCriteriaCommand($record->getDBConnection(),$criteria);
- $this->raiseCommandEvent(TActiveRecordStatementType::Delete,$command,$record,$criteria);
- Prado::trace(get_class($record).'::DeleteRecordsByCriteria('.((string)$criteria).')', 'System.Data.ActiveRecord');
- return $command->execute();
+ return $this->getCommand($record)->delete($criteria);
}
/**
diff --git a/framework/Data/ActiveRecord/TActiveRecordManager.php b/framework/Data/ActiveRecord/TActiveRecordManager.php index 0179479e..5e463d2d 100644 --- a/framework/Data/ActiveRecord/TActiveRecordManager.php +++ b/framework/Data/ActiveRecord/TActiveRecordManager.php @@ -121,17 +121,6 @@ class TActiveRecordManager extends TComponent }
/**
- * @param string|TActiveRecord active record class name or instance
- * @return TDbMetaData record specific meta data
- */
- public function getMetaData($record)
- {
- if(is_string($record))
- $record = TActiveRecord::finder($record);
- return $this->getRecordGateway()->getMetaData($record);
- }
-
- /**
* @return TActiveRecordGateway default record gateway.
*/
protected function createRecordGateway()
@@ -140,51 +129,6 @@ class TActiveRecordManager extends TComponent }
/**
- * Get table meta data for particular database and table.
- * @param TDbConnection database connection.
- * @return TDbMetaDataInspector table meta inspector
- */
- public function getTableInspector(TDbConnection $conn)
- {
- $database = $conn->getConnectionString();
- if(!isset($this->_meta[$database]))
- $this->_meta[$database] = $this->createMetaDataInspector($conn);
- return $this->_meta[$database];
- }
-
- /**
- * Create an instance of a database meta inspector corresponding to the
- * given database vendor specified by the $driver parameter.
- * @param TDbConnection database connection
- * @return TDbMetaDataInspector table meta inspector
- */
- protected function createMetaDataInspector($conn)
- {
- $conn->setActive(true); //must be connected before retrieving driver name!
- $driver = $conn->getDriverName();
- switch(strtolower($driver))
- {
- case 'pgsql':
- Prado::using('System.Data.ActiveRecord.Vendor.TPgsqlMetaDataInspector');
- return new TPgsqlMetaDataInspector($conn);
- case 'mysqli':
- case 'mysql':
- Prado::using('System.Data.ActiveRecord.Vendor.TMysqlMetaDataInspector');
- return new TMysqlMetaDataInspector($conn);
- case 'sqlite': //sqlite 3
- case 'sqlite2': //sqlite 2
- Prado::using('System.Data.ActiveRecord.Vendor.TSqliteMetaDataInspector');
- return new TSqliteMetaDataInspector($conn);
- case 'ibm':
- Prado::using('System.Data.ActiveRecord.Vendor.TIbmMetaDataInspector');
- return new TIbmMetaDataInspector($conn);
- default:
- throw new TActiveRecordConfigurationException(
- 'ar_invalid_database_driver',$driver);
- }
- }
-
- /**
* This method is invoked before the object is inserted into the database.
* The method raises 'OnInsert' event.
* If you override this method, be sure to call the parent implementation
diff --git a/framework/Data/ActiveRecord/TActiveRecordStateRegistry.php b/framework/Data/ActiveRecord/TActiveRecordStateRegistry.php index 59222e44..ef415b5e 100644 --- a/framework/Data/ActiveRecord/TActiveRecordStateRegistry.php +++ b/framework/Data/ActiveRecord/TActiveRecordStateRegistry.php @@ -240,12 +240,12 @@ class TActiveRecordStateRegistry */
public function commit($record,$gateway)
{
- $rowsAffected=0;
+ $rowsAffected=false;
if($this->getIsRemovedObject($record))
{
$rowsAffected = $gateway->delete($record);
- if($rowsAffected===1)
+ if($rowsAffected)
$this->removeRemovedObject($record);
}
else
@@ -255,10 +255,10 @@ class TActiveRecordStateRegistry else if($this->getIsNewObject($record))
$rowsAffected = $gateway->insert($record);
- if($rowsAffected===1)
+ if($rowsAffected)
$this->registerClean($record);
}
- return $rowsAffected===1;
+ return (boolean)$rowsAffected;
}
}
diff --git a/framework/Data/Common/IbmDb2/TIbmColumnMetaData.php b/framework/Data/Common/IbmDb2/TIbmColumnMetaData.php new file mode 100644 index 00000000..284a5fd2 --- /dev/null +++ b/framework/Data/Common/IbmDb2/TIbmColumnMetaData.php @@ -0,0 +1,157 @@ +<?php
+/**
+ * TIbmColumnMetaData class file.
+ *
+ * @author Cesar Ramos <cramos[at]gmail[dot]com>
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright © 2005-2007 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Id: TIbmColumnMetaData.php 1807 2007-03-31 06:42:15Z wei $
+ * @package System.Data.ActiveRecord.Vendor
+ */
+
+/**
+ * TIbmColumnMetaData class.
+ *
+ * Column details for IBM DB2 database. Using php_pdo_ibm.dll extension.
+ *
+ * @author Cesar Ramos <cramos[at]gmail[dot]com>
+ * @version $Id: TIbmColumnMetaData.php 1807 2007-03-31 06:42:15Z wei $
+ * @package System.Data.ActiveRecord.Vendor
+ * @since 3.1
+ */
+class TIbmColumnMetaData extends TComponent
+{
+ private $_name;
+ private $_type;
+ private $_length;
+ private $_autoIncrement;
+ private $_default;
+ private $_notNull=true;
+
+ private $_isPrimary=null;
+
+ private $_property;
+
+ /**
+ * Initialize column meta data.
+ *
+ * @param string column name.
+ * @param string column data type.
+ * @param string column data length.
+ * @param boolean column can not be null.
+ * @param string serial name.
+ * @param string default value.
+ */
+ public function __construct($name,$type,$length,$notNull,$autoIncrement,$default,$primary)
+ {
+ $this->_property=$name;
+ $this->_name=$name;
+ $this->_type=$type;
+ $this->_length=$length;
+ $this->_notNull=$notNull;
+ $this->_autoIncrement=$autoIncrement;
+ $this->_default=$default;
+ $this->_isPrimary=$primary;
+ }
+
+ /**
+ * @return string quoted column name.
+ */
+ public function getName()
+ {
+ return $this->_name;
+ }
+
+ /**
+ * @return integer length.
+ */
+ public function getLength()
+ {
+ return $this->_length;
+ }
+
+ /**
+ * @return string active record property name
+ */
+ public function getProperty()
+ {
+ return $this->_property;
+ }
+
+ /**
+ * @return boolean true if column is a sequence, false otherwise.
+ */
+ public function hasSequence()
+ {
+ return $this->_autoIncrement;
+ }
+
+ /**
+ * @return null no sequence name.
+ */
+ public function getSequenceName()
+ {
+ return null;
+ }
+
+ /**
+ * @return boolean true if the column is a primary key, or part of a composite primary key.
+ */
+ public function getIsPrimaryKey()
+ {
+ return $this->_isPrimary;
+ }
+
+ /**
+ * @return string column type
+ */
+ public function getType()
+ {
+ return $this->_type;
+ }
+
+
+ /**
+ * @return boolean false if column can be null, true otherwise.
+ */
+ public function getNotNull()
+ {
+ return $this->_notNull;
+ }
+
+ /**
+ * @return boolean true if column has default value, false otherwise.
+ */
+ public function hasDefault()
+ {
+ return $this->_default !== null;
+ }
+
+ /**
+ * @return string default column value.
+ */
+ public function getDefaultValue()
+ {
+ return $this->_default;
+ }
+
+ /**
+ * @return string PHP primative type derived from the column type.
+ */
+ public function getPHPType()
+ {
+ switch(strtolower($this->_type))
+ {
+ case 'smallint': case 'integer':
+ return 'integer';
+ case 'real': case 'float': case 'double': case 'decimal': case 'bigint':
+ return 'float';
+ default:
+ return 'string';
+ }
+ }
+
+}
+
+?>
\ No newline at end of file diff --git a/framework/Data/Common/IbmDb2/TIbmMetaData.php b/framework/Data/Common/IbmDb2/TIbmMetaData.php new file mode 100644 index 00000000..99df5465 --- /dev/null +++ b/framework/Data/Common/IbmDb2/TIbmMetaData.php @@ -0,0 +1,113 @@ +<?php
+
+/**
+ * TIbmMetaData class file.
+ *
+ * @author Cesar Ramos <cramos[at]gmail[dot]com>
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright © 2005-2007 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Id: TIbmMetaData.php 1807 2007-03-31 06:42:15Z wei $
+ * @package System.Data.ActiveRecord.Vendor
+ */
+Prado::using('System.Data.ActiveRecord.Vendor.TDbMetaDataCommon');
+
+/**
+ * TIbmMetaData class.
+ *
+ * Column details for IBM DB2 database. Using php_pdo_ibm.dll extension.
+ *
+ * Does not support LIMIT and OFFSET criterias.
+ *
+ * @author Cesar Ramos <cramos[at]gmail[dot]com>
+ * @version $Id: TIbmMetaData.php 1807 2007-03-31 06:42:15Z wei $
+ * @package System.Data.ActiveRecord.Vendor
+ * @since 3.1
+ */
+class TIbmMetaData extends TDbMetaDataCommon
+{
+ /**
+ * Build the SQL search string from the criteria object for IBM DB2 database.
+ * @param TDbConnection database connection.
+ * @param TActiveRecordCriteria search criteria.
+ * @return string SQL search.
+ */
+ protected function getSqlFromCriteria($conn, $criteria)
+ {
+ if($criteria===null) return '';
+ $sql = '';
+ if(($condition = $criteria->getCondition())!==null)
+ $sql .= ' WHERE '.$condition;
+ $orders=array();
+ foreach($criteria->getOrdersBy() as $by=>$ordering)
+ $orders[] = $this->getOrdering($by, $ordering);
+ if(count($orders) > 0)
+ $sql .= ' ORDER BY '.implode(', ', $orders);
+ //if(($limit = $criteria->getLimit())!==null)
+ //{
+ // $sql .= ' FETCH FIRST '.intval($limit).' ROWS ONLY';
+ //}
+ return strlen($sql) > 0 ? $sql : '';
+ }
+
+ /**
+ * Lowercase the data keys, IBM DB2 returns uppercase column names
+ * @param mixed record row
+ * @return array record row
+ */
+ public function postQueryRow($row)
+ {
+ if(!is_array($row)) return $row;
+ $result=array();
+ foreach($row as $k=>$v)
+ $result[strtolower($k)]=$v;
+ return $result;
+ }
+
+ /**
+ * Lowercase the data keys, IBM DB2 returns uppercase column names
+ * @param mixed record row
+ * @return array record row
+ */
+ public function postQuery($rows)
+ {
+ $data = array();
+ foreach($rows as $k=>$v)
+ $data[$k] = $this->postQueryRow($v);
+ return $data;
+ }
+
+ public function getSearchRegExpCriteria($fields, $keywords)
+ {
+ if(strlen(trim($keywords)) == 0) return '';
+ $words = array();
+ preg_match_all('/([a-zA-Z0-9-+]+)/', $keywords, $words);
+ $result = array();
+ foreach($fields as $field)
+ {
+ $column = $this->getColumn($field);
+ if($this->isSearchableColumn($column))
+ $result[] = $this->getLikeCriteriaStr($column->getName(), $words[0]);
+ }
+ $a = '('.implode(' OR ', $result).')';
+ error_log($a);
+ return '('.implode(' OR ', $result).')';
+ }
+
+ protected function isSearchableColumn($column)
+ {
+ $type = strtolower($column->getType());
+ return $type === 'char' || $type === 'varchar';
+ }
+
+ protected function getLikeCriteriaStr($column, $words)
+ {
+ $result=array();
+ foreach($words as $word)
+ $result[] = "{$column} LIKE '%{$word}%'";
+ return '('.implode(' AND ', $result).')';
+ }
+
+
+}
+?>
\ No newline at end of file diff --git a/framework/Data/Common/IbmDb2/TIbmMetaDataInspector.php b/framework/Data/Common/IbmDb2/TIbmMetaDataInspector.php new file mode 100644 index 00000000..1cd8af95 --- /dev/null +++ b/framework/Data/Common/IbmDb2/TIbmMetaDataInspector.php @@ -0,0 +1,113 @@ +<?php
+/**
+ * TIbmMetaDataInspector class file.
+ *
+ * @author Cesar Ramos <cramos[at]gmail[dot]com>
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright © 2005-2007 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Id: TIbmMetaDataInspector.php 1807 2007-03-31 06:42:15Z wei $
+ * @package System.Data.ActiveRecord.Vendor
+ */
+Prado::using('System.Data.ActiveRecord.Vendor.TDbMetaDataInspector');
+Prado::using('System.Data.ActiveRecord.Vendor.TIbmColumnMetaData');
+Prado::using('System.Data.ActiveRecord.Vendor.TIbmMetaData');
+
+/**
+ * TIbmMetaDataInspector class.
+ *
+ * Column details for IBM DB2 database. Using php_pdo_ibm.dll extension.
+ *
+ * @author Cesar Ramos <cramos[at]gmail[dot]com>
+ * @version $Id: TIbmMetaDataInspector.php 1807 2007-03-31 06:42:15Z wei $
+ * @package System.Data.ActiveRecord.Vendor
+ * @since 3.1
+ */
+class TIbmMetaDataInspector extends TDbMetaDataInspector
+{
+ private $_schema;
+
+ /**
+ * @param string default schema.
+ */
+ public function setSchema($schema)
+ {
+ $this->_schema=$schema;
+ }
+
+ /**
+ * @return string default schema.
+ */
+ public function getSchema()
+ {
+ return $this->_schema;
+ }
+ /**
+ * Get the column definitions for given table.
+ * @param string table name.
+ * @return array column name value pairs of column meta data.
+ */
+ protected function getColumnDefinitions($table)
+ {
+ if(count($parts= explode('.', $table)) > 1)
+ {
+ $tablename = $parts[1];
+ $schema = $parts[0];
+ }
+ else
+ {
+ $tablename = $parts[0];
+ $schema = $this->getSchema();
+ }
+ $sql="SELECT * FROM SYSCAT.COLUMNS WHERE TABNAME='".strtoupper($tablename)."'";
+ if ($schema)
+ $sql=$sql." AND TABSCHEMA='".strtoupper($schema)."'";
+
+ $conn = $this->getDbConnection();
+ $conn->setActive(true);
+ $command = $conn->createCommand($sql);
+ $command->prepare();
+ $result=$command->query($sql);
+ foreach ($result as $col)
+ $cols[strtolower($col['COLNAME'])] = $this->getColumnMetaData($col);
+ return $cols;
+ }
+
+ protected function getColumnMetaData($col)
+ {
+ $name = strtolower($col['COLNAME']);
+ $type = $col['TYPENAME'];
+ $length = $col['LENGTH'];
+ $notNull = $col['NULLS']==='N'?1:0;
+ $autoIncrement=$col['IDENTITY']==='Y'?1:0;
+ $default = $col['DEFAULT'];
+ $primaryKey = $col['KEYSEQ']?1:0;
+ return new TIbmColumnMetaData($name,$type,$length,$notNull,$autoIncrement,$default,$primaryKey);
+ }
+
+ /**
+ * Not implemented, IBM does not always have foreign key constraints.
+ */
+ protected function getConstraintKeys($table)
+ {
+ return array('primary'=>array(), 'foreign'=>array());
+ }
+
+ /**
+ * Create a new instance of meta data.
+ * @param string table name
+ * @param array column meta data
+ * @param array primary key meta data
+ * @param array foreign key meta data.
+ * @return TDbMetaData table meta data.
+ */
+ protected function createMetaData($table, $columns, $primary, $foreign)
+ {
+ $pks = array();
+ foreach($columns as $name=>$column)
+ if($column->getIsPrimaryKey())
+ $pks[] = $name;
+ return new TIbmMetaData($table,$columns,$pks);
+ }
+}
+?>
\ No newline at end of file diff --git a/framework/Data/Common/Mssql/TMssqlCommandBuilder.php b/framework/Data/Common/Mssql/TMssqlCommandBuilder.php new file mode 100644 index 00000000..b22c08a5 --- /dev/null +++ b/framework/Data/Common/Mssql/TMssqlCommandBuilder.php @@ -0,0 +1,173 @@ +<?php
+/**
+ * TDbCommandBuilder class file.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright © 2005-2007 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Id: TDbCommandBuilder.php 1863 2007-04-12 12:43:49Z wei $
+ * @package System.Data.Common
+ */
+
+Prado::using('System.Data.Common.TDbCommandBuilder');
+
+/**
+ * TDbCommandBuilder provides basic methods to create query commands for tables
+ * giving by {@link setTableInfo TableInfo} the property.
+ *
+ * @author Wei Zhuo <weizho[at]gmail[dot]com>
+ * @version $Id: TDbCommandBuilder.php 1863 2007-04-12 12:43:49Z wei $
+ * @package System.Data.Common
+ * @since 3.1
+ */
+class TMssqlCommandBuilder extends TDbCommandBuilder
+{
+ /**
+ * Overrides parent implementation. Uses "SELECT @@Identity".
+ * @return integer last insert id, null if none is found.
+ */
+ public function getLastInsertID()
+ {
+ foreach($this->getTableInfo()->getColumns() as $column)
+ {
+ if($column->hasSequence())
+ {
+ $command = $this->getDbConnection()->createCommand('SELECT @@Identity');
+ return intval($command->queryScalar());
+ }
+ }
+ }
+
+ /**
+ * Overrides parent implementation. Alters the sql to apply $limit and $offset.
+ * The idea for limit with offset is done by modifying the sql on the fly
+ * with numerous assumptions on the structure of the sql string.
+ * The modification is done with reference to the notes from
+ * http://troels.arvin.dk/db/rdbms/#select-limit-offset
+ *
+ * <code>
+ * SELECT * FROM (
+ * SELECT TOP n * FROM (
+ * SELECT TOP z columns -- (z=n+skip)
+ * FROM tablename
+ * ORDER BY key ASC
+ * ) AS FOO ORDER BY key DESC -- ('FOO' may be anything)
+ * ) AS BAR ORDER BY key ASC -- ('BAR' may be anything)
+ * </code>
+ *
+ * <b>Regular expressions are used to alter the SQL query. The resulting SQL query
+ * may be malformed for complex queries.</b> The following restrictions apply
+ *
+ * <ul>
+ * <li>
+ * In particular, <b>commas</b> should <b>NOT</b>
+ * be used as part of the ordering expression or identifier. Commas must only be
+ * used for separating the ordering clauses.
+ * </li>
+ * <li>
+ * In the ORDER BY clause, the column name should NOT be be qualified
+ * with a table name or view name. Alias the column names or use column index.
+ * </li>
+ * <li>
+ * No clauses should follow the ORDER BY clause, e.g. no COMPUTE or FOR clauses.
+ * </li>
+ *
+ * @param string SQL query string.
+ * @param integer maximum number of rows, -1 to ignore limit.
+ * @param integer row offset, -1 to ignore offset.
+ * @return string SQL with limit and offset.
+ */
+ public function applyLimitOffset($sql, $limit=-1, $offset=-1)
+ {
+ $limit = $limit!==null ? intval($limit) : -1;
+ $offset = $offset!==null ? intval($offset) : -1;
+ if ($limit > 0 && $offset <= 0) //just limit
+ $sql = preg_replace('/^([\s(])*SELECT( DISTINCT)?(?!\s*TOP\s*\()/i',"\\1SELECT\\2 TOP $limit", $sql);
+ else if($limit > 0 && $offset > 0)
+ $sql = $this->rewriteLimitOffsetSql($sql, $limit,$offset);
+ return $sql;
+ }
+
+ /**
+ * Rewrite sql to apply $limit > and $offset > 0 for MSSQL database.
+ * See http://troels.arvin.dk/db/rdbms/#select-limit-offset
+ * @param string sql query
+ * @param integer $limit > 0
+ * @param integer $offset > 0
+ * @return sql modified sql query applied with limit and offset.
+ */
+ protected function rewriteLimitOffsetSql($sql, $limit, $offset)
+ {
+ $fetch = $limit+$offset;
+ $sql = preg_replace('/^([\s(])*SELECT( DISTINCT)?(?!\s*TOP\s*\()/i',"\\1SELECT\\2 TOP $fetch", $sql);
+ $ordering = $this->findOrdering($sql);
+
+ $orginalOrdering = $this->joinOrdering($ordering);
+ $reverseOrdering = $this->joinOrdering($this->reverseDirection($ordering));
+ $sql = "SELECT * FROM (SELECT TOP {$limit} * FROM ($sql) as [__inner top table__] {$reverseOrdering}) as [__outer top table__] {$orginalOrdering}";
+ return $sql;
+ }
+
+ /**
+ * Base on simplified syntax http://msdn2.microsoft.com/en-us/library/aa259187(SQL.80).aspx
+ *
+ * @param string $sql
+ * @return array ordering expression as key and ordering direction as value
+ */
+ protected function findOrdering($sql)
+ {
+ if(!preg_match('/ORDER BY/i', $sql))
+ return array();
+ $matches=array();
+ $ordering=array();
+ preg_match_all('/(ORDER BY)[\s"\[](.*)(ASC|DESC)?(?:[\s"\[]|$|COMPUTE|FOR)/i', $sql, $matches);
+ if(count($matches)>1 && count($matches[2]) > 0)
+ {
+ $parts = explode(',', $matches[2][0]);
+ foreach($parts as $part)
+ {
+ $subs=array();
+ if(preg_match_all('/(.*)[\s"\]](ASC|DESC)$/i', trim($part), $subs))
+ {
+ if(count($subs) > 1 && count($subs[2]) > 0)
+ {
+ $ordering[$subs[1][0]] = $subs[2][0];
+ }
+ //else what?
+ }
+ else
+ $ordering[trim($part)] = 'ASC';
+ }
+ }
+ return $ordering;
+ }
+
+ /**
+ * @param array ordering obtained from findOrdering()
+ * @return string concat the orderings
+ */
+ protected function joinOrdering($orders)
+ {
+ if(count($orders)>0)
+ {
+ $str=array();
+ foreach($orders as $column => $direction)
+ $str[] = $column.' '.$direction;
+ return 'ORDER BY '.implode(', ', $str);
+ }
+ }
+
+ /**
+ * @param array original ordering
+ * @return array ordering with reversed direction.
+ */
+ protected function reverseDirection($orders)
+ {
+ foreach($orders as $column => $direction)
+ $orders[$column] = strtolower(trim($direction))==='desc' ? 'ASC' : 'DESC';
+ return $orders;
+ }
+}
+
+?>
\ No newline at end of file diff --git a/framework/Data/Common/Mssql/TMssqlMetaData.php b/framework/Data/Common/Mssql/TMssqlMetaData.php new file mode 100644 index 00000000..c70bc349 --- /dev/null +++ b/framework/Data/Common/Mssql/TMssqlMetaData.php @@ -0,0 +1,202 @@ +<?php
+
+/**
+ * Load the base TDbMetaData class.
+ */
+Prado::using('System.Data.Common.TDbMetaData');
+Prado::using('System.Data.Common.Mssql.TMssqlTableInfo');
+
+class TMssqlMetaData extends TDbMetaData
+{
+ /**
+ * Get the column definitions for given table.
+ * @param string table name.
+ * @return TMssqlTableInfo table information.
+ */
+ protected function createTableInfo($table)
+ {
+ list($catalogName,$schemaName,$tableName) = $this->getCatalogSchemaTableName($table);
+ $this->getDbConnection()->setActive(true);
+ $sql = <<<EOD
+ SELECT t.*,
+ c.*,
+ columnproperty(object_id(c.table_schema + '.' + c.table_name), c.column_name,'IsIdentity') as IsIdentity
+ FROM INFORMATION_SCHEMA.TABLES t,
+ INFORMATION_SCHEMA.COLUMNS c
+ WHERE t.table_name = c.table_name
+ AND t.table_name = :table
+EOD;
+ if($schemaName!==null)
+ $sql .= ' AND t.schema_name = :schema';
+ if($catalogName!==null)
+ $sql .= ' AND t.catalog_name = :catalog';
+
+ $command = $this->getDbConnection()->createCommand($sql);
+ $command->bindValue(':table', $tableName);
+ if($schemaName!==null)
+ $command->bindValue(':schema', $schemaName);
+ if($catalogName!==null)
+ $command->bindValue(':catalog', $catalogName);
+
+ $tableInfo=null;
+ foreach($command->query() as $col)
+ {
+ if($tableInfo===null)
+ $tableInfo = $this->createNewTableInfo($col);
+ $this->processColumn($tableInfo,$col);
+ }
+ return $tableInfo;
+ }
+
+ protected function getCatalogSchemaTableName($table)
+ {
+ //remove possible delimiters
+ $result = explode('.', preg_replace('/\[|\]|"/', '', $table));
+ if(count($result)===1)
+ return array(null,null,$result[0]);
+ if(count($result)===2)
+ return array(null,$result[0],$result[1]);
+ if(count($result)>2)
+ return array($result[0],$result[1],$result[2]);
+ }
+
+ /**
+ * @param TMssqlTableInfo table information.
+ * @param array column information.
+ */
+ protected function processColumn($tableInfo, $col)
+ {
+ $columnId = $col['COLUMN_NAME'];
+
+ $info['ColumnName'] = "[$columnId]"; //quote the column names!
+ $info['ColumnId'] = $columnId;
+ $info['ColumnIndex'] = intval($col['ORDINAL_POSITION'])-1; //zero-based index
+ if($col['IS_NULLABLE']!=='NO')
+ $info['AllowNull'] = true;
+ if($col['COLUMN_DEFAULT']!==null)
+ $info['DefaultValue'] = $col['COLUMN_DEFAULT'];
+
+ if(in_array($columnId, $tableInfo->getPrimaryKeys()))
+ $info['IsPrimaryKey'] = true;
+ if($this->isForeignKeyColumn($columnId, $tableInfo))
+ $info['IsForeignKey'] = true;
+
+ if($col['IsIdentity']==='1')
+ $info['AutoIncrement'] = true;
+ $info['DbType'] = $col['DATA_TYPE'];
+ if($col['CHARACTER_MAXIMUM_LENGTH']!==null)
+ $info['ColumnSize'] = intval($col['CHARACTER_MAXIMUM_LENGTH']);
+ if($col['NUMERIC_PRECISION'] !== null)
+ $info['NumericPrecision'] = intval($col['NUMERIC_PRECISION']);
+ if($col['NUMERIC_SCALE']!==null)
+ $info['NumericScale'] = intval($col['NUMERIC_SCALE']);
+ $tableInfo->Columns[$columnId] = new TMssqlTableColumn($info);
+ }
+
+ /**
+ * @param string table schema name
+ * @param string table name.
+ * @return TMssqlTableInfo
+ */
+ protected function createNewTableInfo($col)
+ {
+ $info['CatalogName'] = $col['TABLE_CATALOG'];
+ $info['SchemaName'] = $col['TABLE_SCHEMA'];
+ $info['TableName'] = $col['TABLE_NAME'];
+ if($col['TABLE_TYPE']==='VIEW')
+ $info['IsView'] = true;
+ list($primary, $foreign) = $this->getConstraintKeys($col);
+ return new TMssqlTableInfo($info,$primary,$foreign);
+ }
+
+ /**
+ * Gets the primary and foreign key column details for the given table.
+ * @param string schema name
+ * @param string table name.
+ * @return array tuple ($primary, $foreign)
+ */
+ protected function getConstraintKeys($col)
+ {
+ $sql = <<<EOD
+ SELECT k.column_name field_name
+ FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE k
+ LEFT JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS c
+ ON k.table_name = c.table_name
+ AND k.constraint_name = c.constraint_name
+ WHERE k.constraint_catalog = DB_NAME()
+ AND
+ c.constraint_type ='PRIMARY KEY'
+ AND k.table_name = :table
+EOD;
+ $command = $this->getDbConnection()->createCommand($sql);
+ $command->bindValue(':table', $col['TABLE_NAME']);
+ $primary = array();
+ foreach($command->query()->readAll() as $field)
+ $primary[] = $field['field_name'];
+ $foreign = $this->getForeignConstraints($col);
+ return array($primary,$foreign);
+ }
+
+ /**
+ * Gets foreign relationship constraint keys and table name
+ * @param string database name
+ * @param string table name
+ * @return array foreign relationship table name and keys.
+ */
+ protected function getForeignConstraints($col)
+ {
+ //From http://msdn2.microsoft.com/en-us/library/aa175805(SQL.80).aspx
+ $sql = <<<EOD
+ SELECT
+ KCU1.CONSTRAINT_NAME AS 'FK_CONSTRAINT_NAME'
+ , KCU1.TABLE_NAME AS 'FK_TABLE_NAME'
+ , KCU1.COLUMN_NAME AS 'FK_COLUMN_NAME'
+ , KCU1.ORDINAL_POSITION AS 'FK_ORDINAL_POSITION'
+ , KCU2.CONSTRAINT_NAME AS 'UQ_CONSTRAINT_NAME'
+ , KCU2.TABLE_NAME AS 'UQ_TABLE_NAME'
+ , KCU2.COLUMN_NAME AS 'UQ_COLUMN_NAME'
+ , KCU2.ORDINAL_POSITION AS 'UQ_ORDINAL_POSITION'
+ FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS RC
+ JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE KCU1
+ ON KCU1.CONSTRAINT_CATALOG = RC.CONSTRAINT_CATALOG
+ AND KCU1.CONSTRAINT_SCHEMA = RC.CONSTRAINT_SCHEMA
+ AND KCU1.CONSTRAINT_NAME = RC.CONSTRAINT_NAME
+ JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE KCU2
+ ON KCU2.CONSTRAINT_CATALOG =
+ RC.UNIQUE_CONSTRAINT_CATALOG
+ AND KCU2.CONSTRAINT_SCHEMA =
+ RC.UNIQUE_CONSTRAINT_SCHEMA
+ AND KCU2.CONSTRAINT_NAME =
+ RC.UNIQUE_CONSTRAINT_NAME
+ AND KCU2.ORDINAL_POSITION = KCU1.ORDINAL_POSITION
+ WHERE KCU1.TABLE_NAME = :table
+EOD;
+ $command = $this->getDbConnection()->createCommand($sql);
+ $command->bindValue(':table', $col['TABLE_NAME']);
+ $fkeys=array();
+ $catalogSchema = "[{$col['TABLE_CATALOG']}].[{$col['TABLE_SCHEMA']}]";
+ foreach($command->query() as $info)
+ {
+ $fkeys[$info['FK_CONSTRAINT_NAME']]['keys'][$info['FK_COLUMN_NAME']] = $info['UQ_COLUMN_NAME'];
+ $fkeys[$info['FK_CONSTRAINT_NAME']]['table'] = "{$catalogSchema}.[{$info['UQ_TABLE_NAME']}]";
+ }
+ return count($fkeys) > 0 ? array_values($fkeys) : $fkeys;
+ }
+
+ /**
+ * @param string column name.
+ * @param TPgsqlTableInfo table information.
+ * @return boolean true if column is a foreign key.
+ */
+ protected function isForeignKeyColumn($columnId, $tableInfo)
+ {
+ foreach($tableInfo->getForeignKeys() as $fk)
+ {
+ if(in_array($columnId, array_keys($fk['keys'])))
+ return true;
+ }
+ return false;
+ }
+}
+
+?>
\ No newline at end of file diff --git a/framework/Data/Common/Mssql/TMssqlTableColumn.php b/framework/Data/Common/Mssql/TMssqlTableColumn.php new file mode 100644 index 00000000..77c115e5 --- /dev/null +++ b/framework/Data/Common/Mssql/TMssqlTableColumn.php @@ -0,0 +1,56 @@ +<?php
+/**
+ * TMssqlTableColumn class file.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright © 2005-2007 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Id: TMssqlTableColumn.php 1863 2007-04-12 12:43:49Z wei $
+ * @package System.Data.Common.Mssql
+ */
+
+/**
+ * Load common TDbTableCommon class.
+ */
+Prado::using('System.Data.Common.TDbTableColumn');
+
+/**
+ * Describes the column metadata of the schema for a Mssql database table.
+ *
+ * @author Wei Zhuo <weizho[at]gmail[dot]com>
+ * @version $Id: TMssqlTableColumn.php 1863 2007-04-12 12:43:49Z wei $
+ * @package System.Data.Common.Mssql
+ * @since 3.1
+ */
+class TMssqlTableColumn extends TDbTableColumn
+{
+ private static $types = array();
+
+ /**
+ * Overrides parent implementation, returns PHP type from the db type.
+ * @return boolean derived PHP primitive type from the column db type.
+ */
+ public function getPHPType()
+ {
+
+ return 'string';
+ }
+
+ public function getAutoIncrement()
+ {
+ return $this->getInfo('AutoIncrement',false);
+ }
+
+ public function hasSequence()
+ {
+ return $this->getAutoIncrement();
+ }
+
+ public function getIsExcluded()
+ {
+ return strtolower($this->getDbType())==='timestamp';
+ }
+}
+
+?>
\ No newline at end of file diff --git a/framework/Data/Common/Mssql/TMssqlTableInfo.php b/framework/Data/Common/Mssql/TMssqlTableInfo.php new file mode 100644 index 00000000..b407ad44 --- /dev/null +++ b/framework/Data/Common/Mssql/TMssqlTableInfo.php @@ -0,0 +1,62 @@ +<?php
+/**
+ * TMssqlTableInfo class file.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright © 2005-2007 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Id: TMssqlTableInfo.php 1861 2007-04-12 08:05:03Z wei $
+ * @package System.Data.Common.Mssql
+ */
+
+/**
+ * Loads the base TDbTableInfo class and TMssqlTableColumn class.
+ */
+Prado::using('System.Data.Common.TDbTableInfo');
+Prado::using('System.Data.Common.Mssql.TMssqlTableColumn');
+
+/**
+ * TMssqlTableInfo class provides additional table information for Mssql database.
+ *
+ * @author Wei Zhuo <weizho[at]gmail[dot]com>
+ * @version $Id: TMssqlTableInfo.php 1861 2007-04-12 08:05:03Z wei $
+ * @package System.Data.Common.Mssql
+ * @since 3.1
+ */
+class TMssqlTableInfo extends TDbTableInfo
+{
+ /**
+ * @return string name of the schema this column belongs to.
+ */
+ public function getSchemaName()
+ {
+ return $this->getInfo('SchemaName');
+ }
+
+ public function getCatalogName()
+ {
+ return $this->getInfo('CatalogName');
+ }
+
+ /**
+ * @return string full name of the table, database dependent.
+ */
+ public function getTableFullName()
+ {
+ return '['.$this->getCatalogName().'].['.$this->getSchemaName().'].['.$this->getTableName().']';
+ }
+
+ /**
+ * @param TDbConnection database connection.
+ * @return TDbCommandBuilder new command builder
+ */
+ public function createCommandBuilder($connection)
+ {
+ Prado::using('System.Data.Common.Mssql.TMssqlCommandBuilder');
+ return new TMssqlCommandBuilder($connection,$this);
+ }
+
+}
+
+?>
\ No newline at end of file diff --git a/framework/Data/Common/Mysql/TMysqlMetaData.php b/framework/Data/Common/Mysql/TMysqlMetaData.php index 2a6d80bd..9e947298 100644 --- a/framework/Data/Common/Mysql/TMysqlMetaData.php +++ b/framework/Data/Common/Mysql/TMysqlMetaData.php @@ -1,5 +1,4 @@ -<?php -
+<?php
/**
* Load the base TDbMetaData class.
*/
@@ -15,8 +14,10 @@ class TMysqlMetaData extends TDbMetaData */
protected function createTableInfo($table)
{
+ list($schemaName,$tableName) = $this->getSchemaTableName($table);
+ $find = $schemaName===null ? "`{$tableName}`" : "`{$schemaName}`.`{$tableName}`";
$this->getDbConnection()->setActive(true);
- $sql = "SHOW FULL FIELDS FROM {$table}";
+ $sql = "SHOW FULL FIELDS FROM {$find}";
$command = $this->getDbConnection()->createCommand($sql);
$tableInfo = $this->createNewTableInfo($table);
$index=0;
@@ -37,6 +38,7 @@ class TMysqlMetaData extends TDbMetaData $columnId = $col['Field'];
$info['ColumnName'] = "`$columnId`"; //quote the column names!
+ $info['ColumnId'] = $columnId;
$info['ColumnIndex'] = $col['index'];
if($col['Null']!=='NO')
$info['AllowNull'] = true;
@@ -122,7 +124,8 @@ class TMysqlMetaData extends TDbMetaData /**
* http://dev.mysql.com/doc/refman/5.0/en/identifiers.html
- * @param unknown_type $name
+ * @param string identifier name
+ * @param boolean true if valid identifier.
*/
protected function isValidIdentifier($name)
{
@@ -139,7 +142,8 @@ class TMysqlMetaData extends TDbMetaData list($schemaName,$tableName) = $this->getSchemaTableName($table);
$info['SchemaName'] = $schemaName;
$info['TableName'] = $tableName;
- $info['IsView'] = $this->getIsView($schemaName,$tableName);
+ if($this->getIsView($schemaName,$tableName))
+ $info['IsView'] = true;
list($primary, $foreign) = $this->getConstraintKeys($schemaName, $tableName);
return new TMysqlTableInfo($info,$primary,$foreign);
}
diff --git a/framework/Data/Common/Mysql/TMysqlTableColumn.php b/framework/Data/Common/Mysql/TMysqlTableColumn.php index 4f0f7da2..0e991d6b 100644 --- a/framework/Data/Common/Mysql/TMysqlTableColumn.php +++ b/framework/Data/Common/Mysql/TMysqlTableColumn.php @@ -59,7 +59,7 @@ class TMysqlTableColumn extends TDbTableColumn /**
* @return boolean true if auto increment is true.
*/
- public function getHasSequence()
+ public function hasSequence()
{
return $this->getAutoIncrement();
}
diff --git a/framework/Data/Common/Mysql/TMysqlTableInfo.php b/framework/Data/Common/Mysql/TMysqlTableInfo.php index e8585730..9bc01717 100644 --- a/framework/Data/Common/Mysql/TMysqlTableInfo.php +++ b/framework/Data/Common/Mysql/TMysqlTableInfo.php @@ -39,7 +39,10 @@ class TMysqlTableInfo extends TDbTableInfo */
public function getTableFullName()
{
- return '`'.$this->getSchemaName().'`.`'.$this->getTableName().'`';
+ if(($schema=$this->getSchemaName())!==null)
+ return '`'.$schema.'`.`'.$this->getTableName();
+ else
+ return '`'.$this->getTableName().'`';
}
}
diff --git a/framework/Data/Common/Pgsql/TPgsqlMetaData.php b/framework/Data/Common/Pgsql/TPgsqlMetaData.php index e964cfe7..ac7e6b7a 100644 --- a/framework/Data/Common/Pgsql/TPgsqlMetaData.php +++ b/framework/Data/Common/Pgsql/TPgsqlMetaData.php @@ -50,7 +50,7 @@ class TPgsqlMetaData extends TDbMetaData */
protected function getSchemaTableName($table)
{
- if(count($parts= explode('.', $table)) > 1)
+ if(count($parts= explode('.', str_replace('"','',$table))) > 1)
return array($parts[0], $parts[1]);
else
return array($this->getDefaultSchema(),$parts[0]);
@@ -171,6 +171,7 @@ EOD; $columnId = $col['attname']; //use column name as column Id
$info['ColumnName'] = '"'.$columnId.'"'; //quote the column names!
+ $info['ColumnId'] = $columnId;
$info['ColumnIndex'] = $col['index'];
if(!$col['attnotnull'])
$info['AllowNull'] = true;
diff --git a/framework/Data/Common/Pgsql/TPgsqlTableInfo.php b/framework/Data/Common/Pgsql/TPgsqlTableInfo.php index 88a56635..a2670fe0 100644 --- a/framework/Data/Common/Pgsql/TPgsqlTableInfo.php +++ b/framework/Data/Common/Pgsql/TPgsqlTableInfo.php @@ -39,7 +39,10 @@ class TPgsqlTableInfo extends TDbTableInfo */
public function getTableFullName()
{
- return $this->getSchemaName().'.'.$this->getTableName();
+ if(($schema=$this->getSchemaName())!==null)
+ return $schema.'.'.$this->getTableName();
+ else
+ $this->getTableName();
}
}
diff --git a/framework/Data/Common/Sqlite/TSqliteMetaData.php b/framework/Data/Common/Sqlite/TSqliteMetaData.php index 68734046..2ce46fb7 100644 --- a/framework/Data/Common/Sqlite/TSqliteMetaData.php +++ b/framework/Data/Common/Sqlite/TSqliteMetaData.php @@ -80,6 +80,7 @@ class TSqliteMetaData extends TDbMetaData $columnId = $col['name']; //use column name as column Id
$info['ColumnName'] = '"'.$columnId.'"'; //quote the column names!
+ $info['ColumnId'] = $columnId;
$info['ColumnIndex'] = $col['index'];
if($col['notnull']!=='99')
@@ -115,6 +116,8 @@ class TSqliteMetaData extends TDbMetaData }
/**
+ *
+ *
* @param string quoted table name.
* @return array foreign key details.
*/
@@ -145,6 +148,23 @@ class TSqliteMetaData extends TDbMetaData }
return false;
}
+}
-} +/**
+
+CREATE TABLE foo
+(
+ id INTEGER NOT NULL PRIMARY KEY,
+ id2 CHAR(2)
+);
+
+CREATE TABLE bar
+(
+ id INTEGER NOT NULL PRIMARY KEY,
+ foo_id INTEGER NOT NULL CONSTRAINT fk_foo_id REFERENCES foo(id) ON DELETE CASCADE,
+ foo_id2 CHAR(2) CONSTRAINT fk_foo_id REFERENCES foo(id2) ON DELETE CASCADE,
+ bar_id INTEGER NOT NULL CONSTRAINT fk_bar_id REFERENCES bar(id)
+);
+*/
+ ?>
\ No newline at end of file diff --git a/framework/Data/Common/Sqlite/TSqliteTableColumn.php b/framework/Data/Common/Sqlite/TSqliteTableColumn.php index cb379bfd..b8287218 100644 --- a/framework/Data/Common/Sqlite/TSqliteTableColumn.php +++ b/framework/Data/Common/Sqlite/TSqliteTableColumn.php @@ -53,7 +53,7 @@ class TSqliteTableColumn extends TDbTableColumn /**
* @return boolean true if auto increment is true.
*/
- public function getHasSequence()
+ public function hasSequence()
{
return $this->getAutoIncrement();
}
diff --git a/framework/Data/Common/TDbCommandBuilder.php b/framework/Data/Common/TDbCommandBuilder.php index 2a8f93ee..2c3578f0 100644 --- a/framework/Data/Common/TDbCommandBuilder.php +++ b/framework/Data/Common/TDbCommandBuilder.php @@ -67,6 +67,20 @@ class TDbCommandBuilder extends TComponent }
/**
+ * Iterate through all the columns and returns the last insert id of the
+ * first column that has a sequence or serial.
+ * @return mixed last insert id, null if none is found.
+ */
+ public function getLastInsertID()
+ {
+ foreach($this->getTableInfo()->getColumns() as $column)
+ {
+ if($column->hasSequence())
+ return $this->getDbConnection()->getLastInsertID($column->getSequenceName());
+ }
+ }
+
+ /**
* Alters the sql to apply $limit and $offset. Default implementation is applicable
* for PostgreSQL, MySQL and SQLite.
* @param string SQL query string.
@@ -122,6 +136,8 @@ class TDbCommandBuilder extends TComponent */
public function createFindCommand($where='1=1', $parameters=array(), $ordering=array(), $limit=-1, $offset=-1)
{
+ if($where===null)
+ $where='1=1';
$table = $this->getTableInfo()->getTableFullName();
$sql = "SELECT * FROM {$table} WHERE {$where}";
if(count($ordering) > 0)
@@ -141,6 +157,8 @@ class TDbCommandBuilder extends TComponent */
public function createCountCommand($where='1=1', $parameters=array(),$ordering=array(), $limit=-1, $offset=-1)
{
+ if($where===null)
+ $where='1=1';
$table = $this->getTableInfo()->getTableFullName();
$sql = "SELECT COUNT(*) FROM {$table} WHERE {$where}";
if(count($ordering) > 0)
diff --git a/framework/Data/Common/TDbMetaData.php b/framework/Data/Common/TDbMetaData.php index 477e2805..6838d7dc 100644 --- a/framework/Data/Common/TDbMetaData.php +++ b/framework/Data/Common/TDbMetaData.php @@ -64,9 +64,12 @@ abstract class TDbMetaData extends TComponent case 'sqlite2': //sqlite 2
Prado::using('System.Data.Common.Sqlite.TSqliteMetaData');
return new TSqliteMetaData($conn);
- case 'ibm':
- Prado::using('System.Data.Common.IbmDb2.TIbmDb2MetaData');
- return new TIbmDb2MetaData($conn);
+ case 'mssql':
+ Prado::using('System.Data.Common.Mssql.TMssqlMetaData');
+ return new TMssqlMetaData($conn);
+// case 'ibm':
+// Prado::using('System.Data.Common.IbmDb2.TIbmDb2MetaData');
+// return new TIbmDb2MetaData($conn);
default:
throw new TDbException('ar_invalid_database_driver',$driver);
}
diff --git a/framework/Data/Common/TDbTableColumn.php b/framework/Data/Common/TDbTableColumn.php index c5c9ac80..6ff2ff46 100644 --- a/framework/Data/Common/TDbTableColumn.php +++ b/framework/Data/Common/TDbTableColumn.php @@ -84,6 +84,14 @@ class TDbTableColumn extends TComponent }
/**
+ * @return string name of the column with quoted identifier.
+ */
+ public function getColumnId()
+ {
+ return $this->getInfo('ColumnId');
+ }
+
+ /**
* @return string size of the column.
*/
public function getColumnSize()
@@ -92,7 +100,7 @@ class TDbTableColumn extends TComponent }
/**
- * @return integer zero-based ordinal position of the column in the table.
+ * @return integer one-based ordinal position of the column in the table.
*/
public function getColumnIndex()
{
@@ -139,6 +147,15 @@ class TDbTableColumn extends TComponent return $this->getInfo('NumericScale');
}
+ public function getMaxiumNumericConstraint()
+ {
+ if(($precision=$this->getNumericPrecision())!==null)
+ {
+ $scale=$this->getNumericScale();
+ return $scale===null ? pow(10,$precision) : pow(10,$precision-$scale);
+ }
+ }
+
/**
* @return boolean whether this column is a primary key for the table, default is false.
*/
@@ -170,6 +187,14 @@ class TDbTableColumn extends TComponent {
return $this->getSequenceName()!==null;
}
+
+ /**
+ * @return boolean whether this column is excluded from insert and update.
+ */
+ public function getIsExcluded()
+ {
+ return false;
+ }
}
?>
\ No newline at end of file diff --git a/framework/Data/DataGateway/TDataGatewayCommand.php b/framework/Data/DataGateway/TDataGatewayCommand.php index a5f729d8..8a340ee7 100644 --- a/framework/Data/DataGateway/TDataGatewayCommand.php +++ b/framework/Data/DataGateway/TDataGatewayCommand.php @@ -15,7 +15,7 @@ class TDataGatewayCommand extends TComponent /**
* @return TDbTableInfo
*/
- protected function getTableInfo()
+ public function getTableInfo()
{
return $this->_builder->getTableInfo();
}
@@ -23,7 +23,7 @@ class TDataGatewayCommand extends TComponent /**
* @return TDbConnection
*/
- protected function getDbConnection()
+ public function getDbConnection()
{
return $this->_builder->getDbConnection();
}
@@ -31,7 +31,7 @@ class TDataGatewayCommand extends TComponent /**
* @return TDbCommandBuilder
*/
- protected function getBuilder()
+ public function getBuilder()
{
return $this->_builder;
}
@@ -66,6 +66,17 @@ class TDataGatewayCommand extends TComponent }
/**
+ * @param array update for update
+ * @param array primary key-value name pairs.
+ * @return integer number of records affected.
+ */
+ public function updateByPk($data, $keys)
+ {
+ list($where, $parameters) = $this->getPrimaryKeyCondition((array)$keys);
+ return $this->update($data, new TSqlCriteria($where, $parameters));
+ }
+
+ /**
* Find one record matching the critera.
* @param TSqlCriteria find conditions and parameters.
* @return array matching record.
@@ -281,11 +292,7 @@ class TDataGatewayCommand extends TComponent */
public function getLastInsertID()
{
- foreach($this->getTableInfo()->getColumns() as $column)
- {
- if($column->hasSequence())
- return $this->getDbConnection()->getLastInsertID($column->getSequenceName());
- }
+ return $this->getBuilder()->getLastInsertID();
}
/**
|