From 0f0d3c62e608287cdf77f1a3239371b521ecb40b Mon Sep 17 00:00:00 2001 From: wei <> Date: Sat, 14 Apr 2007 05:02:29 +0000 Subject: Refactor ActiveRecordGateway to use TDataGatewayCommand --- .../Exceptions/TActiveRecordException.php | 2 +- .../Data/ActiveRecord/Exceptions/messages.txt | 3 +- .../Scaffold/InputBuilder/TIbmScaffoldInput.php | 6 +- .../Scaffold/InputBuilder/TMssqlScaffoldInput.php | 43 ++++ .../Scaffold/InputBuilder/TMysqlScaffoldInput.php | 10 +- .../Scaffold/InputBuilder/TPgsqlScaffoldInput.php | 4 +- .../Scaffold/InputBuilder/TScaffoldInputBase.php | 15 +- .../Scaffold/InputBuilder/TScaffoldInputCommon.php | 32 ++- .../Scaffold/InputBuilder/TSqliteScaffoldInput.php | 10 +- .../Data/ActiveRecord/Scaffold/TScaffoldBase.php | 22 +- .../ActiveRecord/Scaffold/TScaffoldEditView.php | 4 +- .../ActiveRecord/Scaffold/TScaffoldEditView.tpl | 2 +- .../ActiveRecord/Scaffold/TScaffoldListView.php | 2 +- .../Data/ActiveRecord/Scaffold/TScaffoldSearch.php | 4 +- framework/Data/ActiveRecord/Scaffold/style.css | 2 +- framework/Data/ActiveRecord/TActiveRecord.php | 135 ++++------- .../Data/ActiveRecord/TActiveRecordGateway.php | 249 ++++++++++++--------- .../Data/ActiveRecord/TActiveRecordManager.php | 56 ----- .../ActiveRecord/TActiveRecordStateRegistry.php | 8 +- 19 files changed, 305 insertions(+), 304 deletions(-) create mode 100644 framework/Data/ActiveRecord/Scaffold/InputBuilder/TMssqlScaffoldInput.php (limited to 'framework/Data/ActiveRecord') 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 @@ +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 @@
+ input_<%# $this->ItemIndex %> property_<%# $this->DataItem->ColumnId %>"> 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,12 +274,24 @@ 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. * @@ -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 @@ -120,17 +120,6 @@ class TActiveRecordManager extends TComponent return $this->_gateway; } - /** - * @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. */ @@ -139,51 +128,6 @@ class TActiveRecordManager extends TComponent return new TActiveRecordGateway($this); } - /** - * 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. 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; } } -- cgit v1.2.3