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 +- .../Data/Common/IbmDb2/TIbmColumnMetaData.php | 157 +++++++++++++ framework/Data/Common/IbmDb2/TIbmMetaData.php | 113 ++++++++++ .../Data/Common/IbmDb2/TIbmMetaDataInspector.php | 113 ++++++++++ .../Data/Common/Mssql/TMssqlCommandBuilder.php | 173 ++++++++++++++ framework/Data/Common/Mssql/TMssqlMetaData.php | 202 +++++++++++++++++ framework/Data/Common/Mssql/TMssqlTableColumn.php | 56 +++++ framework/Data/Common/Mssql/TMssqlTableInfo.php | 62 +++++ framework/Data/Common/Mysql/TMysqlMetaData.php | 14 +- framework/Data/Common/Mysql/TMysqlTableColumn.php | 2 +- framework/Data/Common/Mysql/TMysqlTableInfo.php | 5 +- framework/Data/Common/Pgsql/TPgsqlMetaData.php | 3 +- framework/Data/Common/Pgsql/TPgsqlTableInfo.php | 5 +- framework/Data/Common/Sqlite/TSqliteMetaData.php | 22 +- .../Data/Common/Sqlite/TSqliteTableColumn.php | 2 +- framework/Data/Common/TDbCommandBuilder.php | 18 ++ framework/Data/Common/TDbMetaData.php | 9 +- framework/Data/Common/TDbTableColumn.php | 27 ++- framework/Data/DataGateway/TDataGatewayCommand.php | 23 +- framework/Exceptions/messages.txt | 2 +- .../Javascripts/source/prado/ratings/ratings.js | 2 +- .../source/prado/validator/validation3.js | 4 +- framework/Web/UI/WebControls/TRangeValidator.php | 54 ++++- framework/prado-cli.php | 8 +- 42 files changed, 1338 insertions(+), 347 deletions(-) create mode 100644 framework/Data/ActiveRecord/Scaffold/InputBuilder/TMssqlScaffoldInput.php create mode 100644 framework/Data/Common/IbmDb2/TIbmColumnMetaData.php create mode 100644 framework/Data/Common/IbmDb2/TIbmMetaData.php create mode 100644 framework/Data/Common/IbmDb2/TIbmMetaDataInspector.php create mode 100644 framework/Data/Common/Mssql/TMssqlCommandBuilder.php create mode 100644 framework/Data/Common/Mssql/TMssqlMetaData.php create mode 100644 framework/Data/Common/Mssql/TMssqlTableColumn.php create mode 100644 framework/Data/Common/Mssql/TMssqlTableInfo.php (limited to 'framework') 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; } } 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 @@ + + * @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 + * @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 @@ + + * @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 + * @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 @@ + + * @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 + * @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 @@ + + * @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 + * @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 + * + * + * 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) + * + * + * Regular expressions are used to alter the SQL query. The resulting SQL query + * may be malformed for complex queries. The following restrictions apply + * + *
    + *
  • + * In particular, commas should NOT + * be used as part of the ordering expression or identifier. Commas must only be + * used for separating the ordering clauses. + *
  • + *
  • + * 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. + *
  • + *
  • + * No clauses should follow the ORDER BY clause, e.g. no COMPUTE or FOR clauses. + *
  • + * + * @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 @@ +getCatalogSchemaTableName($table); + $this->getDbConnection()->setActive(true); + $sql = <<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 = <<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 = <<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 @@ + + * @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 + * @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 @@ + + * @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 + * @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 @@ -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 @@ -66,6 +66,20 @@ class TDbCommandBuilder extends TComponent return $this->_tableInfo; } + /** + * 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. @@ -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 @@ -83,6 +83,14 @@ class TDbTableColumn extends TComponent return $this->getInfo('ColumnName'); } + /** + * @return string name of the column with quoted identifier. + */ + public function getColumnId() + { + return $this->getInfo('ColumnId'); + } + /** * @return string size of the column. */ @@ -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; } @@ -65,6 +65,17 @@ class TDataGatewayCommand extends TComponent return $command->execute(); } + /** + * @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. @@ -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(); } /** diff --git a/framework/Exceptions/messages.txt b/framework/Exceptions/messages.txt index ff2a6927..ca7faed1 100644 --- a/framework/Exceptions/messages.txt +++ b/framework/Exceptions/messages.txt @@ -391,4 +391,4 @@ soapserver_version_invalid = Invalid SOAP version '{0}'. It must be either '1 dbusermanager_userclass_required = TDbUserManager.UserClass is required. dbusermanager_userclass_invalid = TDbUserManager.UserClass '{0}' is not a valid user class. The class must extend TDbUser. dbusermanager_connectionid_invalid = TDbUserManager.ConnectionID '{0}' does not point to a valid TDataSourceConfig module. -dbusermanager_connectionid_required = TDbUserManager.ConnectionID is required. \ No newline at end of file +dbusermanager_connectionid_required = TDbUserManager.ConnectionID is required. diff --git a/framework/Web/Javascripts/source/prado/ratings/ratings.js b/framework/Web/Javascripts/source/prado/ratings/ratings.js index 4e6ae6c5..d84b7aa1 100644 --- a/framework/Web/Javascripts/source/prado/ratings/ratings.js +++ b/framework/Web/Javascripts/source/prado/ratings/ratings.js @@ -57,4 +57,4 @@ Prado.WebUI.TRatingList.prototype = this.caption.innerHTML = index > -1 ? this.radios[index].value : this.options.caption; } -} \ No newline at end of file +}; diff --git a/framework/Web/Javascripts/source/prado/validator/validation3.js b/framework/Web/Javascripts/source/prado/validator/validation3.js index 5fa879a5..38e11cd0 100644 --- a/framework/Web/Javascripts/source/prado/validator/validation3.js +++ b/framework/Web/Javascripts/source/prado/validator/validation3.js @@ -1223,9 +1223,9 @@ Prado.WebUI.TRangeValidator = Class.extend(Prado.WebUI.TBaseValidator, var valid = true; if(min != null) - valid = valid && value >= min; + valid = valid && (this.options.StrictComparison ? value > min : value >= min); if(max != null) - valid = valid && value <= max; + valid = valid && (this.options.StrictComparison ? value < max : value <= max); return valid; } }); diff --git a/framework/Web/UI/WebControls/TRangeValidator.php b/framework/Web/UI/WebControls/TRangeValidator.php index a5cc2807..369b887b 100644 --- a/framework/Web/UI/WebControls/TRangeValidator.php +++ b/framework/Web/UI/WebControls/TRangeValidator.php @@ -36,6 +36,9 @@ Prado::using('System.Web.UI.WebControls.TBaseValidator'); * - String A string data type. * - StringLength check for string length. * + * If {@link setStrictComparison StrictComparison} is true, then the ranges + * are compared as strictly less than the max value and/or strictly greater than the min value. + * * The TRangeValidator allows a special DataType "StringLength" that * can be used to verify minimum and maximum string length. The * {@link setCharset Charset} property can be used to force a particular @@ -93,6 +96,22 @@ class TRangeValidator extends TBaseValidator $this->setViewState('MaxValue',$value,''); } + /** + * @param boolean true to perform strict comparison (i.e. strictly less than max and/or strictly greater than min). + */ + public function setStrictComparison($value) + { + $this->setViewState('StrictComparison', TPropertyValue::ensureBoolean($value),false); + } + + /** + * @return boolean true to perform strict comparison. + */ + public function getStrictComparison() + { + return $this->getViewState('StrictComparison', false); + } + /** * @return TRangeValidationDataType the data type that the values being compared are * converted to before the comparison is made. Defaults to TRangeValidationDataType::String. @@ -184,12 +203,22 @@ class TRangeValidator extends TBaseValidator $value=intval($value); $valid=true; if($minValue!=='') - $valid=$valid && ($value>=intval($minValue)); + $valid=$valid && $this->isGreaterThan($value, intval($minValue)); if($maxValue!=='') - $valid=$valid && ($value<=intval($maxValue)); + $valid=$valid && $this->isLessThan($value,intval($maxValue)); return $valid; } + protected function isLessThan($left,$right) + { + return $this->getStrictComparison() ? $left < $right : $left <= $right; + } + + protected function isGreaterThan($left, $right) + { + return $this->getStrictComparison() ? $left > $right : $left >= $right; + } + /** * Determine if the value is within the specified float range. * @param string value to validate @@ -203,9 +232,9 @@ class TRangeValidator extends TBaseValidator $value=floatval($value); $valid=true; if($minValue!=='') - $valid=$valid && ($value>=floatval($minValue)); + $valid=$valid && $this->isGreaterThan($value,floatval($minValue)); if($maxValue!=='') - $valid=$valid && ($value<=floatval($maxValue)); + $valid=$valid && $this->isLessThan($value,floatval($maxValue)); return $valid; } @@ -228,18 +257,18 @@ class TRangeValidator extends TBaseValidator $formatter=Prado::createComponent('System.Util.TSimpleDateFormatter', $dateFormat); $value = $formatter->parse($value, $dateFormat); if($minValue!=='') - $valid=$valid && ($value>=$formatter->parse($minValue)); + $valid=$valid && $this->isGreaterThan($value,$formatter->parse($minValue)); if($maxValue!=='') - $valid=$valid && ($value<=$formatter->parse($maxValue)); + $valid=$valid && $this->isLessThan($value,$formatter->parse($maxValue)); return $valid; } else { $value=strtotime($value); if($minValue!=='') - $valid=$valid && ($value>=strtotime($minValue)); + $valid=$valid && $this->isGreaterThan($value,strtotime($minValue)); if($maxValue!=='') - $valid=$valid && ($value<=strtotime($maxValue)); + $valid=$valid && $this->isLessThan($value,strtotime($maxValue)); return $valid; } } @@ -257,9 +286,9 @@ class TRangeValidator extends TBaseValidator $valid=true; if($minValue!=='') - $valid=$valid && (strcmp($value,$minValue)>=0); + $valid=$valid && $this->isGreaterThan(strcmp($value,$minValue),0); if($maxValue!=='') - $valid=$valid && (strcmp($value,$maxValue)<=0); + $valid=$valid && $this->isLessThan(strcmp($value,$maxValue),0); return $valid; } @@ -284,9 +313,9 @@ class TRangeValidator extends TBaseValidator $length = iconv_strlen($value, $charset); if($minValue!=='') - $valid = $valid && $length >= intval($minValue); + $valid = $valid && $this->isGreaterThan($length,intval($minValue)); if($maxValue!=='') - $valid = $valid && $length <= intval($maxValue); + $valid = $valid && $this->isLessThan($length,intval($maxValue)); return $valid; } @@ -300,6 +329,7 @@ class TRangeValidator extends TBaseValidator $options['MinValue']=$this->getMinValue(); $options['MaxValue']=$this->getMaxValue(); $options['DataType']=$this->getDataType(); + $options['StrictComparison']=$this->getStrictComparison(); if(($dateFormat=$this->getDateFormat())!=='') $options['DateFormat']=$dateFormat; return $options; diff --git a/framework/prado-cli.php b/framework/prado-cli.php index 537cbf98..ca3e1461 100755 --- a/framework/prado-cli.php +++ b/framework/prado-cli.php @@ -662,9 +662,9 @@ class PradoCommandLineActiveRecordGen extends PradoCommandLineAction protected function generateActiveRecord($config, $tablename, $output) { $manager = TActiveRecordManager::getInstance(); - $inspector = $manager->getTableInspector($manager->getDbConnection()); - $meta = $inspector->getTableMetaData($tablename); - if(count($meta->getColumns()) === 0) + $gateway = $manager->getRecordGateway(); + $tableInfo = $gateway->getTableInfo($manager->getDbConnection(), $tablename); + if(count($tableInfo->getColumns()) === 0) { echo '** Unable to find table or view "'.$tablename.'" in "'.$manager->getDbConnection()->getConnectionString()."\".\n"; return false; @@ -672,7 +672,7 @@ class PradoCommandLineActiveRecordGen extends PradoCommandLineAction else { $properties = array(); - foreach($meta->getColumns() as $field=>$column) + foreach($tableInfo->getColumns() as $field=>$column) $properties[] = $this->generateProperty($field,$column); } -- cgit v1.2.3