diff options
Diffstat (limited to 'framework/Data')
74 files changed, 14941 insertions, 14941 deletions
diff --git a/framework/Data/ActiveRecord/Exceptions/TActiveRecordException.php b/framework/Data/ActiveRecord/Exceptions/TActiveRecordException.php index a5d6405e..bcfbfbcb 100644 --- a/framework/Data/ActiveRecord/Exceptions/TActiveRecordException.php +++ b/framework/Data/ActiveRecord/Exceptions/TActiveRecordException.php @@ -1,49 +1,49 @@ -<?php
-/**
- * TActiveRecordException class file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.ActiveRecord
- */
-
-/**
- * Base exception class for Active Records.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.ActiveRecord
- * @since 3.1
- */
-class TActiveRecordException extends TDbException
-{
- /**
- * @return string path to the error message file
- */
- protected function getErrorMessageFile()
- {
- $lang=Prado::getPreferredLanguage();
- $path = dirname(__FILE__);
- $msgFile=$path.'/messages-'.$lang.'.txt';
- if(!is_file($msgFile))
- $msgFile=$path.'/messages.txt';
- return $msgFile;
- }
-}
-
-/**
- * TActiveRecordConfigurationException class.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.ActiveRecord
- * @since 3.1
- */
-class TActiveRecordConfigurationException extends TActiveRecordException
-{
-
-}
-
+<?php +/** + * TActiveRecordException class file. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.ActiveRecord + */ + +/** + * Base exception class for Active Records. + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.ActiveRecord + * @since 3.1 + */ +class TActiveRecordException extends TDbException +{ + /** + * @return string path to the error message file + */ + protected function getErrorMessageFile() + { + $lang=Prado::getPreferredLanguage(); + $path = dirname(__FILE__); + $msgFile=$path.'/messages-'.$lang.'.txt'; + if(!is_file($msgFile)) + $msgFile=$path.'/messages.txt'; + return $msgFile; + } +} + +/** + * TActiveRecordConfigurationException class. + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.ActiveRecord + * @since 3.1 + */ +class TActiveRecordConfigurationException extends TActiveRecordException +{ + +} + diff --git a/framework/Data/ActiveRecord/Relations/TActiveRecordBelongsTo.php b/framework/Data/ActiveRecord/Relations/TActiveRecordBelongsTo.php index 68510042..f89660a6 100644 --- a/framework/Data/ActiveRecord/Relations/TActiveRecordBelongsTo.php +++ b/framework/Data/ActiveRecord/Relations/TActiveRecordBelongsTo.php @@ -1,138 +1,138 @@ -<?php
-/**
- * TActiveRecordBelongsTo class file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
+<?php +/** + * TActiveRecordBelongsTo class file. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.ActiveRecord.Relations
- */
-
-/**
- * Loads base active record relationship class.
- */
-Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordRelation');
-
-/**
- * Implements the foreign key relationship (TActiveRecord::BELONGS_TO) between
- * the source objects and the related foreign object. Consider the
- * <b>entity</b> relationship between a Team and a Player.
- * <code>
- * +------+ +--------+
- * | Team | 1 <----- * | Player |
- * +------+ +--------+
- * </code>
- * Where one team may have 0 or more players and each player belongs to only
- * one team. We may model Team-Player <b>object</b> relationship as active record as follows.
- * <code>
- * class TeamRecord extends TActiveRecord
- * {
- * // see TActiveRecordHasMany for detailed definition.
- * }
- * class PlayerRecord extends TActiveRecord
- * {
- * const TABLE='player';
- * public $player_id; //primary key
- * public $team_name; //foreign key player.team_name <-> team.name
- * public $age;
- * public $team; //foreign object TeamRecord
- *
- * public static $RELATIONS = array
- * (
- * 'team' => array(self::BELONGS_TO, 'TeamRecord')
- * );
- *
- * public static function finder($className=__CLASS__)
- * {
- * return parent::finder($className);
- * }
- * }
- * </code>
- * The static <tt>$RELATIONS</tt> property of PlayerRecord defines that the
- * property <tt>$team</tt> belongs to a <tt>TeamRecord</tt>.
- *
- * The team object may be fetched as follows.
- * <code>
- * $players = PlayerRecord::finder()->with_team()->findAll();
- * </code>
- * The method <tt>with_xxx()</tt> (where <tt>xxx</tt> is the relationship property
- * name, in this case, <tt>team</tt>) fetchs the corresponding TeamRecords using
- * a second query (not by using a join). The <tt>with_xxx()</tt> accepts the same
- * arguments as other finder methods of TActiveRecord, e.g.
- * <tt>with_team('location = ?', 'Madrid')</tt>.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.ActiveRecord.Relations
- * @since 3.1
- */
-class TActiveRecordBelongsTo extends TActiveRecordRelation
-{
- /**
- * Get the foreign key index values from the results and make calls to the
- * database to find the corresponding foreign objects.
- * @param array original results.
- */
- protected function collectForeignObjects(&$results)
- {
- $fkeys = $this->getRelationForeignKeys();
-
- $properties = array_keys($fkeys);
- $fields = array_values($fkeys);
- $indexValues = $this->getIndexValues($properties, $results);
- $fkObjects = $this->findForeignObjects($fields, $indexValues);
- $this->populateResult($results,$properties,$fkObjects,$fields);
- }
-
- /**
- * @return array foreign key field names as key and object properties as value.
- * @since 3.1.2
- */
- public function getRelationForeignKeys()
- {
- $fkObject = $this->getContext()->getForeignRecordFinder();
- return $this->findForeignKeys($this->getSourceRecord(),$fkObject);
- }
-
- /**
- * Sets the foreign objects to the given property on the source object.
- * @param TActiveRecord source object.
- * @param array foreign objects.
- */
- protected function setObjectProperty($source, $properties, &$collections)
- {
- $hash = $this->getObjectHash($source, $properties);
- $prop = $this->getContext()->getProperty();
- if(isset($collections[$hash]) && count($collections[$hash]) > 0)
- {
- if(count($collections[$hash]) > 1)
- throw new TActiveRecordException('ar_belongs_to_multiple_result');
- $source->$prop=$collections[$hash][0];
- }
- else
- $source->$prop=null;
- }
-
- /**
- * Updates the source object first.
- * @return boolean true if all update are success (including if no update was required), false otherwise .
- */
- public function updateAssociatedRecords()
- {
- $obj = $this->getContext()->getSourceRecord();
- $fkObject = $obj->getColumnValue($this->getContext()->getProperty());
- if($fkObject!==null)
- {
- $fkObject->save();
- $source = $this->getSourceRecord();
- $fkeys = $this->findForeignKeys($source, $fkObject);
- foreach($fkeys as $srcKey => $fKey)
- $source->setColumnValue($srcKey, $fkObject->getColumnValue($fKey));
- return true;
- }
- return false;
- }
-}
-
+ * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.ActiveRecord.Relations + */ + +/** + * Loads base active record relationship class. + */ +Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordRelation'); + +/** + * Implements the foreign key relationship (TActiveRecord::BELONGS_TO) between + * the source objects and the related foreign object. Consider the + * <b>entity</b> relationship between a Team and a Player. + * <code> + * +------+ +--------+ + * | Team | 1 <----- * | Player | + * +------+ +--------+ + * </code> + * Where one team may have 0 or more players and each player belongs to only + * one team. We may model Team-Player <b>object</b> relationship as active record as follows. + * <code> + * class TeamRecord extends TActiveRecord + * { + * // see TActiveRecordHasMany for detailed definition. + * } + * class PlayerRecord extends TActiveRecord + * { + * const TABLE='player'; + * public $player_id; //primary key + * public $team_name; //foreign key player.team_name <-> team.name + * public $age; + * public $team; //foreign object TeamRecord + * + * public static $RELATIONS = array + * ( + * 'team' => array(self::BELONGS_TO, 'TeamRecord') + * ); + * + * public static function finder($className=__CLASS__) + * { + * return parent::finder($className); + * } + * } + * </code> + * The static <tt>$RELATIONS</tt> property of PlayerRecord defines that the + * property <tt>$team</tt> belongs to a <tt>TeamRecord</tt>. + * + * The team object may be fetched as follows. + * <code> + * $players = PlayerRecord::finder()->with_team()->findAll(); + * </code> + * The method <tt>with_xxx()</tt> (where <tt>xxx</tt> is the relationship property + * name, in this case, <tt>team</tt>) fetchs the corresponding TeamRecords using + * a second query (not by using a join). The <tt>with_xxx()</tt> accepts the same + * arguments as other finder methods of TActiveRecord, e.g. + * <tt>with_team('location = ?', 'Madrid')</tt>. + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.ActiveRecord.Relations + * @since 3.1 + */ +class TActiveRecordBelongsTo extends TActiveRecordRelation +{ + /** + * Get the foreign key index values from the results and make calls to the + * database to find the corresponding foreign objects. + * @param array original results. + */ + protected function collectForeignObjects(&$results) + { + $fkeys = $this->getRelationForeignKeys(); + + $properties = array_keys($fkeys); + $fields = array_values($fkeys); + $indexValues = $this->getIndexValues($properties, $results); + $fkObjects = $this->findForeignObjects($fields, $indexValues); + $this->populateResult($results,$properties,$fkObjects,$fields); + } + + /** + * @return array foreign key field names as key and object properties as value. + * @since 3.1.2 + */ + public function getRelationForeignKeys() + { + $fkObject = $this->getContext()->getForeignRecordFinder(); + return $this->findForeignKeys($this->getSourceRecord(),$fkObject); + } + + /** + * Sets the foreign objects to the given property on the source object. + * @param TActiveRecord source object. + * @param array foreign objects. + */ + protected function setObjectProperty($source, $properties, &$collections) + { + $hash = $this->getObjectHash($source, $properties); + $prop = $this->getContext()->getProperty(); + if(isset($collections[$hash]) && count($collections[$hash]) > 0) + { + if(count($collections[$hash]) > 1) + throw new TActiveRecordException('ar_belongs_to_multiple_result'); + $source->$prop=$collections[$hash][0]; + } + else + $source->$prop=null; + } + + /** + * Updates the source object first. + * @return boolean true if all update are success (including if no update was required), false otherwise . + */ + public function updateAssociatedRecords() + { + $obj = $this->getContext()->getSourceRecord(); + $fkObject = $obj->getColumnValue($this->getContext()->getProperty()); + if($fkObject!==null) + { + $fkObject->save(); + $source = $this->getSourceRecord(); + $fkeys = $this->findForeignKeys($source, $fkObject); + foreach($fkeys as $srcKey => $fKey) + $source->setColumnValue($srcKey, $fkObject->getColumnValue($fKey)); + return true; + } + return false; + } +} + diff --git a/framework/Data/ActiveRecord/Relations/TActiveRecordHasMany.php b/framework/Data/ActiveRecord/Relations/TActiveRecordHasMany.php index f5bc4438..6c5628b8 100644 --- a/framework/Data/ActiveRecord/Relations/TActiveRecordHasMany.php +++ b/framework/Data/ActiveRecord/Relations/TActiveRecordHasMany.php @@ -1,121 +1,121 @@ -<?php
-/**
- * TActiveRecordHasMany class file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
+<?php +/** + * TActiveRecordHasMany class file. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.ActiveRecord.Relations
- */
-
-/**
- * Loads base active record relations class.
- */
-Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordRelation');
-
-/**
- * Implements TActiveRecord::HAS_MANY relationship between the source object having zero or
- * more foreign objects. Consider the <b>entity</b> relationship between a Team and a Player.
- * <code>
- * +------+ +--------+
- * | Team | 1 <----- * | Player |
- * +------+ +--------+
- * </code>
- * Where one team may have 0 or more players and each player belongs to only
- * one team. We may model Team-Player <b>object</b> relationship as active record as follows.
- * <code>
- * class TeamRecord extends TActiveRecord
- * {
- * const TABLE='team';
- * public $name; //primary key
- * public $location;
- *
- * public $players=array(); //list of players
- *
- * public static $RELATIONS=array
- * (
- * 'players' => array(self::HAS_MANY, 'PlayerRecord')
- * );
- *
- * public static function finder($className=__CLASS__)
- * {
- * return parent::finder($className);
- * }
- * }
- * class PlayerRecord extends TActiveRecord
- * {
- * // see TActiveRecordBelongsTo for detailed definition
- * }
- * </code>
- * The static <tt>$RELATIONS</tt> property of TeamRecord defines that the
- * property <tt>$players</tt> has many <tt>PlayerRecord</tt>s.
- *
- * The players list may be fetched as follows.
- * <code>
- * $team = TeamRecord::finder()->with_players()->findAll();
- * </code>
- * The method <tt>with_xxx()</tt> (where <tt>xxx</tt> is the relationship property
- * name, in this case, <tt>players</tt>) fetchs the corresponding PlayerRecords using
- * a second query (not by using a join). The <tt>with_xxx()</tt> accepts the same
- * arguments as other finder methods of TActiveRecord, e.g. <tt>with_players('age < ?', 35)</tt>.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.ActiveRecord.Relations
- * @since 3.1
- */
-class TActiveRecordHasMany extends TActiveRecordRelation
-{
- /**
- * Get the foreign key index values from the results and make calls to the
- * database to find the corresponding foreign objects.
- * @param array original results.
- */
- protected function collectForeignObjects(&$results)
- {
- $fkeys = $this->getRelationForeignKeys();
-
- $properties = array_values($fkeys);
- $fields = array_keys($fkeys);
-
- $indexValues = $this->getIndexValues($properties, $results);
- $fkObjects = $this->findForeignObjects($fields,$indexValues);
- $this->populateResult($results,$properties,$fkObjects,$fields);
- }
-
- /**
- * @return array foreign key field names as key and object properties as value.
- * @since 3.1.2
- */
- public function getRelationForeignKeys()
- {
- $fkObject = $this->getContext()->getForeignRecordFinder();
- return $this->findForeignKeys($fkObject, $this->getSourceRecord());
- }
-
- /**
- * Updates the associated foreign objects.
- * @return boolean true if all update are success (including if no update was required), false otherwise .
- */
- public function updateAssociatedRecords()
- {
- $obj = $this->getContext()->getSourceRecord();
- $fkObjects = &$obj->{$this->getContext()->getProperty()};
- $success=true;
- if(($total = count($fkObjects))> 0)
- {
- $source = $this->getSourceRecord();
- $fkeys = $this->findForeignKeys($fkObjects[0], $source);
- for($i=0;$i<$total;$i++)
- {
- foreach($fkeys as $fKey => $srcKey)
- $fkObjects[$i]->setColumnValue($fKey, $source->getColumnValue($srcKey));
- $success = $fkObjects[$i]->save() && $success;
- }
- }
- return $success;
- }
-}
-
+ * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.ActiveRecord.Relations + */ + +/** + * Loads base active record relations class. + */ +Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordRelation'); + +/** + * Implements TActiveRecord::HAS_MANY relationship between the source object having zero or + * more foreign objects. Consider the <b>entity</b> relationship between a Team and a Player. + * <code> + * +------+ +--------+ + * | Team | 1 <----- * | Player | + * +------+ +--------+ + * </code> + * Where one team may have 0 or more players and each player belongs to only + * one team. We may model Team-Player <b>object</b> relationship as active record as follows. + * <code> + * class TeamRecord extends TActiveRecord + * { + * const TABLE='team'; + * public $name; //primary key + * public $location; + * + * public $players=array(); //list of players + * + * public static $RELATIONS=array + * ( + * 'players' => array(self::HAS_MANY, 'PlayerRecord') + * ); + * + * public static function finder($className=__CLASS__) + * { + * return parent::finder($className); + * } + * } + * class PlayerRecord extends TActiveRecord + * { + * // see TActiveRecordBelongsTo for detailed definition + * } + * </code> + * The static <tt>$RELATIONS</tt> property of TeamRecord defines that the + * property <tt>$players</tt> has many <tt>PlayerRecord</tt>s. + * + * The players list may be fetched as follows. + * <code> + * $team = TeamRecord::finder()->with_players()->findAll(); + * </code> + * The method <tt>with_xxx()</tt> (where <tt>xxx</tt> is the relationship property + * name, in this case, <tt>players</tt>) fetchs the corresponding PlayerRecords using + * a second query (not by using a join). The <tt>with_xxx()</tt> accepts the same + * arguments as other finder methods of TActiveRecord, e.g. <tt>with_players('age < ?', 35)</tt>. + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.ActiveRecord.Relations + * @since 3.1 + */ +class TActiveRecordHasMany extends TActiveRecordRelation +{ + /** + * Get the foreign key index values from the results and make calls to the + * database to find the corresponding foreign objects. + * @param array original results. + */ + protected function collectForeignObjects(&$results) + { + $fkeys = $this->getRelationForeignKeys(); + + $properties = array_values($fkeys); + $fields = array_keys($fkeys); + + $indexValues = $this->getIndexValues($properties, $results); + $fkObjects = $this->findForeignObjects($fields,$indexValues); + $this->populateResult($results,$properties,$fkObjects,$fields); + } + + /** + * @return array foreign key field names as key and object properties as value. + * @since 3.1.2 + */ + public function getRelationForeignKeys() + { + $fkObject = $this->getContext()->getForeignRecordFinder(); + return $this->findForeignKeys($fkObject, $this->getSourceRecord()); + } + + /** + * Updates the associated foreign objects. + * @return boolean true if all update are success (including if no update was required), false otherwise . + */ + public function updateAssociatedRecords() + { + $obj = $this->getContext()->getSourceRecord(); + $fkObjects = &$obj->{$this->getContext()->getProperty()}; + $success=true; + if(($total = count($fkObjects))> 0) + { + $source = $this->getSourceRecord(); + $fkeys = $this->findForeignKeys($fkObjects[0], $source); + for($i=0;$i<$total;$i++) + { + foreach($fkeys as $fKey => $srcKey) + $fkObjects[$i]->setColumnValue($fKey, $source->getColumnValue($srcKey)); + $success = $fkObjects[$i]->save() && $success; + } + } + return $success; + } +} + diff --git a/framework/Data/ActiveRecord/Relations/TActiveRecordHasManyAssociation.php b/framework/Data/ActiveRecord/Relations/TActiveRecordHasManyAssociation.php index fe368f9c..28ebb317 100644 --- a/framework/Data/ActiveRecord/Relations/TActiveRecordHasManyAssociation.php +++ b/framework/Data/ActiveRecord/Relations/TActiveRecordHasManyAssociation.php @@ -1,376 +1,376 @@ -<?php
-/**
- * TActiveRecordHasManyAssociation class file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
+<?php +/** + * TActiveRecordHasManyAssociation class file. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.ActiveRecord.Relations
- */
-
-/**
- * Loads base active record relations class.
- */
-Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordRelation');
-
-/**
- * Implements the M-N (many to many) relationship via association table.
- * Consider the <b>entity</b> relationship between Articles and Categories
- * via the association table <tt>Article_Category</tt>.
- * <code>
- * +---------+ +------------------+ +----------+
- * | Article | * -----> * | Article_Category | * <----- * | Category |
- * +---------+ +------------------+ +----------+
- * </code>
- * Where one article may have 0 or more categories and each category may have 0
- * or more articles. We may model Article-Category <b>object</b> relationship
- * as active record as follows.
- * <code>
- * class ArticleRecord
- * {
- * const TABLE='Article';
- * public $article_id;
- *
- * public $Categories=array(); //foreign object collection.
- *
- * public static $RELATIONS = array
- * (
- * 'Categories' => array(self::MANY_TO_MANY, 'CategoryRecord', 'Article_Category')
- * );
- *
- * public static function finder($className=__CLASS__)
- * {
- * return parent::finder($className);
- * }
- * }
- * class CategoryRecord
- * {
- * const TABLE='Category';
- * public $category_id;
- *
- * public $Articles=array();
- *
- * public static $RELATIONS = array
- * (
- * 'Articles' => array(self::MANY_TO_MANY, 'ArticleRecord', 'Article_Category')
- * );
- *
- * public static function finder($className=__CLASS__)
- * {
- * return parent::finder($className);
- * }
- * }
- * </code>
- *
- * The static <tt>$RELATIONS</tt> property of ArticleRecord defines that the
- * property <tt>$Categories</tt> has many <tt>CategoryRecord</tt>s. Similar, the
- * static <tt>$RELATIONS</tt> property of CategoryRecord defines many ArticleRecords.
- *
- * The articles with categories list may be fetched as follows.
- * <code>
- * $articles = TeamRecord::finder()->withCategories()->findAll();
- * </code>
- * The method <tt>with_xxx()</tt> (where <tt>xxx</tt> is the relationship property
- * name, in this case, <tt>Categories</tt>) fetchs the corresponding CategoryRecords using
- * a second query (not by using a join). The <tt>with_xxx()</tt> accepts the same
- * arguments as other finder methods of TActiveRecord.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.ActiveRecord.Relations
- * @since 3.1
- */
-class TActiveRecordHasManyAssociation extends TActiveRecordRelation
-{
- private $_association;
- private $_sourceTable;
- private $_foreignTable;
- private $_association_columns=array();
-
- /**
- * Get the foreign key index values from the results and make calls to the
- * database to find the corresponding foreign objects using association table.
- * @param array original results.
- */
- protected function collectForeignObjects(&$results)
- {
- list($sourceKeys, $foreignKeys) = $this->getRelationForeignKeys();
- $properties = array_values($sourceKeys);
- $indexValues = $this->getIndexValues($properties, $results);
- $this->fetchForeignObjects($results, $foreignKeys,$indexValues,$sourceKeys);
- }
-
- /**
- * @return array 2 arrays of source keys and foreign keys from the association table.
- */
- public function getRelationForeignKeys()
- {
- $association = $this->getAssociationTable();
- $sourceKeys = $this->findForeignKeys($association, $this->getSourceRecord(), true);
- $fkObject = $this->getContext()->getForeignRecordFinder();
- $foreignKeys = $this->findForeignKeys($association, $fkObject);
- return array($sourceKeys, $foreignKeys);
- }
-
- /**
- * @return TDbTableInfo association table information.
- */
- protected function getAssociationTable()
- {
- if($this->_association===null)
- {
- $gateway = $this->getSourceRecord()->getRecordGateway();
- $conn = $this->getSourceRecord()->getDbConnection();
- //table name may include the fk column name separated with a dot.
- $table = explode('.', $this->getContext()->getAssociationTable());
- if(count($table)>1)
- {
- $columns = preg_replace('/^\((.*)\)/', '\1', $table[1]);
- $this->_association_columns = preg_split('/\s*[, ]\*/',$columns);
- }
- $this->_association = $gateway->getTableInfo($conn, $table[0]);
- }
- return $this->_association;
- }
-
- /**
- * @return TDbTableInfo source table information.
- */
- protected function getSourceTable()
- {
- if($this->_sourceTable===null)
- {
- $gateway = $this->getSourceRecord()->getRecordGateway();
- $this->_sourceTable = $gateway->getRecordTableInfo($this->getSourceRecord());
- }
- return $this->_sourceTable;
- }
-
- /**
- * @return TDbTableInfo foreign table information.
- */
- protected function getForeignTable()
- {
- if($this->_foreignTable===null)
- {
- $gateway = $this->getSourceRecord()->getRecordGateway();
- $fkObject = $this->getContext()->getForeignRecordFinder();
- $this->_foreignTable = $gateway->getRecordTableInfo($fkObject);
- }
- return $this->_foreignTable;
- }
-
- /**
- * @return TDataGatewayCommand
- */
- protected function getCommandBuilder()
- {
- return $this->getSourceRecord()->getRecordGateway()->getCommand($this->getSourceRecord());
- }
-
- /**
- * @return TDataGatewayCommand
- */
- protected function getForeignCommandBuilder()
- {
- $obj = $this->getContext()->getForeignRecordFinder();
- return $this->getSourceRecord()->getRecordGateway()->getCommand($obj);
- }
-
-
- /**
- * Fetches the foreign objects using TActiveRecord::findAllByIndex()
- * @param array field names
- * @param array foreign key index values.
- */
- protected function fetchForeignObjects(&$results,$foreignKeys,$indexValues,$sourceKeys)
- {
- $criteria = $this->getCriteria();
- $finder = $this->getContext()->getForeignRecordFinder();
- $type = get_class($finder);
- $command = $this->createCommand($criteria, $foreignKeys,$indexValues,$sourceKeys);
- $srcProps = array_keys($sourceKeys);
- $collections=array();
- foreach($this->getCommandBuilder()->onExecuteCommand($command, $command->query()) as $row)
- {
- $hash = $this->getObjectHash($row, $srcProps);
- foreach($srcProps as $column)
- unset($row[$column]);
- $obj = $this->createFkObject($type,$row,$foreignKeys);
- $collections[$hash][] = $obj;
- }
- $this->setResultCollection($results, $collections, array_values($sourceKeys));
- }
-
- /**
- * @param string active record class name.
- * @param array row data
- * @param array foreign key column names
- * @return TActiveRecord
- */
- protected function createFkObject($type,$row,$foreignKeys)
- {
- $obj = TActiveRecord::createRecord($type, $row);
- if(count($this->_association_columns) > 0)
- {
- $i=0;
- foreach($foreignKeys as $ref=>$fk)
- $obj->setColumnValue($ref, $row[$this->_association_columns[$i++]]);
- }
- return $obj;
- }
-
- /**
- * @param TSqlCriteria
- * @param TTableInfo association table info
- * @param array field names
- * @param array field values
- */
- public function createCommand($criteria, $foreignKeys,$indexValues,$sourceKeys)
- {
- $innerJoin = $this->getAssociationJoin($foreignKeys,$indexValues,$sourceKeys);
- $fkTable = $this->getForeignTable()->getTableFullName();
- $srcColumns = $this->getSourceColumns($sourceKeys);
- if(($where=$criteria->getCondition())===null)
- $where='1=1';
- $sql = "SELECT {$fkTable}.*, {$srcColumns} FROM {$fkTable} {$innerJoin} WHERE {$where}";
-
- $parameters = $criteria->getParameters()->toArray();
- $ordering = $criteria->getOrdersBy();
- $limit = $criteria->getLimit();
- $offset = $criteria->getOffset();
-
- $builder = $this->getForeignCommandBuilder()->getBuilder();
- $command = $builder->applyCriterias($sql,$parameters,$ordering,$limit,$offset);
- $this->getCommandBuilder()->onCreateCommand($command, $criteria);
- return $command;
- }
-
- /**
- * @param array source table column names.
- * @return string comma separated source column names.
- */
- protected function getSourceColumns($sourceKeys)
- {
- $columns=array();
- $table = $this->getAssociationTable();
- $tableName = $table->getTableFullName();
- $columnNames = array_merge(array_keys($sourceKeys),$this->_association_columns);
- foreach($columnNames as $name)
- $columns[] = $tableName.'.'.$table->getColumn($name)->getColumnName();
- return implode(', ', $columns);
- }
-
- /**
- * SQL inner join for M-N relationship via association table.
- * @param array foreign table column key names.
- * @param array source table index values.
- * @param array source table column names.
- * @return string inner join condition for M-N relationship via association table.
- */
- protected function getAssociationJoin($foreignKeys,$indexValues,$sourceKeys)
- {
- $refInfo= $this->getAssociationTable();
- $fkInfo = $this->getForeignTable();
-
- $refTable = $refInfo->getTableFullName();
- $fkTable = $fkInfo->getTableFullName();
-
- $joins = array();
- $hasAssociationColumns = count($this->_association_columns) > 0;
- $i=0;
- foreach($foreignKeys as $ref=>$fk)
- {
- if($hasAssociationColumns)
- $refField = $refInfo->getColumn($this->_association_columns[$i++])->getColumnName();
- else
- $refField = $refInfo->getColumn($ref)->getColumnName();
- $fkField = $fkInfo->getColumn($fk)->getColumnName();
- $joins[] = "{$fkTable}.{$fkField} = {$refTable}.{$refField}";
- }
- $joinCondition = implode(' AND ', $joins);
- $index = $this->getCommandBuilder()->getIndexKeyCondition($refInfo,array_keys($sourceKeys), $indexValues);
- return "INNER JOIN {$refTable} ON ({$joinCondition}) AND {$index}";
- }
-
- /**
- * Updates the associated foreign objects.
- * @return boolean true if all update are success (including if no update was required), false otherwise .
- */
- public function updateAssociatedRecords()
- {
- $obj = $this->getContext()->getSourceRecord();
- $fkObjects = &$obj->{$this->getContext()->getProperty()};
- $success=true;
- if(($total = count($fkObjects))> 0)
- {
- $source = $this->getSourceRecord();
- $builder = $this->getAssociationTableCommandBuilder();
- for($i=0;$i<$total;$i++)
- $success = $fkObjects[$i]->save() && $success;
- return $this->updateAssociationTable($obj, $fkObjects, $builder) && $success;
- }
- return $success;
- }
-
- /**
- * @return TDbCommandBuilder
- */
- protected function getAssociationTableCommandBuilder()
- {
- $conn = $this->getContext()->getSourceRecord()->getDbConnection();
- return $this->getAssociationTable()->createCommandBuilder($conn);
- }
-
- private function hasAssociationData($builder,$data)
- {
- $condition=array();
- $table = $this->getAssociationTable();
- foreach($data as $name=>$value)
- $condition[] = $table->getColumn($name)->getColumnName().' = ?';
- $command = $builder->createCountCommand(implode(' AND ', $condition),array_values($data));
- $result = $this->getCommandBuilder()->onExecuteCommand($command, intval($command->queryScalar()));
- return intval($result) > 0;
- }
-
- private function addAssociationData($builder,$data)
- {
- $command = $builder->createInsertCommand($data);
- return $this->getCommandBuilder()->onExecuteCommand($command, $command->execute()) > 0;
- }
-
- private function updateAssociationTable($obj,$fkObjects, $builder)
- {
- $source = $this->getSourceRecordValues($obj);
- $foreignKeys = $this->findForeignKeys($this->getAssociationTable(), $fkObjects[0]);
- $success=true;
- foreach($fkObjects as $fkObject)
- {
- $data = array_merge($source, $this->getForeignObjectValues($foreignKeys,$fkObject));
- if(!$this->hasAssociationData($builder,$data))
- $success = $this->addAssociationData($builder,$data) && $success;
- }
- return $success;
- }
-
- private function getSourceRecordValues($obj)
- {
- $sourceKeys = $this->findForeignKeys($this->getAssociationTable(), $obj);
- $indexValues = $this->getIndexValues(array_values($sourceKeys), $obj);
- $data = array();
- $i=0;
- foreach($sourceKeys as $name=>$srcKey)
- $data[$name] = $indexValues[0][$i++];
- return $data;
- }
-
- private function getForeignObjectValues($foreignKeys,$fkObject)
- {
- $data=array();
- foreach($foreignKeys as $name=>$fKey)
- $data[$name] = $fkObject->getColumnValue($fKey);
- return $data;
- }
-}
+ * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.ActiveRecord.Relations + */ + +/** + * Loads base active record relations class. + */ +Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordRelation'); + +/** + * Implements the M-N (many to many) relationship via association table. + * Consider the <b>entity</b> relationship between Articles and Categories + * via the association table <tt>Article_Category</tt>. + * <code> + * +---------+ +------------------+ +----------+ + * | Article | * -----> * | Article_Category | * <----- * | Category | + * +---------+ +------------------+ +----------+ + * </code> + * Where one article may have 0 or more categories and each category may have 0 + * or more articles. We may model Article-Category <b>object</b> relationship + * as active record as follows. + * <code> + * class ArticleRecord + * { + * const TABLE='Article'; + * public $article_id; + * + * public $Categories=array(); //foreign object collection. + * + * public static $RELATIONS = array + * ( + * 'Categories' => array(self::MANY_TO_MANY, 'CategoryRecord', 'Article_Category') + * ); + * + * public static function finder($className=__CLASS__) + * { + * return parent::finder($className); + * } + * } + * class CategoryRecord + * { + * const TABLE='Category'; + * public $category_id; + * + * public $Articles=array(); + * + * public static $RELATIONS = array + * ( + * 'Articles' => array(self::MANY_TO_MANY, 'ArticleRecord', 'Article_Category') + * ); + * + * public static function finder($className=__CLASS__) + * { + * return parent::finder($className); + * } + * } + * </code> + * + * The static <tt>$RELATIONS</tt> property of ArticleRecord defines that the + * property <tt>$Categories</tt> has many <tt>CategoryRecord</tt>s. Similar, the + * static <tt>$RELATIONS</tt> property of CategoryRecord defines many ArticleRecords. + * + * The articles with categories list may be fetched as follows. + * <code> + * $articles = TeamRecord::finder()->withCategories()->findAll(); + * </code> + * The method <tt>with_xxx()</tt> (where <tt>xxx</tt> is the relationship property + * name, in this case, <tt>Categories</tt>) fetchs the corresponding CategoryRecords using + * a second query (not by using a join). The <tt>with_xxx()</tt> accepts the same + * arguments as other finder methods of TActiveRecord. + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.ActiveRecord.Relations + * @since 3.1 + */ +class TActiveRecordHasManyAssociation extends TActiveRecordRelation +{ + private $_association; + private $_sourceTable; + private $_foreignTable; + private $_association_columns=array(); + + /** + * Get the foreign key index values from the results and make calls to the + * database to find the corresponding foreign objects using association table. + * @param array original results. + */ + protected function collectForeignObjects(&$results) + { + list($sourceKeys, $foreignKeys) = $this->getRelationForeignKeys(); + $properties = array_values($sourceKeys); + $indexValues = $this->getIndexValues($properties, $results); + $this->fetchForeignObjects($results, $foreignKeys,$indexValues,$sourceKeys); + } + + /** + * @return array 2 arrays of source keys and foreign keys from the association table. + */ + public function getRelationForeignKeys() + { + $association = $this->getAssociationTable(); + $sourceKeys = $this->findForeignKeys($association, $this->getSourceRecord(), true); + $fkObject = $this->getContext()->getForeignRecordFinder(); + $foreignKeys = $this->findForeignKeys($association, $fkObject); + return array($sourceKeys, $foreignKeys); + } + + /** + * @return TDbTableInfo association table information. + */ + protected function getAssociationTable() + { + if($this->_association===null) + { + $gateway = $this->getSourceRecord()->getRecordGateway(); + $conn = $this->getSourceRecord()->getDbConnection(); + //table name may include the fk column name separated with a dot. + $table = explode('.', $this->getContext()->getAssociationTable()); + if(count($table)>1) + { + $columns = preg_replace('/^\((.*)\)/', '\1', $table[1]); + $this->_association_columns = preg_split('/\s*[, ]\*/',$columns); + } + $this->_association = $gateway->getTableInfo($conn, $table[0]); + } + return $this->_association; + } + + /** + * @return TDbTableInfo source table information. + */ + protected function getSourceTable() + { + if($this->_sourceTable===null) + { + $gateway = $this->getSourceRecord()->getRecordGateway(); + $this->_sourceTable = $gateway->getRecordTableInfo($this->getSourceRecord()); + } + return $this->_sourceTable; + } + + /** + * @return TDbTableInfo foreign table information. + */ + protected function getForeignTable() + { + if($this->_foreignTable===null) + { + $gateway = $this->getSourceRecord()->getRecordGateway(); + $fkObject = $this->getContext()->getForeignRecordFinder(); + $this->_foreignTable = $gateway->getRecordTableInfo($fkObject); + } + return $this->_foreignTable; + } + + /** + * @return TDataGatewayCommand + */ + protected function getCommandBuilder() + { + return $this->getSourceRecord()->getRecordGateway()->getCommand($this->getSourceRecord()); + } + + /** + * @return TDataGatewayCommand + */ + protected function getForeignCommandBuilder() + { + $obj = $this->getContext()->getForeignRecordFinder(); + return $this->getSourceRecord()->getRecordGateway()->getCommand($obj); + } + + + /** + * Fetches the foreign objects using TActiveRecord::findAllByIndex() + * @param array field names + * @param array foreign key index values. + */ + protected function fetchForeignObjects(&$results,$foreignKeys,$indexValues,$sourceKeys) + { + $criteria = $this->getCriteria(); + $finder = $this->getContext()->getForeignRecordFinder(); + $type = get_class($finder); + $command = $this->createCommand($criteria, $foreignKeys,$indexValues,$sourceKeys); + $srcProps = array_keys($sourceKeys); + $collections=array(); + foreach($this->getCommandBuilder()->onExecuteCommand($command, $command->query()) as $row) + { + $hash = $this->getObjectHash($row, $srcProps); + foreach($srcProps as $column) + unset($row[$column]); + $obj = $this->createFkObject($type,$row,$foreignKeys); + $collections[$hash][] = $obj; + } + $this->setResultCollection($results, $collections, array_values($sourceKeys)); + } + + /** + * @param string active record class name. + * @param array row data + * @param array foreign key column names + * @return TActiveRecord + */ + protected function createFkObject($type,$row,$foreignKeys) + { + $obj = TActiveRecord::createRecord($type, $row); + if(count($this->_association_columns) > 0) + { + $i=0; + foreach($foreignKeys as $ref=>$fk) + $obj->setColumnValue($ref, $row[$this->_association_columns[$i++]]); + } + return $obj; + } + + /** + * @param TSqlCriteria + * @param TTableInfo association table info + * @param array field names + * @param array field values + */ + public function createCommand($criteria, $foreignKeys,$indexValues,$sourceKeys) + { + $innerJoin = $this->getAssociationJoin($foreignKeys,$indexValues,$sourceKeys); + $fkTable = $this->getForeignTable()->getTableFullName(); + $srcColumns = $this->getSourceColumns($sourceKeys); + if(($where=$criteria->getCondition())===null) + $where='1=1'; + $sql = "SELECT {$fkTable}.*, {$srcColumns} FROM {$fkTable} {$innerJoin} WHERE {$where}"; + + $parameters = $criteria->getParameters()->toArray(); + $ordering = $criteria->getOrdersBy(); + $limit = $criteria->getLimit(); + $offset = $criteria->getOffset(); + + $builder = $this->getForeignCommandBuilder()->getBuilder(); + $command = $builder->applyCriterias($sql,$parameters,$ordering,$limit,$offset); + $this->getCommandBuilder()->onCreateCommand($command, $criteria); + return $command; + } + + /** + * @param array source table column names. + * @return string comma separated source column names. + */ + protected function getSourceColumns($sourceKeys) + { + $columns=array(); + $table = $this->getAssociationTable(); + $tableName = $table->getTableFullName(); + $columnNames = array_merge(array_keys($sourceKeys),$this->_association_columns); + foreach($columnNames as $name) + $columns[] = $tableName.'.'.$table->getColumn($name)->getColumnName(); + return implode(', ', $columns); + } + + /** + * SQL inner join for M-N relationship via association table. + * @param array foreign table column key names. + * @param array source table index values. + * @param array source table column names. + * @return string inner join condition for M-N relationship via association table. + */ + protected function getAssociationJoin($foreignKeys,$indexValues,$sourceKeys) + { + $refInfo= $this->getAssociationTable(); + $fkInfo = $this->getForeignTable(); + + $refTable = $refInfo->getTableFullName(); + $fkTable = $fkInfo->getTableFullName(); + + $joins = array(); + $hasAssociationColumns = count($this->_association_columns) > 0; + $i=0; + foreach($foreignKeys as $ref=>$fk) + { + if($hasAssociationColumns) + $refField = $refInfo->getColumn($this->_association_columns[$i++])->getColumnName(); + else + $refField = $refInfo->getColumn($ref)->getColumnName(); + $fkField = $fkInfo->getColumn($fk)->getColumnName(); + $joins[] = "{$fkTable}.{$fkField} = {$refTable}.{$refField}"; + } + $joinCondition = implode(' AND ', $joins); + $index = $this->getCommandBuilder()->getIndexKeyCondition($refInfo,array_keys($sourceKeys), $indexValues); + return "INNER JOIN {$refTable} ON ({$joinCondition}) AND {$index}"; + } + + /** + * Updates the associated foreign objects. + * @return boolean true if all update are success (including if no update was required), false otherwise . + */ + public function updateAssociatedRecords() + { + $obj = $this->getContext()->getSourceRecord(); + $fkObjects = &$obj->{$this->getContext()->getProperty()}; + $success=true; + if(($total = count($fkObjects))> 0) + { + $source = $this->getSourceRecord(); + $builder = $this->getAssociationTableCommandBuilder(); + for($i=0;$i<$total;$i++) + $success = $fkObjects[$i]->save() && $success; + return $this->updateAssociationTable($obj, $fkObjects, $builder) && $success; + } + return $success; + } + + /** + * @return TDbCommandBuilder + */ + protected function getAssociationTableCommandBuilder() + { + $conn = $this->getContext()->getSourceRecord()->getDbConnection(); + return $this->getAssociationTable()->createCommandBuilder($conn); + } + + private function hasAssociationData($builder,$data) + { + $condition=array(); + $table = $this->getAssociationTable(); + foreach($data as $name=>$value) + $condition[] = $table->getColumn($name)->getColumnName().' = ?'; + $command = $builder->createCountCommand(implode(' AND ', $condition),array_values($data)); + $result = $this->getCommandBuilder()->onExecuteCommand($command, intval($command->queryScalar())); + return intval($result) > 0; + } + + private function addAssociationData($builder,$data) + { + $command = $builder->createInsertCommand($data); + return $this->getCommandBuilder()->onExecuteCommand($command, $command->execute()) > 0; + } + + private function updateAssociationTable($obj,$fkObjects, $builder) + { + $source = $this->getSourceRecordValues($obj); + $foreignKeys = $this->findForeignKeys($this->getAssociationTable(), $fkObjects[0]); + $success=true; + foreach($fkObjects as $fkObject) + { + $data = array_merge($source, $this->getForeignObjectValues($foreignKeys,$fkObject)); + if(!$this->hasAssociationData($builder,$data)) + $success = $this->addAssociationData($builder,$data) && $success; + } + return $success; + } + + private function getSourceRecordValues($obj) + { + $sourceKeys = $this->findForeignKeys($this->getAssociationTable(), $obj); + $indexValues = $this->getIndexValues(array_values($sourceKeys), $obj); + $data = array(); + $i=0; + foreach($sourceKeys as $name=>$srcKey) + $data[$name] = $indexValues[0][$i++]; + return $data; + } + + private function getForeignObjectValues($foreignKeys,$fkObject) + { + $data=array(); + foreach($foreignKeys as $name=>$fKey) + $data[$name] = $fkObject->getColumnValue($fKey); + return $data; + } +} diff --git a/framework/Data/ActiveRecord/Relations/TActiveRecordHasOne.php b/framework/Data/ActiveRecord/Relations/TActiveRecordHasOne.php index bc895901..a762e196 100644 --- a/framework/Data/ActiveRecord/Relations/TActiveRecordHasOne.php +++ b/framework/Data/ActiveRecord/Relations/TActiveRecordHasOne.php @@ -1,145 +1,145 @@ -<?php
-/**
- * TActiveRecordHasOne class file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
+<?php +/** + * TActiveRecordHasOne class file. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.ActiveRecord.Relations
- */
-
-/**
- * Loads base active record relationship class.
- */
-Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordRelation');
-
-/**
- * TActiveRecordHasOne models the object relationship that a record (the source object)
- * property is an instance of foreign record object having a foreign key
- * related to the source object. The HAS_ONE relation is very similar to the
- * HAS_MANY relationship (in fact, it is equivalent in the entities relationship point of view).
- *
- * The difference of HAS_ONE from HAS_MANY is that the foreign object is singular.
- * That is, HAS_MANY will return a collection of records while HAS_ONE returns the
- * corresponding record.
- *
- * Consider the <b>entity</b> relationship between a Car and a Engine.
- * <code>
- * +-----+ +--------+
- * | Car | 1 <----- 1 | Engine |
- * +-----+ +--------+
- * </code>
- * Where each engine belongs to only one car, that is, the Engine entity has
- * a foreign key to the Car's primary key. We may model
- * Engine-Car <b>object</b> relationship as active record as follows.
- * <code>
- * class CarRecord extends TActiveRecord
- * {
- * const TABLE='car';
- * public $car_id; //primary key
- * public $colour;
- *
- * public $engine; //engine foreign object
- *
- * public static $RELATIONS=array
- * (
- * 'engine' => array(self::HAS_ONE, 'EngineRecord')
- * );
- *
- * public static function finder($className=__CLASS__)
- * {
- * return parent::finder($className);
- * }
- * }
- * class EngineRecord extends TActiveRecord
- * {
- * const TABLE='engine';
- * public $engine_id;
- * public $capacity;
- * public $car_id; //foreign key to cars
- *
- * public static function finder($className=__CLASS__)
- * {
- * return parent::finder($className);
- * }
- * }
- * </code>
- * The static <tt>$RELATIONS</tt> property of CarRecord defines that the
- * property <tt>$engine</tt> that will reference an <tt>EngineRecord</tt> instance.
- *
- * The car record with engine property list may be fetched as follows.
- * <code>
- * $cars = CarRecord::finder()->with_engine()->findAll();
- * </code>
- * The method <tt>with_xxx()</tt> (where <tt>xxx</tt> is the relationship property
- * name, in this case, <tt>engine</tt>) fetchs the corresponding EngineRecords using
- * a second query (not by using a join). The <tt>with_xxx()</tt> accepts the same
- * arguments as other finder methods of TActiveRecord, e.g. <tt>with_engine('capacity < ?', 3.8)</tt>.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.ActiveRecord.Relations
- * @since 3.1
- */
-class TActiveRecordHasOne extends TActiveRecordRelation
-{
- /**
- * Get the foreign key index values from the results and make calls to the
- * database to find the corresponding foreign objects.
- * @param array original results.
- */
- protected function collectForeignObjects(&$results)
- {
- $fkeys = $this->getRelationForeignKeys();
- $properties = array_values($fkeys);
- $fields = array_keys($fkeys);
-
- $indexValues = $this->getIndexValues($properties, $results);
- $fkObjects = $this->findForeignObjects($fields,$indexValues);
- $this->populateResult($results,$properties,$fkObjects,$fields);
- }
-
- /**
- * @return array foreign key field names as key and object properties as value.
- * @since 3.1.2
- */
- public function getRelationForeignKeys()
- {
- $fkObject = $this->getContext()->getForeignRecordFinder();
- return $this->findForeignKeys($fkObject, $this->getSourceRecord());
- }
-
- /**
- * Sets the foreign objects to the given property on the source object.
- * @param TActiveRecord source object.
- * @param array foreign objects.
- */
- protected function setObjectProperty($source, $properties, &$collections)
- {
- $hash = $this->getObjectHash($source, $properties);
- $prop = $this->getContext()->getProperty();
- if(isset($collections[$hash]) && count($collections[$hash]) > 0)
- {
- if(count($collections[$hash]) > 1)
- throw new TActiveRecordException('ar_belongs_to_multiple_result');
- $source->setColumnValue($prop, $collections[$hash][0]);
- }
- }
-
- /**
- * Updates the associated foreign objects.
- * @return boolean true if all update are success (including if no update was required), false otherwise .
- */
- public function updateAssociatedRecords()
- {
- $fkObject = $this->getContext()->getPropertyValue();
- $source = $this->getSourceRecord();
- $fkeys = $this->findForeignKeys($fkObject, $source);
- foreach($fkeys as $fKey => $srcKey)
- $fkObject->setColumnValue($fKey, $source->getColumnValue($srcKey));
- return $fkObject->save();
- }
-}
-
+ * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.ActiveRecord.Relations + */ + +/** + * Loads base active record relationship class. + */ +Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordRelation'); + +/** + * TActiveRecordHasOne models the object relationship that a record (the source object) + * property is an instance of foreign record object having a foreign key + * related to the source object. The HAS_ONE relation is very similar to the + * HAS_MANY relationship (in fact, it is equivalent in the entities relationship point of view). + * + * The difference of HAS_ONE from HAS_MANY is that the foreign object is singular. + * That is, HAS_MANY will return a collection of records while HAS_ONE returns the + * corresponding record. + * + * Consider the <b>entity</b> relationship between a Car and a Engine. + * <code> + * +-----+ +--------+ + * | Car | 1 <----- 1 | Engine | + * +-----+ +--------+ + * </code> + * Where each engine belongs to only one car, that is, the Engine entity has + * a foreign key to the Car's primary key. We may model + * Engine-Car <b>object</b> relationship as active record as follows. + * <code> + * class CarRecord extends TActiveRecord + * { + * const TABLE='car'; + * public $car_id; //primary key + * public $colour; + * + * public $engine; //engine foreign object + * + * public static $RELATIONS=array + * ( + * 'engine' => array(self::HAS_ONE, 'EngineRecord') + * ); + * + * public static function finder($className=__CLASS__) + * { + * return parent::finder($className); + * } + * } + * class EngineRecord extends TActiveRecord + * { + * const TABLE='engine'; + * public $engine_id; + * public $capacity; + * public $car_id; //foreign key to cars + * + * public static function finder($className=__CLASS__) + * { + * return parent::finder($className); + * } + * } + * </code> + * The static <tt>$RELATIONS</tt> property of CarRecord defines that the + * property <tt>$engine</tt> that will reference an <tt>EngineRecord</tt> instance. + * + * The car record with engine property list may be fetched as follows. + * <code> + * $cars = CarRecord::finder()->with_engine()->findAll(); + * </code> + * The method <tt>with_xxx()</tt> (where <tt>xxx</tt> is the relationship property + * name, in this case, <tt>engine</tt>) fetchs the corresponding EngineRecords using + * a second query (not by using a join). The <tt>with_xxx()</tt> accepts the same + * arguments as other finder methods of TActiveRecord, e.g. <tt>with_engine('capacity < ?', 3.8)</tt>. + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.ActiveRecord.Relations + * @since 3.1 + */ +class TActiveRecordHasOne extends TActiveRecordRelation +{ + /** + * Get the foreign key index values from the results and make calls to the + * database to find the corresponding foreign objects. + * @param array original results. + */ + protected function collectForeignObjects(&$results) + { + $fkeys = $this->getRelationForeignKeys(); + $properties = array_values($fkeys); + $fields = array_keys($fkeys); + + $indexValues = $this->getIndexValues($properties, $results); + $fkObjects = $this->findForeignObjects($fields,$indexValues); + $this->populateResult($results,$properties,$fkObjects,$fields); + } + + /** + * @return array foreign key field names as key and object properties as value. + * @since 3.1.2 + */ + public function getRelationForeignKeys() + { + $fkObject = $this->getContext()->getForeignRecordFinder(); + return $this->findForeignKeys($fkObject, $this->getSourceRecord()); + } + + /** + * Sets the foreign objects to the given property on the source object. + * @param TActiveRecord source object. + * @param array foreign objects. + */ + protected function setObjectProperty($source, $properties, &$collections) + { + $hash = $this->getObjectHash($source, $properties); + $prop = $this->getContext()->getProperty(); + if(isset($collections[$hash]) && count($collections[$hash]) > 0) + { + if(count($collections[$hash]) > 1) + throw new TActiveRecordException('ar_belongs_to_multiple_result'); + $source->setColumnValue($prop, $collections[$hash][0]); + } + } + + /** + * Updates the associated foreign objects. + * @return boolean true if all update are success (including if no update was required), false otherwise . + */ + public function updateAssociatedRecords() + { + $fkObject = $this->getContext()->getPropertyValue(); + $source = $this->getSourceRecord(); + $fkeys = $this->findForeignKeys($fkObject, $source); + foreach($fkeys as $fKey => $srcKey) + $fkObject->setColumnValue($fKey, $source->getColumnValue($srcKey)); + return $fkObject->save(); + } +} + diff --git a/framework/Data/ActiveRecord/Relations/TActiveRecordRelation.php b/framework/Data/ActiveRecord/Relations/TActiveRecordRelation.php index 39482635..c59532d9 100644 --- a/framework/Data/ActiveRecord/Relations/TActiveRecordRelation.php +++ b/framework/Data/ActiveRecord/Relations/TActiveRecordRelation.php @@ -1,254 +1,254 @@ -<?php
-/**
- * TActiveRecordRelation class file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.ActiveRecord.Relations
- */
-
-/**
- * Load active record relationship context.
- */
-Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordRelationContext');
-
-/**
- * Base class for active record relationships.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.ActiveRecord.Relations
- * @since 3.1
- */
-abstract class TActiveRecordRelation
-{
- private $_context;
- private $_criteria;
-
- public function __construct(TActiveRecordRelationContext $context, $criteria)
- {
- $this->_context = $context;
- $this->_criteria = $criteria;
- }
-
- /**
- * @return TActiveRecordRelationContext
- */
- protected function getContext()
- {
- return $this->_context;
- }
-
- /**
- * @return TActiveRecordCriteria
- */
- protected function getCriteria()
- {
- return $this->_criteria;
- }
-
- /**
- * @return TActiveRecord
- */
- protected function getSourceRecord()
- {
- return $this->getContext()->getSourceRecord();
- }
-
- abstract protected function collectForeignObjects(&$results);
-
- /**
- * Dispatch the method calls to the source record finder object. When
- * an instance of TActiveRecord or an array of TActiveRecord is returned
- * the corresponding foreign objects are also fetched and assigned.
- *
- * Multiple relationship calls can be chain together.
- *
- * @param string method name called
- * @param array method arguments
- * @return mixed TActiveRecord or array of TActiveRecord results depending on the method called.
- */
- public function __call($method,$args)
- {
- static $stack=array();
-
- $results = call_user_func_array(array($this->getSourceRecord(),$method),$args);
- $validArray = is_array($results) && count($results) > 0;
- if($validArray || $results instanceof ArrayAccess || $results instanceof TActiveRecord)
- {
- $this->collectForeignObjects($results);
- while($obj = array_pop($stack))
- $obj->collectForeignObjects($results);
- }
- else if($results instanceof TActiveRecordRelation)
- $stack[] = $this; //call it later
- else if($results === null || !$validArray)
- $stack = array();
- return $results;
- }
-
- /**
- * Fetch results for current relationship.
- * @return boolean always true.
- */
- public function fetchResultsInto($obj)
- {
- $this->collectForeignObjects($obj);
- return true;
- }
-
- /**
- * Returns foreign keys in $fromRecord with source column names as key
- * and foreign column names in the corresponding $matchesRecord as value.
- * The method returns the first matching foreign key between these 2 records.
- * @param TActiveRecord $fromRecord
- * @param TActiveRecord $matchesRecord
- * @return array foreign keys with source column names as key and foreign column names as value.
- */
- protected function findForeignKeys($from, $matchesRecord, $loose=false)
- {
- $gateway = $matchesRecord->getRecordGateway();
- $recordTableInfo = $gateway->getRecordTableInfo($matchesRecord);
- $matchingTableName = strtolower($recordTableInfo->getTableName());
- $matchingFullTableName = strtolower($recordTableInfo->getTableFullName());
- $tableInfo=$from;
- if($from instanceof TActiveRecord)
- $tableInfo = $gateway->getRecordTableInfo($from);
- //find first non-empty FK
- foreach($tableInfo->getForeignKeys() as $fkeys)
- {
- $fkTable = strtolower($fkeys['table']);
- if($fkTable===$matchingTableName || $fkTable===$matchingFullTableName)
- {
- $hasFkField = !$loose && $this->getContext()->hasFkField();
- $key = $hasFkField ? $this->getFkFields($fkeys['keys']) : $fkeys['keys'];
- if(!empty($key))
- return $key;
- }
- }
-
- //none found
- $matching = $gateway->getRecordTableInfo($matchesRecord)->getTableFullName();
- throw new TActiveRecordException('ar_relations_missing_fk',
- $tableInfo->getTableFullName(), $matching);
- }
-
- /**
- * @return array foreign key field names as key and object properties as value.
- * @since 3.1.2
- */
- abstract public function getRelationForeignKeys();
-
- /**
- * Find matching foreign key fields from the 3rd element of an entry in TActiveRecord::$RELATION.
- * Assume field names consist of [\w-] character sets. Prefix to the field names ending with a dot
- * are ignored.
- */
- private function getFkFields($fkeys)
- {
- $matching = array();
- preg_match_all('/\s*(\S+\.)?([\w-]+)\s*/', $this->getContext()->getFkField(), $matching);
- $fields = array();
- foreach($fkeys as $fkName => $field)
- {
- if(in_array($fkName, $matching[2]))
- $fields[$fkName] = $field;
- }
- return $fields;
- }
-
- /**
- * @param mixed object or array to be hashed
- * @param array name of property for hashing the properties.
- * @return string object hash using crc32 and serialize.
- */
- protected function getObjectHash($obj, $properties)
- {
- $ids=array();
- foreach($properties as $property)
- $ids[] = is_object($obj) ? (string)$obj->getColumnValue($property) : (string)$obj[$property];
- return serialize($ids);
- }
-
- /**
- * Fetches the foreign objects using TActiveRecord::findAllByIndex()
- * @param array field names
- * @param array foreign key index values.
- * @return TActiveRecord[] foreign objects.
- */
- protected function findForeignObjects($fields, $indexValues)
- {
- $finder = $this->getContext()->getForeignRecordFinder();
- return $finder->findAllByIndex($this->_criteria, $fields, $indexValues);
- }
-
- /**
- * Obtain the foreign key index values from the results.
- * @param array property names
- * @param array TActiveRecord results
- * @return array foreign key index values.
- */
- protected function getIndexValues($keys, $results)
- {
- if(!is_array($results) && !$results instanceof ArrayAccess)
- $results = array($results);
- $values=array();
- foreach($results as $result)
- {
- $value = array();
- foreach($keys as $name)
- $value[] = $result->getColumnValue($name);
- $values[] = $value;
- }
- return $values;
- }
-
- /**
- * Populate the results with the foreign objects found.
- * @param array source results
- * @param array source property names
- * @param array foreign objects
- * @param array foreign object field names.
- */
- protected function populateResult(&$results,$properties,&$fkObjects,$fields)
- {
- $collections=array();
- foreach($fkObjects as $fkObject)
- $collections[$this->getObjectHash($fkObject, $fields)][]=$fkObject;
- $this->setResultCollection($results, $collections, $properties);
- }
-
- /**
- * Populates the result array with foreign objects (matched using foreign key hashed property values).
- * @param array $results
- * @param array $collections
- * @param array property names
- */
- protected function setResultCollection(&$results, &$collections, $properties)
- {
- if(is_array($results) || $results instanceof ArrayAccess)
- {
- for($i=0,$k=count($results);$i<$k;$i++)
- $this->setObjectProperty($results[$i], $properties, $collections);
- }
- else
- $this->setObjectProperty($results, $properties, $collections);
- }
-
- /**
- * Sets the foreign objects to the given property on the source object.
- * @param TActiveRecord source object.
- * @param array source properties
- * @param array foreign objects.
- */
- protected function setObjectProperty($source, $properties, &$collections)
- {
- $hash = $this->getObjectHash($source, $properties);
- $prop = $this->getContext()->getProperty();
- $source->$prop=isset($collections[$hash]) ? $collections[$hash] : array();
- }
-}
-
+<?php +/** + * TActiveRecordRelation class file. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.ActiveRecord.Relations + */ + +/** + * Load active record relationship context. + */ +Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordRelationContext'); + +/** + * Base class for active record relationships. + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.ActiveRecord.Relations + * @since 3.1 + */ +abstract class TActiveRecordRelation +{ + private $_context; + private $_criteria; + + public function __construct(TActiveRecordRelationContext $context, $criteria) + { + $this->_context = $context; + $this->_criteria = $criteria; + } + + /** + * @return TActiveRecordRelationContext + */ + protected function getContext() + { + return $this->_context; + } + + /** + * @return TActiveRecordCriteria + */ + protected function getCriteria() + { + return $this->_criteria; + } + + /** + * @return TActiveRecord + */ + protected function getSourceRecord() + { + return $this->getContext()->getSourceRecord(); + } + + abstract protected function collectForeignObjects(&$results); + + /** + * Dispatch the method calls to the source record finder object. When + * an instance of TActiveRecord or an array of TActiveRecord is returned + * the corresponding foreign objects are also fetched and assigned. + * + * Multiple relationship calls can be chain together. + * + * @param string method name called + * @param array method arguments + * @return mixed TActiveRecord or array of TActiveRecord results depending on the method called. + */ + public function __call($method,$args) + { + static $stack=array(); + + $results = call_user_func_array(array($this->getSourceRecord(),$method),$args); + $validArray = is_array($results) && count($results) > 0; + if($validArray || $results instanceof ArrayAccess || $results instanceof TActiveRecord) + { + $this->collectForeignObjects($results); + while($obj = array_pop($stack)) + $obj->collectForeignObjects($results); + } + else if($results instanceof TActiveRecordRelation) + $stack[] = $this; //call it later + else if($results === null || !$validArray) + $stack = array(); + return $results; + } + + /** + * Fetch results for current relationship. + * @return boolean always true. + */ + public function fetchResultsInto($obj) + { + $this->collectForeignObjects($obj); + return true; + } + + /** + * Returns foreign keys in $fromRecord with source column names as key + * and foreign column names in the corresponding $matchesRecord as value. + * The method returns the first matching foreign key between these 2 records. + * @param TActiveRecord $fromRecord + * @param TActiveRecord $matchesRecord + * @return array foreign keys with source column names as key and foreign column names as value. + */ + protected function findForeignKeys($from, $matchesRecord, $loose=false) + { + $gateway = $matchesRecord->getRecordGateway(); + $recordTableInfo = $gateway->getRecordTableInfo($matchesRecord); + $matchingTableName = strtolower($recordTableInfo->getTableName()); + $matchingFullTableName = strtolower($recordTableInfo->getTableFullName()); + $tableInfo=$from; + if($from instanceof TActiveRecord) + $tableInfo = $gateway->getRecordTableInfo($from); + //find first non-empty FK + foreach($tableInfo->getForeignKeys() as $fkeys) + { + $fkTable = strtolower($fkeys['table']); + if($fkTable===$matchingTableName || $fkTable===$matchingFullTableName) + { + $hasFkField = !$loose && $this->getContext()->hasFkField(); + $key = $hasFkField ? $this->getFkFields($fkeys['keys']) : $fkeys['keys']; + if(!empty($key)) + return $key; + } + } + + //none found + $matching = $gateway->getRecordTableInfo($matchesRecord)->getTableFullName(); + throw new TActiveRecordException('ar_relations_missing_fk', + $tableInfo->getTableFullName(), $matching); + } + + /** + * @return array foreign key field names as key and object properties as value. + * @since 3.1.2 + */ + abstract public function getRelationForeignKeys(); + + /** + * Find matching foreign key fields from the 3rd element of an entry in TActiveRecord::$RELATION. + * Assume field names consist of [\w-] character sets. Prefix to the field names ending with a dot + * are ignored. + */ + private function getFkFields($fkeys) + { + $matching = array(); + preg_match_all('/\s*(\S+\.)?([\w-]+)\s*/', $this->getContext()->getFkField(), $matching); + $fields = array(); + foreach($fkeys as $fkName => $field) + { + if(in_array($fkName, $matching[2])) + $fields[$fkName] = $field; + } + return $fields; + } + + /** + * @param mixed object or array to be hashed + * @param array name of property for hashing the properties. + * @return string object hash using crc32 and serialize. + */ + protected function getObjectHash($obj, $properties) + { + $ids=array(); + foreach($properties as $property) + $ids[] = is_object($obj) ? (string)$obj->getColumnValue($property) : (string)$obj[$property]; + return serialize($ids); + } + + /** + * Fetches the foreign objects using TActiveRecord::findAllByIndex() + * @param array field names + * @param array foreign key index values. + * @return TActiveRecord[] foreign objects. + */ + protected function findForeignObjects($fields, $indexValues) + { + $finder = $this->getContext()->getForeignRecordFinder(); + return $finder->findAllByIndex($this->_criteria, $fields, $indexValues); + } + + /** + * Obtain the foreign key index values from the results. + * @param array property names + * @param array TActiveRecord results + * @return array foreign key index values. + */ + protected function getIndexValues($keys, $results) + { + if(!is_array($results) && !$results instanceof ArrayAccess) + $results = array($results); + $values=array(); + foreach($results as $result) + { + $value = array(); + foreach($keys as $name) + $value[] = $result->getColumnValue($name); + $values[] = $value; + } + return $values; + } + + /** + * Populate the results with the foreign objects found. + * @param array source results + * @param array source property names + * @param array foreign objects + * @param array foreign object field names. + */ + protected function populateResult(&$results,$properties,&$fkObjects,$fields) + { + $collections=array(); + foreach($fkObjects as $fkObject) + $collections[$this->getObjectHash($fkObject, $fields)][]=$fkObject; + $this->setResultCollection($results, $collections, $properties); + } + + /** + * Populates the result array with foreign objects (matched using foreign key hashed property values). + * @param array $results + * @param array $collections + * @param array property names + */ + protected function setResultCollection(&$results, &$collections, $properties) + { + if(is_array($results) || $results instanceof ArrayAccess) + { + for($i=0,$k=count($results);$i<$k;$i++) + $this->setObjectProperty($results[$i], $properties, $collections); + } + else + $this->setObjectProperty($results, $properties, $collections); + } + + /** + * Sets the foreign objects to the given property on the source object. + * @param TActiveRecord source object. + * @param array source properties + * @param array foreign objects. + */ + protected function setObjectProperty($source, $properties, &$collections) + { + $hash = $this->getObjectHash($source, $properties); + $prop = $this->getContext()->getProperty(); + $source->$prop=isset($collections[$hash]) ? $collections[$hash] : array(); + } +} + diff --git a/framework/Data/ActiveRecord/Relations/TActiveRecordRelationContext.php b/framework/Data/ActiveRecord/Relations/TActiveRecordRelationContext.php index 8826bb69..dbcc097b 100644 --- a/framework/Data/ActiveRecord/Relations/TActiveRecordRelationContext.php +++ b/framework/Data/ActiveRecord/Relations/TActiveRecordRelationContext.php @@ -1,230 +1,230 @@ -<?php
-/**
- * TActiveRecordRelationContext class.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
+<?php +/** + * TActiveRecordRelationContext class. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.ActiveRecord.Relations
- */
-
-/**
- * TActiveRecordRelationContext holds information regarding record relationships
- * such as record relation property name, query criteria and foreign object record
- * class names.
- *
- * This class is use internally by passing a context to the TActiveRecordRelation
- * constructor.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.ActiveRecord.Relations
- * @since 3.1
- */
-class TActiveRecordRelationContext
-{
- private $_property;
- private $_record;
- private $_relation; //data from an entry of TActiveRecord::$RELATION
- private $_fkeys;
-
- public function __construct($record, $property=null, $relation=null)
- {
- $this->_record=$record;
- $this->_property=$property;
- $this->_relation=$relation;
- }
-
- /**
- * @return boolean true if the relation is defined in TActiveRecord::$RELATIONS
- * @since 3.1.2
- */
- public function hasRecordRelation()
- {
- return $this->_relation!==null;
- }
-
- public function getPropertyValue()
- {
- $obj = $this->getSourceRecord();
- return $obj->getColumnValue($this->getProperty());
- }
-
- /**
- * @return string name of the record property that the relationship results will be assigned to.
- */
- public function getProperty()
- {
- return $this->_property;
- }
-
- /**
- * @return TActiveRecord the active record instance that queried for its related records.
- */
- public function getSourceRecord()
- {
- return $this->_record;
- }
-
- /**
- * @return array foreign key of this relations, the keys is dependent on the
- * relationship type.
- * @since 3.1.2
- */
- public function getRelationForeignKeys()
- {
- if($this->_fkeys===null)
- $this->_fkeys=$this->getRelationHandler()->getRelationForeignKeys();
- return $this->_fkeys;
- }
-
- /**
- * @return string HAS_MANY, HAS_ONE, or BELONGS_TO
- */
- public function getRelationType()
- {
- return $this->_relation[0];
- }
-
- /**
- * @return string foreign record class name.
- */
- public function getForeignRecordClass()
- {
- return $this->_relation[1];
- }
-
- /**
- * @return string foreign key field names, comma delimited.
- * @since 3.1.2
- */
- public function getFkField()
- {
- return $this->_relation[2];
- }
-
- /**
- * @return string the query condition for the relation as specified in RELATIONS
- * @since 3.1.2
- */
- public function getCondition()
- {
- return isset($this->_relation[3])?$this->_relation[3]:null;
- }
-
- /**
- * @return array the query parameters for the relation as specified in RELATIONS
- * @since 3.1.2
- */
- public function getParameters()
- {
- return isset($this->_relation[4])?$this->_relation[4]:array();
- }
-
- /**
- * @return boolean true if the 3rd element of an TActiveRecord::$RELATION entry is set.
- * @since 3.1.2
- */
- public function hasFkField()
- {
- $notManyToMany = $this->getRelationType() !== TActiveRecord::MANY_TO_MANY;
- return $notManyToMany && isset($this->_relation[2]) && !empty($this->_relation[2]);
- }
-
- /**
- * @return string the M-N relationship association table name.
- */
- public function getAssociationTable()
- {
- return $this->_relation[2];
- }
-
- /**
- * @return boolean true if the relationship is HAS_MANY and requires an association table.
- */
- public function hasAssociationTable()
- {
- $isManyToMany = $this->getRelationType() === TActiveRecord::MANY_TO_MANY;
- return $isManyToMany && isset($this->_relation[2]) && !empty($this->_relation[2]);
- }
-
- /**
- * @return TActiveRecord corresponding relationship foreign object finder instance.
- */
- public function getForeignRecordFinder()
- {
- return TActiveRecord::finder($this->getForeignRecordClass());
- }
-
- /**
- * Creates and return the TActiveRecordRelation handler for specific relationships.
- * An instance of TActiveRecordHasOne, TActiveRecordBelongsTo, TActiveRecordHasMany,
- * or TActiveRecordHasManyAssocation will be returned.
- * @param TActiveRecordCriteria search criteria
- * @return TActiveRecordRelation record relationship handler instnace.
- * @throws TActiveRecordException if property is not defined or missing.
- */
- public function getRelationHandler($criteria=null)
- {
- if(!$this->hasRecordRelation())
- {
- throw new TActiveRecordException('ar_undefined_relation_prop',
- $this->_property, get_class($this->_record), 'RELATIONS');
- }
- if($criteria===null)
- $criteria = new TActiveRecordCriteria($this->getCondition(), $this->getParameters());
- switch($this->getRelationType())
- {
- case TActiveRecord::HAS_MANY:
- Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordHasMany');
- return new TActiveRecordHasMany($this, $criteria);
- case TActiveRecord::MANY_TO_MANY:
- Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordHasManyAssociation');
- return new TActiveRecordHasManyAssociation($this, $criteria);
- case TActiveRecord::HAS_ONE:
- Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordHasOne');
- return new TActiveRecordHasOne($this, $criteria);
- case TActiveRecord::BELONGS_TO:
- Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordBelongsTo');
- return new TActiveRecordBelongsTo($this, $criteria);
- default:
- throw new TActiveRecordException('ar_invalid_relationship');
- }
- }
-
- /**
- * @return TActiveRecordRelationCommand
- */
- public function updateAssociatedRecords($updateBelongsTo=false)
- {
- $success=true;
- foreach($this->_record->getRecordRelations() as $data)
- {
- list($property, $relation) = $data;
- $belongsTo = $relation[0]==TActiveRecord::BELONGS_TO;
- if(($updateBelongsTo && $belongsTo) || (!$updateBelongsTo && !$belongsTo))
- {
- $obj = $this->getSourceRecord();
- if(!$this->isEmptyFkObject($obj->getColumnValue($property)))
- {
- $context = new TActiveRecordRelationContext($this->getSourceRecord(),$property,$relation);
- $success = $context->getRelationHandler()->updateAssociatedRecords() && $success;
- }
- }
- }
- return $success;
- }
-
- protected function isEmptyFkObject($obj)
- {
- if(is_object($obj))
- return $obj instanceof TList ? $obj->count() === 0 : false;
- else if(is_array($obj))
- return count($obj)===0;
- else
- return empty($obj);
- }
-}
-
+ * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.ActiveRecord.Relations + */ + +/** + * TActiveRecordRelationContext holds information regarding record relationships + * such as record relation property name, query criteria and foreign object record + * class names. + * + * This class is use internally by passing a context to the TActiveRecordRelation + * constructor. + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.ActiveRecord.Relations + * @since 3.1 + */ +class TActiveRecordRelationContext +{ + private $_property; + private $_record; + private $_relation; //data from an entry of TActiveRecord::$RELATION + private $_fkeys; + + public function __construct($record, $property=null, $relation=null) + { + $this->_record=$record; + $this->_property=$property; + $this->_relation=$relation; + } + + /** + * @return boolean true if the relation is defined in TActiveRecord::$RELATIONS + * @since 3.1.2 + */ + public function hasRecordRelation() + { + return $this->_relation!==null; + } + + public function getPropertyValue() + { + $obj = $this->getSourceRecord(); + return $obj->getColumnValue($this->getProperty()); + } + + /** + * @return string name of the record property that the relationship results will be assigned to. + */ + public function getProperty() + { + return $this->_property; + } + + /** + * @return TActiveRecord the active record instance that queried for its related records. + */ + public function getSourceRecord() + { + return $this->_record; + } + + /** + * @return array foreign key of this relations, the keys is dependent on the + * relationship type. + * @since 3.1.2 + */ + public function getRelationForeignKeys() + { + if($this->_fkeys===null) + $this->_fkeys=$this->getRelationHandler()->getRelationForeignKeys(); + return $this->_fkeys; + } + + /** + * @return string HAS_MANY, HAS_ONE, or BELONGS_TO + */ + public function getRelationType() + { + return $this->_relation[0]; + } + + /** + * @return string foreign record class name. + */ + public function getForeignRecordClass() + { + return $this->_relation[1]; + } + + /** + * @return string foreign key field names, comma delimited. + * @since 3.1.2 + */ + public function getFkField() + { + return $this->_relation[2]; + } + + /** + * @return string the query condition for the relation as specified in RELATIONS + * @since 3.1.2 + */ + public function getCondition() + { + return isset($this->_relation[3])?$this->_relation[3]:null; + } + + /** + * @return array the query parameters for the relation as specified in RELATIONS + * @since 3.1.2 + */ + public function getParameters() + { + return isset($this->_relation[4])?$this->_relation[4]:array(); + } + + /** + * @return boolean true if the 3rd element of an TActiveRecord::$RELATION entry is set. + * @since 3.1.2 + */ + public function hasFkField() + { + $notManyToMany = $this->getRelationType() !== TActiveRecord::MANY_TO_MANY; + return $notManyToMany && isset($this->_relation[2]) && !empty($this->_relation[2]); + } + + /** + * @return string the M-N relationship association table name. + */ + public function getAssociationTable() + { + return $this->_relation[2]; + } + + /** + * @return boolean true if the relationship is HAS_MANY and requires an association table. + */ + public function hasAssociationTable() + { + $isManyToMany = $this->getRelationType() === TActiveRecord::MANY_TO_MANY; + return $isManyToMany && isset($this->_relation[2]) && !empty($this->_relation[2]); + } + + /** + * @return TActiveRecord corresponding relationship foreign object finder instance. + */ + public function getForeignRecordFinder() + { + return TActiveRecord::finder($this->getForeignRecordClass()); + } + + /** + * Creates and return the TActiveRecordRelation handler for specific relationships. + * An instance of TActiveRecordHasOne, TActiveRecordBelongsTo, TActiveRecordHasMany, + * or TActiveRecordHasManyAssocation will be returned. + * @param TActiveRecordCriteria search criteria + * @return TActiveRecordRelation record relationship handler instnace. + * @throws TActiveRecordException if property is not defined or missing. + */ + public function getRelationHandler($criteria=null) + { + if(!$this->hasRecordRelation()) + { + throw new TActiveRecordException('ar_undefined_relation_prop', + $this->_property, get_class($this->_record), 'RELATIONS'); + } + if($criteria===null) + $criteria = new TActiveRecordCriteria($this->getCondition(), $this->getParameters()); + switch($this->getRelationType()) + { + case TActiveRecord::HAS_MANY: + Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordHasMany'); + return new TActiveRecordHasMany($this, $criteria); + case TActiveRecord::MANY_TO_MANY: + Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordHasManyAssociation'); + return new TActiveRecordHasManyAssociation($this, $criteria); + case TActiveRecord::HAS_ONE: + Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordHasOne'); + return new TActiveRecordHasOne($this, $criteria); + case TActiveRecord::BELONGS_TO: + Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordBelongsTo'); + return new TActiveRecordBelongsTo($this, $criteria); + default: + throw new TActiveRecordException('ar_invalid_relationship'); + } + } + + /** + * @return TActiveRecordRelationCommand + */ + public function updateAssociatedRecords($updateBelongsTo=false) + { + $success=true; + foreach($this->_record->getRecordRelations() as $data) + { + list($property, $relation) = $data; + $belongsTo = $relation[0]==TActiveRecord::BELONGS_TO; + if(($updateBelongsTo && $belongsTo) || (!$updateBelongsTo && !$belongsTo)) + { + $obj = $this->getSourceRecord(); + if(!$this->isEmptyFkObject($obj->getColumnValue($property))) + { + $context = new TActiveRecordRelationContext($this->getSourceRecord(),$property,$relation); + $success = $context->getRelationHandler()->updateAssociatedRecords() && $success; + } + } + } + return $success; + } + + protected function isEmptyFkObject($obj) + { + if(is_object($obj)) + return $obj instanceof TList ? $obj->count() === 0 : false; + else if(is_array($obj)) + return count($obj)===0; + else + return empty($obj); + } +} + diff --git a/framework/Data/ActiveRecord/Scaffold/TScaffoldBase.php b/framework/Data/ActiveRecord/Scaffold/TScaffoldBase.php index d2e58bc3..68f5806a 100644 --- a/framework/Data/ActiveRecord/Scaffold/TScaffoldBase.php +++ b/framework/Data/ActiveRecord/Scaffold/TScaffoldBase.php @@ -1,207 +1,207 @@ -<?php
-/**
- * TScaffoldBase class file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.ActiveRecord.Scaffold
- */
-
-/**
- * Include the base Active Record class.
- */
-Prado::using('System.Data.ActiveRecord.TActiveRecord');
-
-/**
- * Base class for Active Record scaffold views.
- *
- * Provides common properties for all scaffold views (such as, TScaffoldListView,
- * TScaffoldEditView, TScaffoldListView and TScaffoldView).
- *
- * During the OnPrRender stage the default css style file (filename style.css)
- * is published and registered. To override the default style, provide your own stylesheet
- * file explicitly.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.ActiveRecord.Scaffold
- * @since 3.1
- */
-abstract class TScaffoldBase extends TTemplateControl
-{
- /**
- * @var TActiveRecord record instance (may be new or retrieved from db)
- */
- private $_record;
-
- /**
- * @return TDbMetaData table/view information
- */
- protected function getTableInfo()
- {
- $finder = $this->getRecordFinder();
- $gateway = $finder->getRecordManager()->getRecordGateWay();
- return $gateway->getRecordTableInfo($finder);
- }
-
- /**
- * @param TActiveRecord record instance
- * @return array record property values
- */
- protected function getRecordPropertyValues($record)
- {
- $data = array();
- foreach($this->getTableInfo()->getColumns() as $name=>$column)
- $data[] = $record->getColumnValue($name);
- return $data;
- }
-
- /**
- * @param TActiveRecord record instance
- * @return array record primary key values.
- */
- protected function getRecordPkValues($record)
- {
- $data=array();
- foreach($this->getTableInfo()->getColumns() as $name=>$column)
- {
- if($column->getIsPrimaryKey())
- $data[] = $record->getColumnValue($name);
- }
- return $data;
- }
-
- /**
- * Name of the Active Record class to be viewed or scaffolded.
- * @return string Active Record class name.
- */
- public function getRecordClass()
- {
- return $this->getViewState('RecordClass');
- }
-
- /**
- * Name of the Active Record class to be viewed or scaffolded.
- * @param string Active Record class name.
- */
- public function setRecordClass($value)
- {
- $this->setViewState('RecordClass', $value);
- }
-
- /**
- * Copy the view details from another scaffold view instance.
- * @param TScaffoldBase scaffold view.
- */
- protected function copyFrom(TScaffoldBase $obj)
- {
- $this->_record = $obj->_record;
- $this->setRecordClass($obj->getRecordClass());
- $this->setEnableDefaultStyle($obj->getEnableDefaultStyle());
- }
-
- /**
- * Unset the current record instance and table information.
- */
- protected function clearRecordObject()
- {
- $this->_record=null;
- }
-
- /**
- * Gets the current Active Record instance. Creates new instance if the
- * primary key value is null otherwise the record is fetched from the db.
- * @param array primary key value
- * @return TActiveRecord record instance
- */
- protected function getRecordObject($pk=null)
- {
- if($this->_record===null)
- {
- if($pk!==null)
- {
- $this->_record=$this->getRecordFinder()->findByPk($pk);
- if($this->_record===null)
- throw new TConfigurationException('scaffold_invalid_record_pk',
- $this->getRecordClass(), $pk);
- }
- else
- {
- $class = $this->getRecordClass();
- if($class!==null)
- $this->_record=Prado::createComponent($class);
- else
- {
- throw new TConfigurationException('scaffold_invalid_record_class',
- $this->getRecordClass(),$this->getID());
- }
- }
- }
- return $this->_record;
- }
-
- /**
- * @param TActiveRecord Active Record instance.
- */
- protected function setRecordObject(TActiveRecord $value)
- {
- $this->_record=$value;
- }
-
- /**
- * @return TActiveRecord Active Record finder instance
- */
- protected function getRecordFinder()
- {
- return TActiveRecord::finder($this->getRecordClass());
- }
-
- /**
- * @return string default scaffold stylesheet name
- */
- public function getDefaultStyle()
- {
- return $this->getViewState('DefaultStyle', 'style');
- }
-
- /**
- * @param string default scaffold stylesheet name
- */
- public function setDefaultStyle($value)
- {
- $this->setViewState('DefaultStyle', TPropertyValue::ensureString($value), 'style');
- }
-
- /**
- * @return boolean enable default stylesheet, default is true.
- */
- public function getEnableDefaultStyle()
- {
- return $this->getViewState('EnableDefaultStyle', true);
- }
-
- /**
- * @param boolean enable default stylesheet, default is true.
- */
- public function setEnableDefaultStyle($value)
- {
- return $this->setViewState('EnableDefaultStyle', TPropertyValue::ensureBoolean($value), true);
- }
-
- /**
- * Publish the default stylesheet file.
- */
- public function onPreRender($param)
- {
- parent::onPreRender($param);
- if($this->getEnableDefaultStyle())
- {
- $url = $this->publishAsset($this->getDefaultStyle().'.css');
- $this->getPage()->getClientScript()->registerStyleSheetFile($url,$url);
- }
- }
-}
-
+<?php +/** + * TScaffoldBase class file. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.ActiveRecord.Scaffold + */ + +/** + * Include the base Active Record class. + */ +Prado::using('System.Data.ActiveRecord.TActiveRecord'); + +/** + * Base class for Active Record scaffold views. + * + * Provides common properties for all scaffold views (such as, TScaffoldListView, + * TScaffoldEditView, TScaffoldListView and TScaffoldView). + * + * During the OnPrRender stage the default css style file (filename style.css) + * is published and registered. To override the default style, provide your own stylesheet + * file explicitly. + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.ActiveRecord.Scaffold + * @since 3.1 + */ +abstract class TScaffoldBase extends TTemplateControl +{ + /** + * @var TActiveRecord record instance (may be new or retrieved from db) + */ + private $_record; + + /** + * @return TDbMetaData table/view information + */ + protected function getTableInfo() + { + $finder = $this->getRecordFinder(); + $gateway = $finder->getRecordManager()->getRecordGateWay(); + return $gateway->getRecordTableInfo($finder); + } + + /** + * @param TActiveRecord record instance + * @return array record property values + */ + protected function getRecordPropertyValues($record) + { + $data = array(); + foreach($this->getTableInfo()->getColumns() as $name=>$column) + $data[] = $record->getColumnValue($name); + return $data; + } + + /** + * @param TActiveRecord record instance + * @return array record primary key values. + */ + protected function getRecordPkValues($record) + { + $data=array(); + foreach($this->getTableInfo()->getColumns() as $name=>$column) + { + if($column->getIsPrimaryKey()) + $data[] = $record->getColumnValue($name); + } + return $data; + } + + /** + * Name of the Active Record class to be viewed or scaffolded. + * @return string Active Record class name. + */ + public function getRecordClass() + { + return $this->getViewState('RecordClass'); + } + + /** + * Name of the Active Record class to be viewed or scaffolded. + * @param string Active Record class name. + */ + public function setRecordClass($value) + { + $this->setViewState('RecordClass', $value); + } + + /** + * Copy the view details from another scaffold view instance. + * @param TScaffoldBase scaffold view. + */ + protected function copyFrom(TScaffoldBase $obj) + { + $this->_record = $obj->_record; + $this->setRecordClass($obj->getRecordClass()); + $this->setEnableDefaultStyle($obj->getEnableDefaultStyle()); + } + + /** + * Unset the current record instance and table information. + */ + protected function clearRecordObject() + { + $this->_record=null; + } + + /** + * Gets the current Active Record instance. Creates new instance if the + * primary key value is null otherwise the record is fetched from the db. + * @param array primary key value + * @return TActiveRecord record instance + */ + protected function getRecordObject($pk=null) + { + if($this->_record===null) + { + if($pk!==null) + { + $this->_record=$this->getRecordFinder()->findByPk($pk); + if($this->_record===null) + throw new TConfigurationException('scaffold_invalid_record_pk', + $this->getRecordClass(), $pk); + } + else + { + $class = $this->getRecordClass(); + if($class!==null) + $this->_record=Prado::createComponent($class); + else + { + throw new TConfigurationException('scaffold_invalid_record_class', + $this->getRecordClass(),$this->getID()); + } + } + } + return $this->_record; + } + + /** + * @param TActiveRecord Active Record instance. + */ + protected function setRecordObject(TActiveRecord $value) + { + $this->_record=$value; + } + + /** + * @return TActiveRecord Active Record finder instance + */ + protected function getRecordFinder() + { + return TActiveRecord::finder($this->getRecordClass()); + } + + /** + * @return string default scaffold stylesheet name + */ + public function getDefaultStyle() + { + return $this->getViewState('DefaultStyle', 'style'); + } + + /** + * @param string default scaffold stylesheet name + */ + public function setDefaultStyle($value) + { + $this->setViewState('DefaultStyle', TPropertyValue::ensureString($value), 'style'); + } + + /** + * @return boolean enable default stylesheet, default is true. + */ + public function getEnableDefaultStyle() + { + return $this->getViewState('EnableDefaultStyle', true); + } + + /** + * @param boolean enable default stylesheet, default is true. + */ + public function setEnableDefaultStyle($value) + { + return $this->setViewState('EnableDefaultStyle', TPropertyValue::ensureBoolean($value), true); + } + + /** + * Publish the default stylesheet file. + */ + public function onPreRender($param) + { + parent::onPreRender($param); + if($this->getEnableDefaultStyle()) + { + $url = $this->publishAsset($this->getDefaultStyle().'.css'); + $this->getPage()->getClientScript()->registerStyleSheetFile($url,$url); + } + } +} + diff --git a/framework/Data/ActiveRecord/Scaffold/TScaffoldEditView.php b/framework/Data/ActiveRecord/Scaffold/TScaffoldEditView.php index c0547627..166e3360 100644 --- a/framework/Data/ActiveRecord/Scaffold/TScaffoldEditView.php +++ b/framework/Data/ActiveRecord/Scaffold/TScaffoldEditView.php @@ -1,309 +1,309 @@ -<?php
-/**
- * TScaffoldEditView class and IScaffoldEditRenderer interface file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
+<?php +/** + * TScaffoldEditView class and IScaffoldEditRenderer interface file. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.ActiveRecord.Scaffold
- */
-
-/**
- * Load scaffold base.
- */
-Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldBase');
-
-/**
- * Template control for editing an Active Record instance.
- * The <b>RecordClass</b> determines the Active Record class to be edited.
- * A particular record can be edited by specifying the {@link setRecordPk RecordPk}
- * value (may be an array for composite keys).
- *
- * The default editor input controls are created based on the column types.
- * The editor layout can be specified by a renderer by set the value
- * of the {@link setEditRenderer EditRenderer} property to the class name of a
- * class that implements TScaffoldEditRenderer. A renderer is an external
- * template control that implements IScaffoldEditRenderer.
- *
- * The <b>Data</b> of the IScaffoldEditRenderer will be set as the current Active
- * Record to be edited. The <b>UpdateRecord()</b> method of IScaffoldEditRenderer
- * is called when request to save the record is requested.
- *
- * Validators in the custom external editor template should have the
- * {@link TBaseValidator::setValidationGroup ValidationGroup} property set to the
- * value of the {@link getValidationGroup} of the TScaffoldEditView instance
- * (the edit view instance is the <b>Parent</b> of the IScaffoldEditRenderer in most
- * cases.
- *
- * Cosmetic changes to the default editor should be done using Cascading Stylesheets.
- * For example, a particular field/property can be hidden by specifying "display:none" for
- * the corresponding style (each field/property has unique Css class name as "property_xxx", where
- * xxx is the property name).
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.ActiveRecord.Scaffold
- * @since 3.1
- */
-class TScaffoldEditView extends TScaffoldBase
-{
- /**
- * @var IScaffoldEditRenderer custom scaffold edit renderer
- */
- private $_editRenderer;
-
- /**
- * Initialize the editor form if it is Visible.
- */
- public function onLoad($param)
- {
- if($this->getVisible())
- $this->initializeEditForm();
- }
-
- /**
- * @return string the class name for scaffold editor. Defaults to empty, meaning not set.
- */
- public function getEditRenderer()
- {
- return $this->getViewState('EditRenderer', '');
- }
-
- /**
- * @param string the class name for scaffold editor. Defaults to empty, meaning not set.
- */
- public function setEditRenderer($value)
- {
- $this->setViewState('EditRenderer', $value, '');
- }
-
- /**
- * @param array Active Record primary key value to be edited.
- */
- public function setRecordPk($value)
- {
- $this->clearRecordObject();
- $val = TPropertyValue::ensureArray($value);
- $this->setViewState('PK', count($val) > 0 ? $val : null);
- }
-
- /**
- * @return array Active Record primary key value.
- */
- public function getRecordPk()
- {
- return $this->getViewState('PK');
- }
-
- /**
- * @return TActiveRecord current Active Record instance
- */
- protected function getCurrentRecord()
- {
- return $this->getRecordObject($this->getRecordPk());
- }
-
- /**
- * Initialize the editor form
- */
- public function initializeEditForm()
- {
- $record = $this->getCurrentRecord();
- $classPath = $this->getEditRenderer();
- if($classPath === '')
- {
- $columns = $this->getTableInfo()->getColumns();
- $this->getInputRepeater()->setDataSource($columns);
- $this->getInputRepeater()->dataBind();
- }
- else
- {
- if($this->_editRenderer===null)
- $this->createEditRenderer($record, $classPath);
- else
- $this->_editRenderer->setData($record);
- }
- }
-
- /**
- * Instantiate the external edit renderer.
- * @param TActiveRecord record to be edited
- * @param string external edit renderer class name.
- * @throws TConfigurationException raised when renderer is not an
- * instance of IScaffoldEditRenderer.
- */
- protected function createEditRenderer($record, $classPath)
- {
- $this->_editRenderer = Prado::createComponent($classPath);
- if($this->_editRenderer instanceof IScaffoldEditRenderer)
- {
- $index = $this->getControls()->remove($this->getInputRepeater());
- $this->getControls()->insertAt($index,$this->_editRenderer);
- $this->_editRenderer->setData($record);
- }
- else
- {
- throw new TConfigurationException(
- 'scaffold_invalid_edit_renderer', $this->getID(), get_class($record));
- }
- }
-
- /**
- * Initialize the default editor using the scaffold input builder.
- */
- protected function createRepeaterEditItem($sender, $param)
- {
- $type = $param->getItem()->getItemType();
- if($type==TListItemType::Item || $type==TListItemType::AlternatingItem)
- {
- $item = $param->getItem();
- $column = $item->getDataItem();
- if($column===null)
- return;
-
- $record = $this->getCurrentRecord();
- $builder = $this->getScaffoldInputBuilder($record);
- $builder->createScaffoldInput($this, $item, $column, $record);
- }
- }
-
- /**
- * Bubble the command name event. Stops bubbling when the page validator false.
- * Otherwise, the bubble event is continued.
- */
- public function bubbleEvent($sender, $param)
- {
- switch(strtolower($param->getCommandName()))
- {
- case 'save':
- return $this->doSave() ? false : true;
- case 'clear':
- $this->setRecordPk(null);
- $this->initializeEditForm();
- return false;
- default:
- return false;
- }
- }
-
- /**
- * Check the validators, then tries to save the record.
- * @return boolean true if the validators are true, false otherwise.
- */
- protected function doSave()
- {
- if($this->getPage()->getIsValid())
- {
- $record = $this->getCurrentRecord();
- if($this->_editRenderer===null)
- {
- $table = $this->getTableInfo();
- $builder = $this->getScaffoldInputBuilder($record);
- foreach($this->getInputRepeater()->getItems() as $item)
- {
- $column = $table->getColumn($item->getCustomData());
- $builder->loadScaffoldInput($this, $item, $column, $record);
- }
- }
- else
- {
- $this->_editRenderer->updateRecord($record);
- }
- $record->save();
- return true;
- }
- else if($this->_editRenderer!==null)
- {
- //preserve the form data.
- $this->_editRenderer->updateRecord($this->getCurrentRecord());
- }
-
- return false;
- }
-
- /**
- * @return TRepeater default editor input controls repeater
- */
- protected function getInputRepeater()
- {
- $this->ensureChildControls();
- return $this->getRegisteredObject('_repeater');
- }
-
- /**
- * @return TButton Button triggered to save the Active Record.
- */
- public function getSaveButton()
- {
- $this->ensureChildControls();
- return $this->getRegisteredObject('_save');
- }
-
- /**
- * @return TButton Button to clear the editor inputs.
- */
- public function getClearButton()
- {
- $this->ensureChildControls();
- return $this->getRegisteredObject('_clear');
- }
-
- /**
- * @return TButton Button to cancel the edit action (e.g. hide the edit view).
- */
- public function getCancelButton()
- {
- $this->ensureChildControls();
- return $this->getRegisteredObject('_cancel');
- }
-
- /**
- * Create the default scaffold editor control factory.
- * @param TActiveRecord record instance.
- * @return TScaffoldInputBase scaffold editor control factory.
- */
- protected function getScaffoldInputBuilder($record)
- {
- static $_builders=array();
- $class = get_class($record);
- if(!isset($_builders[$class]))
- {
- Prado::using('System.Data.ActiveRecord.Scaffold.InputBuilder.TScaffoldInputBase');
- $_builders[$class] = TScaffoldInputBase::createInputBuilder($record);
- }
- return $_builders[$class];
- }
-
- /**
- * @return string editor validation group name.
- */
- public function getValidationGroup()
- {
- return 'group_'.$this->getUniqueID();
- }
-}
-
-/**
- * IScaffoldEditRenderer interface.
- *
- * IScaffoldEditRenderer defines the interface that an edit renderer
- * needs to implement. Besides the {@link getData Data} property, an edit
- * renderer also needs to provide {@link updateRecord updateRecord} method
- * that is called before the save() method is called on the TActiveRecord.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.ActiveRecord.Scaffold
- * @since 3.1
- */
-interface IScaffoldEditRenderer extends IDataRenderer
-{
- /**
- * This method should update the record with the user input data.
- * @param TActiveRecord record to be saved.
- */
- public function updateRecord($record);
-}
-
+ * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.ActiveRecord.Scaffold + */ + +/** + * Load scaffold base. + */ +Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldBase'); + +/** + * Template control for editing an Active Record instance. + * The <b>RecordClass</b> determines the Active Record class to be edited. + * A particular record can be edited by specifying the {@link setRecordPk RecordPk} + * value (may be an array for composite keys). + * + * The default editor input controls are created based on the column types. + * The editor layout can be specified by a renderer by set the value + * of the {@link setEditRenderer EditRenderer} property to the class name of a + * class that implements TScaffoldEditRenderer. A renderer is an external + * template control that implements IScaffoldEditRenderer. + * + * The <b>Data</b> of the IScaffoldEditRenderer will be set as the current Active + * Record to be edited. The <b>UpdateRecord()</b> method of IScaffoldEditRenderer + * is called when request to save the record is requested. + * + * Validators in the custom external editor template should have the + * {@link TBaseValidator::setValidationGroup ValidationGroup} property set to the + * value of the {@link getValidationGroup} of the TScaffoldEditView instance + * (the edit view instance is the <b>Parent</b> of the IScaffoldEditRenderer in most + * cases. + * + * Cosmetic changes to the default editor should be done using Cascading Stylesheets. + * For example, a particular field/property can be hidden by specifying "display:none" for + * the corresponding style (each field/property has unique Css class name as "property_xxx", where + * xxx is the property name). + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.ActiveRecord.Scaffold + * @since 3.1 + */ +class TScaffoldEditView extends TScaffoldBase +{ + /** + * @var IScaffoldEditRenderer custom scaffold edit renderer + */ + private $_editRenderer; + + /** + * Initialize the editor form if it is Visible. + */ + public function onLoad($param) + { + if($this->getVisible()) + $this->initializeEditForm(); + } + + /** + * @return string the class name for scaffold editor. Defaults to empty, meaning not set. + */ + public function getEditRenderer() + { + return $this->getViewState('EditRenderer', ''); + } + + /** + * @param string the class name for scaffold editor. Defaults to empty, meaning not set. + */ + public function setEditRenderer($value) + { + $this->setViewState('EditRenderer', $value, ''); + } + + /** + * @param array Active Record primary key value to be edited. + */ + public function setRecordPk($value) + { + $this->clearRecordObject(); + $val = TPropertyValue::ensureArray($value); + $this->setViewState('PK', count($val) > 0 ? $val : null); + } + + /** + * @return array Active Record primary key value. + */ + public function getRecordPk() + { + return $this->getViewState('PK'); + } + + /** + * @return TActiveRecord current Active Record instance + */ + protected function getCurrentRecord() + { + return $this->getRecordObject($this->getRecordPk()); + } + + /** + * Initialize the editor form + */ + public function initializeEditForm() + { + $record = $this->getCurrentRecord(); + $classPath = $this->getEditRenderer(); + if($classPath === '') + { + $columns = $this->getTableInfo()->getColumns(); + $this->getInputRepeater()->setDataSource($columns); + $this->getInputRepeater()->dataBind(); + } + else + { + if($this->_editRenderer===null) + $this->createEditRenderer($record, $classPath); + else + $this->_editRenderer->setData($record); + } + } + + /** + * Instantiate the external edit renderer. + * @param TActiveRecord record to be edited + * @param string external edit renderer class name. + * @throws TConfigurationException raised when renderer is not an + * instance of IScaffoldEditRenderer. + */ + protected function createEditRenderer($record, $classPath) + { + $this->_editRenderer = Prado::createComponent($classPath); + if($this->_editRenderer instanceof IScaffoldEditRenderer) + { + $index = $this->getControls()->remove($this->getInputRepeater()); + $this->getControls()->insertAt($index,$this->_editRenderer); + $this->_editRenderer->setData($record); + } + else + { + throw new TConfigurationException( + 'scaffold_invalid_edit_renderer', $this->getID(), get_class($record)); + } + } + + /** + * Initialize the default editor using the scaffold input builder. + */ + protected function createRepeaterEditItem($sender, $param) + { + $type = $param->getItem()->getItemType(); + if($type==TListItemType::Item || $type==TListItemType::AlternatingItem) + { + $item = $param->getItem(); + $column = $item->getDataItem(); + if($column===null) + return; + + $record = $this->getCurrentRecord(); + $builder = $this->getScaffoldInputBuilder($record); + $builder->createScaffoldInput($this, $item, $column, $record); + } + } + + /** + * Bubble the command name event. Stops bubbling when the page validator false. + * Otherwise, the bubble event is continued. + */ + public function bubbleEvent($sender, $param) + { + switch(strtolower($param->getCommandName())) + { + case 'save': + return $this->doSave() ? false : true; + case 'clear': + $this->setRecordPk(null); + $this->initializeEditForm(); + return false; + default: + return false; + } + } + + /** + * Check the validators, then tries to save the record. + * @return boolean true if the validators are true, false otherwise. + */ + protected function doSave() + { + if($this->getPage()->getIsValid()) + { + $record = $this->getCurrentRecord(); + if($this->_editRenderer===null) + { + $table = $this->getTableInfo(); + $builder = $this->getScaffoldInputBuilder($record); + foreach($this->getInputRepeater()->getItems() as $item) + { + $column = $table->getColumn($item->getCustomData()); + $builder->loadScaffoldInput($this, $item, $column, $record); + } + } + else + { + $this->_editRenderer->updateRecord($record); + } + $record->save(); + return true; + } + else if($this->_editRenderer!==null) + { + //preserve the form data. + $this->_editRenderer->updateRecord($this->getCurrentRecord()); + } + + return false; + } + + /** + * @return TRepeater default editor input controls repeater + */ + protected function getInputRepeater() + { + $this->ensureChildControls(); + return $this->getRegisteredObject('_repeater'); + } + + /** + * @return TButton Button triggered to save the Active Record. + */ + public function getSaveButton() + { + $this->ensureChildControls(); + return $this->getRegisteredObject('_save'); + } + + /** + * @return TButton Button to clear the editor inputs. + */ + public function getClearButton() + { + $this->ensureChildControls(); + return $this->getRegisteredObject('_clear'); + } + + /** + * @return TButton Button to cancel the edit action (e.g. hide the edit view). + */ + public function getCancelButton() + { + $this->ensureChildControls(); + return $this->getRegisteredObject('_cancel'); + } + + /** + * Create the default scaffold editor control factory. + * @param TActiveRecord record instance. + * @return TScaffoldInputBase scaffold editor control factory. + */ + protected function getScaffoldInputBuilder($record) + { + static $_builders=array(); + $class = get_class($record); + if(!isset($_builders[$class])) + { + Prado::using('System.Data.ActiveRecord.Scaffold.InputBuilder.TScaffoldInputBase'); + $_builders[$class] = TScaffoldInputBase::createInputBuilder($record); + } + return $_builders[$class]; + } + + /** + * @return string editor validation group name. + */ + public function getValidationGroup() + { + return 'group_'.$this->getUniqueID(); + } +} + +/** + * IScaffoldEditRenderer interface. + * + * IScaffoldEditRenderer defines the interface that an edit renderer + * needs to implement. Besides the {@link getData Data} property, an edit + * renderer also needs to provide {@link updateRecord updateRecord} method + * that is called before the save() method is called on the TActiveRecord. + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.ActiveRecord.Scaffold + * @since 3.1 + */ +interface IScaffoldEditRenderer extends IDataRenderer +{ + /** + * This method should update the record with the user input data. + * @param TActiveRecord record to be saved. + */ + public function updateRecord($record); +} + diff --git a/framework/Data/ActiveRecord/Scaffold/TScaffoldListView.php b/framework/Data/ActiveRecord/Scaffold/TScaffoldListView.php index 953420e3..bed5bf88 100644 --- a/framework/Data/ActiveRecord/Scaffold/TScaffoldListView.php +++ b/framework/Data/ActiveRecord/Scaffold/TScaffoldListView.php @@ -1,306 +1,306 @@ -<?php
-/**
- * TScaffoldListView class file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
+<?php +/** + * TScaffoldListView class file. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.ActiveRecord.Scaffold
- */
-
-/**
- * Load the scaffold base class.
- */
-Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldBase');
-
-/**
- * TScaffoldListView displays a list of Active Records.
- *
- * The {@link getHeader Header} property is a TRepeater displaying the
- * Active Record property/field names. The {@link getSort Sort} property
- * is a drop down list displaying the combination of properties and its possible
- * ordering. The {@link getPager Pager} property is a TPager control displaying
- * the links and/or buttons that navigate to different pages in the Active Record data.
- * The {@link getList List} property is a TRepeater that renders a row of
- * Active Record data.
- *
- * Custom rendering of the each Active Record can be achieved by specifying
- * the ItemTemplate or AlternatingItemTemplate property of the main {@linnk getList List}
- * repeater.
- *
- * The TScaffoldListView will listen for two command events named "delete" and
- * "edit". A "delete" command will delete a the record for the row where the
- * "delete" command is originates. An "edit" command will push
- * the record data to be edited by a TScaffoldEditView with ID specified by the
- * {@link setEditViewID EditViewID}.
- *
- * Additional {@link setSearchCondition SearchCondition} and
- * {@link setSearchParameters SearchParameters} (takes array values) can be
- * specified to customize the records to be shown. The {@link setSearchCondition SearchCondition}
- * will be used as the Condition property of TActiveRecordCriteria, and similarly
- * the {@link setSearchParameters SearchParameters} will be the corresponding
- * Parameters property of TActiveRecordCriteria.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.ActiveRecord.Scaffold
- * @since 3.1
- */
-class TScaffoldListView extends TScaffoldBase
-{
- /**
- * Initialize the sort drop down list and the column names repeater.
- */
- protected function initializeSort()
- {
- $table = $this->getTableInfo();
- $sorts = array('Sort By', str_repeat('-',15));
- $headers = array();
- foreach($table->getColumns() as $name=>$colum)
- {
- $fname = ucwords(str_replace('_', ' ', $name));
- $sorts[$name.' ASC'] = $fname .' Ascending';
- $sorts[$name.' DESC'] = $fname .' Descending';
- $headers[] = $fname ;
- }
- $this->_sort->setDataSource($sorts);
- $this->_sort->dataBind();
- $this->_header->setDataSource($headers);
- $this->_header->dataBind();
- }
-
- /**
- * Loads and display the data.
- */
- public function onPreRender($param)
- {
- parent::onPreRender($param);
- if(!$this->getPage()->getIsPostBack() || $this->getViewState('CurrentClass')!=$this->getRecordClass())
- {
- $this->initializeSort();
- $this->setViewState('CurrentClass', $this->getRecordClass());
- }
- $this->loadRecordData();
- }
-
- /**
- * Fetch the records and data bind it to the list.
- */
- protected function loadRecordData()
- {
- $search = new TActiveRecordCriteria($this->getSearchCondition(), $this->getSearchParameters());
- $this->_list->setVirtualItemCount($this->getRecordFinder()->count($search));
- $finder = $this->getRecordFinder();
- $criteria = $this->getRecordCriteria();
- $this->_list->setDataSource($finder->findAll($criteria));
- $this->_list->dataBind();
- }
-
- /**
- * @return TActiveRecordCriteria sort/search/paging criteria
- */
- protected function getRecordCriteria()
- {
- $total = $this->_list->getVirtualItemCount();
- $limit = $this->_list->getPageSize();
- $offset = $this->_list->getCurrentPageIndex()*$limit;
- if($offset + $limit > $total)
- $limit = $total - $offset;
- $criteria = new TActiveRecordCriteria($this->getSearchCondition(), $this->getSearchParameters());
- if($limit > 0)
- {
- $criteria->setLimit($limit);
- if($offset <= $total)
- $criteria->setOffset($offset);
- }
- $order = explode(' ',$this->_sort->getSelectedValue(), 2);
- if(is_array($order) && count($order) === 2)
- $criteria->OrdersBy[$order[0]] = $order[1];
- return $criteria;
- }
-
- /**
- * @param string search condition, the SQL string after the WHERE clause.
- */
- public function setSearchCondition($value)
- {
- $this->setViewState('SearchCondition', $value);
- }
-
- /**
- * @param string SQL search condition for list display.
- */
- public function getSearchCondition()
- {
- return $this->getViewState('SearchCondition');
- }
-
- /**
- * @param array search parameters
- */
- public function setSearchParameters($value)
- {
- $this->setViewState('SearchParameters', TPropertyValue::ensureArray($value),array());
- }
-
- /**
- * @return array search parameters
- */
- public function getSearchParameters()
- {
- return $this->getViewState('SearchParameters', array());
- }
-
- /**
- * Continue bubbling the "edit" command, "delete" command is handled in this class.
- */
- public function bubbleEvent($sender, $param)
- {
- switch(strtolower($param->getCommandName()))
- {
- case 'delete':
- return $this->deleteRecord($sender, $param);
- case 'edit':
- $this->initializeEdit($sender, $param);
- }
- $this->raiseBubbleEvent($this, $param);
- return true;
- }
-
- /**
- * Initialize the edit view control form when EditViewID is set.
- */
- protected function initializeEdit($sender, $param)
- {
- if(($ctrl=$this->getEditViewControl())!==null)
- {
- if($param instanceof TRepeaterCommandEventParameter)
- {
- $pk = $param->getItem()->getCustomData();
- $ctrl->setRecordPk($pk);
- $ctrl->initializeEditForm();
- }
- }
- }
-
- /**
- * Deletes an Active Record.
- */
- protected function deleteRecord($sender, $param)
- {
- if($param instanceof TRepeaterCommandEventParameter)
- {
- $pk = $param->getItem()->getCustomData();
- $this->getRecordFinder()->deleteByPk($pk);
- }
- }
-
- /**
- * Initialize the default display for each Active Record item.
- */
- protected function listItemCreated($sender, $param)
- {
- $item = $param->getItem();
- if($item instanceof IItemDataRenderer)
- {
- $type = $item->getItemType();
- if($type==TListItemType::Item || $type==TListItemType::AlternatingItem)
- $this->populateField($sender, $param);
- }
- }
-
- /**
- * Sets the Record primary key to the current repeater item's CustomData.
- * Binds the inner repeater with properties of the current Active Record.
- */
- protected function populateField($sender, $param)
- {
- $item = $param->getItem();
- if(($data = $item->getData()) !== null)
- {
- $item->setCustomData($this->getRecordPkValues($data));
- if(($prop = $item->findControl('_properties'))!==null)
- {
- $item->_properties->setDataSource($this->getRecordPropertyValues($data));
- $item->_properties->dataBind();
- }
- }
- }
-
- /**
- * Updates repeater page index with the pager new index value.
- */
- protected function pageChanged($sender, $param)
- {
- $this->_list->setCurrentPageIndex($param->getNewPageIndex());
- }
-
- /**
- * @return TRepeater Repeater control for Active Record instances.
- */
- public function getList()
- {
- $this->ensureChildControls();
- return $this->getRegisteredObject('_list');
- }
-
- /**
- * @return TPager List pager control.
- */
- public function getPager()
- {
- $this->ensureChildControls();
- return $this->getRegisteredObject('_pager');
- }
-
- /**
- * @return TDropDownList Control that displays and controls the record ordering.
- */
- public function getSort()
- {
- $this->ensureChildControls();
- return $this->getRegisteredObject('_sort');
- }
-
- /**
- * @return TRepeater Repeater control for record property names.
- */
- public function getHeader()
- {
- $this->ensureChildControls();
- return $this->getRegisteredObject('_header');
- }
-
- /**
- * @return string TScaffoldEditView control ID for editing selected Active Record.
- */
- public function getEditViewID()
- {
- return $this->getViewState('EditViewID');
- }
-
- /**
- * @param string TScaffoldEditView control ID for editing selected Active Record.
- */
- public function setEditViewID($value)
- {
- $this->setViewState('EditViewID', $value);
- }
-
- /**
- * @return TScaffoldEditView control for editing selected Active Record, null if EditViewID is not set.
- */
- protected function getEditViewControl()
- {
- if(($id=$this->getEditViewID())!==null)
- {
- $ctrl = $this->getParent()->findControl($id);
- if($ctrl===null)
- throw new TConfigurationException('scaffold_unable_to_find_edit_view', $id);
- return $ctrl;
- }
- }
-}
+ * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.ActiveRecord.Scaffold + */ + +/** + * Load the scaffold base class. + */ +Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldBase'); + +/** + * TScaffoldListView displays a list of Active Records. + * + * The {@link getHeader Header} property is a TRepeater displaying the + * Active Record property/field names. The {@link getSort Sort} property + * is a drop down list displaying the combination of properties and its possible + * ordering. The {@link getPager Pager} property is a TPager control displaying + * the links and/or buttons that navigate to different pages in the Active Record data. + * The {@link getList List} property is a TRepeater that renders a row of + * Active Record data. + * + * Custom rendering of the each Active Record can be achieved by specifying + * the ItemTemplate or AlternatingItemTemplate property of the main {@linnk getList List} + * repeater. + * + * The TScaffoldListView will listen for two command events named "delete" and + * "edit". A "delete" command will delete a the record for the row where the + * "delete" command is originates. An "edit" command will push + * the record data to be edited by a TScaffoldEditView with ID specified by the + * {@link setEditViewID EditViewID}. + * + * Additional {@link setSearchCondition SearchCondition} and + * {@link setSearchParameters SearchParameters} (takes array values) can be + * specified to customize the records to be shown. The {@link setSearchCondition SearchCondition} + * will be used as the Condition property of TActiveRecordCriteria, and similarly + * the {@link setSearchParameters SearchParameters} will be the corresponding + * Parameters property of TActiveRecordCriteria. + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.ActiveRecord.Scaffold + * @since 3.1 + */ +class TScaffoldListView extends TScaffoldBase +{ + /** + * Initialize the sort drop down list and the column names repeater. + */ + protected function initializeSort() + { + $table = $this->getTableInfo(); + $sorts = array('Sort By', str_repeat('-',15)); + $headers = array(); + foreach($table->getColumns() as $name=>$colum) + { + $fname = ucwords(str_replace('_', ' ', $name)); + $sorts[$name.' ASC'] = $fname .' Ascending'; + $sorts[$name.' DESC'] = $fname .' Descending'; + $headers[] = $fname ; + } + $this->_sort->setDataSource($sorts); + $this->_sort->dataBind(); + $this->_header->setDataSource($headers); + $this->_header->dataBind(); + } + + /** + * Loads and display the data. + */ + public function onPreRender($param) + { + parent::onPreRender($param); + if(!$this->getPage()->getIsPostBack() || $this->getViewState('CurrentClass')!=$this->getRecordClass()) + { + $this->initializeSort(); + $this->setViewState('CurrentClass', $this->getRecordClass()); + } + $this->loadRecordData(); + } + + /** + * Fetch the records and data bind it to the list. + */ + protected function loadRecordData() + { + $search = new TActiveRecordCriteria($this->getSearchCondition(), $this->getSearchParameters()); + $this->_list->setVirtualItemCount($this->getRecordFinder()->count($search)); + $finder = $this->getRecordFinder(); + $criteria = $this->getRecordCriteria(); + $this->_list->setDataSource($finder->findAll($criteria)); + $this->_list->dataBind(); + } + + /** + * @return TActiveRecordCriteria sort/search/paging criteria + */ + protected function getRecordCriteria() + { + $total = $this->_list->getVirtualItemCount(); + $limit = $this->_list->getPageSize(); + $offset = $this->_list->getCurrentPageIndex()*$limit; + if($offset + $limit > $total) + $limit = $total - $offset; + $criteria = new TActiveRecordCriteria($this->getSearchCondition(), $this->getSearchParameters()); + if($limit > 0) + { + $criteria->setLimit($limit); + if($offset <= $total) + $criteria->setOffset($offset); + } + $order = explode(' ',$this->_sort->getSelectedValue(), 2); + if(is_array($order) && count($order) === 2) + $criteria->OrdersBy[$order[0]] = $order[1]; + return $criteria; + } + + /** + * @param string search condition, the SQL string after the WHERE clause. + */ + public function setSearchCondition($value) + { + $this->setViewState('SearchCondition', $value); + } + + /** + * @param string SQL search condition for list display. + */ + public function getSearchCondition() + { + return $this->getViewState('SearchCondition'); + } + + /** + * @param array search parameters + */ + public function setSearchParameters($value) + { + $this->setViewState('SearchParameters', TPropertyValue::ensureArray($value),array()); + } + + /** + * @return array search parameters + */ + public function getSearchParameters() + { + return $this->getViewState('SearchParameters', array()); + } + + /** + * Continue bubbling the "edit" command, "delete" command is handled in this class. + */ + public function bubbleEvent($sender, $param) + { + switch(strtolower($param->getCommandName())) + { + case 'delete': + return $this->deleteRecord($sender, $param); + case 'edit': + $this->initializeEdit($sender, $param); + } + $this->raiseBubbleEvent($this, $param); + return true; + } + + /** + * Initialize the edit view control form when EditViewID is set. + */ + protected function initializeEdit($sender, $param) + { + if(($ctrl=$this->getEditViewControl())!==null) + { + if($param instanceof TRepeaterCommandEventParameter) + { + $pk = $param->getItem()->getCustomData(); + $ctrl->setRecordPk($pk); + $ctrl->initializeEditForm(); + } + } + } + + /** + * Deletes an Active Record. + */ + protected function deleteRecord($sender, $param) + { + if($param instanceof TRepeaterCommandEventParameter) + { + $pk = $param->getItem()->getCustomData(); + $this->getRecordFinder()->deleteByPk($pk); + } + } + + /** + * Initialize the default display for each Active Record item. + */ + protected function listItemCreated($sender, $param) + { + $item = $param->getItem(); + if($item instanceof IItemDataRenderer) + { + $type = $item->getItemType(); + if($type==TListItemType::Item || $type==TListItemType::AlternatingItem) + $this->populateField($sender, $param); + } + } + + /** + * Sets the Record primary key to the current repeater item's CustomData. + * Binds the inner repeater with properties of the current Active Record. + */ + protected function populateField($sender, $param) + { + $item = $param->getItem(); + if(($data = $item->getData()) !== null) + { + $item->setCustomData($this->getRecordPkValues($data)); + if(($prop = $item->findControl('_properties'))!==null) + { + $item->_properties->setDataSource($this->getRecordPropertyValues($data)); + $item->_properties->dataBind(); + } + } + } + + /** + * Updates repeater page index with the pager new index value. + */ + protected function pageChanged($sender, $param) + { + $this->_list->setCurrentPageIndex($param->getNewPageIndex()); + } + + /** + * @return TRepeater Repeater control for Active Record instances. + */ + public function getList() + { + $this->ensureChildControls(); + return $this->getRegisteredObject('_list'); + } + + /** + * @return TPager List pager control. + */ + public function getPager() + { + $this->ensureChildControls(); + return $this->getRegisteredObject('_pager'); + } + + /** + * @return TDropDownList Control that displays and controls the record ordering. + */ + public function getSort() + { + $this->ensureChildControls(); + return $this->getRegisteredObject('_sort'); + } + + /** + * @return TRepeater Repeater control for record property names. + */ + public function getHeader() + { + $this->ensureChildControls(); + return $this->getRegisteredObject('_header'); + } + + /** + * @return string TScaffoldEditView control ID for editing selected Active Record. + */ + public function getEditViewID() + { + return $this->getViewState('EditViewID'); + } + + /** + * @param string TScaffoldEditView control ID for editing selected Active Record. + */ + public function setEditViewID($value) + { + $this->setViewState('EditViewID', $value); + } + + /** + * @return TScaffoldEditView control for editing selected Active Record, null if EditViewID is not set. + */ + protected function getEditViewControl() + { + if(($id=$this->getEditViewID())!==null) + { + $ctrl = $this->getParent()->findControl($id); + if($ctrl===null) + throw new TConfigurationException('scaffold_unable_to_find_edit_view', $id); + return $ctrl; + } + } +} diff --git a/framework/Data/ActiveRecord/Scaffold/TScaffoldSearch.php b/framework/Data/ActiveRecord/Scaffold/TScaffoldSearch.php index bd679c94..40335085 100644 --- a/framework/Data/ActiveRecord/Scaffold/TScaffoldSearch.php +++ b/framework/Data/ActiveRecord/Scaffold/TScaffoldSearch.php @@ -1,150 +1,150 @@ -<?php
-/**
- * TScaffoldSearch class file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
+<?php +/** + * TScaffoldSearch class file. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.ActiveRecord.Scaffold
- */
-
-/**
- * Import the scaffold base.
- */
-Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldBase');
-
-/**
- * TScaffoldSearch provide a simple textbox and a button that is used
- * to perform search on a TScaffoldListView with ID given by {@link setListViewID ListViewID}.
- *
- * The {@link getSearchText SearchText} property is a TTextBox and the
- * {@link getSearchButton SearchButton} property is a TButton with label value "Search".
- *
- * Searchable fields of the Active Record can be restricted by specifying
- * a comma delimited string of allowable fields in the
- * {@link setSearchableFields SearchableFields} property. The default is null,
- * meaning that most text type fields are searched (the default searchable fields
- * are database dependent).
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.ActiveRecord.Scaffold
- * @since 3.1
- */
-class TScaffoldSearch extends TScaffoldBase
-{
- /**
- * @var TScaffoldListView the scaffold list view.
- */
- private $_list;
-
- /**
- * @return TScaffoldListView the scaffold list view this search box belongs to.
- */
- protected function getListView()
- {
- if($this->_list===null && ($id = $this->getListViewID()) !== null)
- {
- $this->_list = $this->getParent()->findControl($id);
- if($this->_list ===null)
- throw new TConfigurationException('scaffold_unable_to_find_list_view', $id);
- }
- return $this->_list;
- }
-
- /**
- * @param string ID of the TScaffoldListView this search control belongs to.
- */
- public function setListViewID($value)
- {
- $this->setViewState('ListViewID', $value);
- }
-
- /**
- * @return string ID of the TScaffoldListView this search control belongs to.
- */
- public function getListViewID()
- {
- return $this->getViewState('ListViewID');
- }
-
- /**
- * Sets the SearchCondition of the TScaffoldListView as the search terms
- * given by the text of the search text box.
- */
- public function bubbleEvent($sender, $param)
- {
- if(strtolower($param->getCommandName())==='search')
- {
- if(($list = $this->getListView()) !== null)
- {
- $list->setSearchCondition($this->createSearchCondition());
- return false;
- }
- }
- $this->raiseBubbleEvent($this, $param);
- return true;
- }
-
- /**
- * @return string the search criteria for the search terms in the search text box.
- */
- protected function createSearchCondition()
- {
- $table = $this->getTableInfo();
- if(strlen($str=$this->getSearchText()->getText()) > 0)
- {
- $builder = $table->createCommandBuilder($this->getRecordFinder()->getDbConnection());
- return $builder->getSearchExpression($this->getFields(), $str);
- }
- }
-
- /**
- * @return array list of fields to be searched.
- */
- protected function getFields()
- {
- if(strlen(trim($str=$this->getSearchableFields()))>0)
- $fields = preg_split('/\s*,\s*/', $str);
- else
- $fields = $this->getTableInfo()->getColumns()->getKeys();
- return $fields;
- }
-
- /**
- * @return string comma delimited list of fields that may be searched.
- */
- public function getSearchableFields()
- {
- return $this->getViewState('SearchableFields','');
- }
-
- /**
- * @param string comma delimited list of fields that may be searched.
- */
- public function setSearchableFields($value)
- {
- $this->setViewState('SearchableFields', $value, '');
- }
-
- /**
- * @return TButton button with default label "Search".
- */
- public function getSearchButton()
- {
- $this->ensureChildControls();
- return $this->getRegisteredObject('_search');
- }
-
- /**
- * @return TTextBox search text box.
- */
- public function getSearchText()
- {
- $this->ensureChildControls();
- return $this->getRegisteredObject('_textbox');
- }
-}
+ * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.ActiveRecord.Scaffold + */ + +/** + * Import the scaffold base. + */ +Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldBase'); + +/** + * TScaffoldSearch provide a simple textbox and a button that is used + * to perform search on a TScaffoldListView with ID given by {@link setListViewID ListViewID}. + * + * The {@link getSearchText SearchText} property is a TTextBox and the + * {@link getSearchButton SearchButton} property is a TButton with label value "Search". + * + * Searchable fields of the Active Record can be restricted by specifying + * a comma delimited string of allowable fields in the + * {@link setSearchableFields SearchableFields} property. The default is null, + * meaning that most text type fields are searched (the default searchable fields + * are database dependent). + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.ActiveRecord.Scaffold + * @since 3.1 + */ +class TScaffoldSearch extends TScaffoldBase +{ + /** + * @var TScaffoldListView the scaffold list view. + */ + private $_list; + + /** + * @return TScaffoldListView the scaffold list view this search box belongs to. + */ + protected function getListView() + { + if($this->_list===null && ($id = $this->getListViewID()) !== null) + { + $this->_list = $this->getParent()->findControl($id); + if($this->_list ===null) + throw new TConfigurationException('scaffold_unable_to_find_list_view', $id); + } + return $this->_list; + } + + /** + * @param string ID of the TScaffoldListView this search control belongs to. + */ + public function setListViewID($value) + { + $this->setViewState('ListViewID', $value); + } + + /** + * @return string ID of the TScaffoldListView this search control belongs to. + */ + public function getListViewID() + { + return $this->getViewState('ListViewID'); + } + + /** + * Sets the SearchCondition of the TScaffoldListView as the search terms + * given by the text of the search text box. + */ + public function bubbleEvent($sender, $param) + { + if(strtolower($param->getCommandName())==='search') + { + if(($list = $this->getListView()) !== null) + { + $list->setSearchCondition($this->createSearchCondition()); + return false; + } + } + $this->raiseBubbleEvent($this, $param); + return true; + } + + /** + * @return string the search criteria for the search terms in the search text box. + */ + protected function createSearchCondition() + { + $table = $this->getTableInfo(); + if(strlen($str=$this->getSearchText()->getText()) > 0) + { + $builder = $table->createCommandBuilder($this->getRecordFinder()->getDbConnection()); + return $builder->getSearchExpression($this->getFields(), $str); + } + } + + /** + * @return array list of fields to be searched. + */ + protected function getFields() + { + if(strlen(trim($str=$this->getSearchableFields()))>0) + $fields = preg_split('/\s*,\s*/', $str); + else + $fields = $this->getTableInfo()->getColumns()->getKeys(); + return $fields; + } + + /** + * @return string comma delimited list of fields that may be searched. + */ + public function getSearchableFields() + { + return $this->getViewState('SearchableFields',''); + } + + /** + * @param string comma delimited list of fields that may be searched. + */ + public function setSearchableFields($value) + { + $this->setViewState('SearchableFields', $value, ''); + } + + /** + * @return TButton button with default label "Search". + */ + public function getSearchButton() + { + $this->ensureChildControls(); + return $this->getRegisteredObject('_search'); + } + + /** + * @return TTextBox search text box. + */ + public function getSearchText() + { + $this->ensureChildControls(); + return $this->getRegisteredObject('_textbox'); + } +} diff --git a/framework/Data/ActiveRecord/Scaffold/TScaffoldView.php b/framework/Data/ActiveRecord/Scaffold/TScaffoldView.php index 69bc1f81..40eee21d 100644 --- a/framework/Data/ActiveRecord/Scaffold/TScaffoldView.php +++ b/framework/Data/ActiveRecord/Scaffold/TScaffoldView.php @@ -1,143 +1,143 @@ -<?php
-/**
- * TScaffoldView class.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.ActiveRecord.Scaffold
- */
-
-/**
- * Import scaffold base, list, edit and search controls.
- */
-Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldBase');
-Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldListView');
-Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldEditView');
-Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldSearch');
-
-/**
- * TScaffoldView is a composite control consisting of TScaffoldListView
- * with a TScaffoldSearch. In addition, it will display a TScaffoldEditView
- * when an "edit" command is raised from the TScaffoldListView (when the
- * edit button is clicked). Futher more, the "add" button can be clicked
- * that shows an empty data TScaffoldListView for creating new records.
- *
- * The {@link getListView ListView} property gives a TScaffoldListView for
- * display the record data. The {@link getEditView EditView} is the
- * TScaffoldEditView that renders the
- * inputs for editing and adding records. The {@link getSearchControl SearchControl}
- * is a TScaffoldSearch responsible to the search user interface.
- *
- * Set the {@link setRecordClass RecordClass} property to the name of
- * the Active Record class to be displayed/edited/added.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.ActiveRecord.Scaffold
- * @since 3.0
- */
-class TScaffoldView extends TScaffoldBase
-{
- /**
- * Copy basic record details to the list/edit/search controls.
- */
- public function onPreRender($param)
- {
- parent::onPreRender($param);
- $this->getListView()->copyFrom($this);
- $this->getEditView()->copyFrom($this);
- $this->getSearchControl()->copyFrom($this);
- }
-
- /**
- * @return TScaffoldListView scaffold list view.
- */
- public function getListView()
- {
- $this->ensureChildControls();
- return $this->getRegisteredObject('_listView');
- }
-
- /**
- * @return TScaffoldEditView scaffold edit view.
- */
- public function getEditView()
- {
- $this->ensureChildControls();
- return $this->getRegisteredObject('_editView');
- }
-
- /**
- * @return TScaffoldSearch scaffold search textbox and button.
- */
- public function getSearchControl()
- {
- $this->ensureChildControls();
- return $this->getRegisteredObject('_search');
- }
-
- /**
- * @return TButton "Add new record" button.
- */
- public function getAddButton()
- {
- $this->ensureChildControls();
- return $this->getRegisteredObject('_newButton');
- }
-
- /**
- * Handle the "edit" and "new" commands by displaying the edit view.
- * Default command shows the list view.
- */
- public function bubbleEvent($sender,$param)
- {
- switch(strtolower($param->getCommandName()))
- {
- case 'edit':
- return $this->showEditView($sender, $param);
- case 'new':
- return $this->showAddView($sender, $param);
- default:
- return $this->showListView($sender, $param);
- }
- return false;
- }
-
- /**
- * Shows the edit record view.
- */
- protected function showEditView($sender, $param)
- {
- $this->getListView()->setVisible(false);
- $this->getEditView()->setVisible(true);
- $this->_panForNewButton->setVisible(false);
- $this->_panForSearch->setVisible(false);
- $this->getEditView()->getCancelButton()->setVisible(true);
- $this->getEditView()->getClearButton()->setVisible(false);
- }
-
- /**
- * Shows the view for listing the records.
- */
- protected function showListView($sender, $param)
- {
- $this->getListView()->setVisible(true);
- $this->getEditView()->setVisible(false);
- $this->_panForNewButton->setVisible(true);
- $this->_panForSearch->setVisible(true);
- }
-
- /**
- * Shows the add record view.
- */
- protected function showAddView($sender, $param)
- {
- $this->getEditView()->setRecordPk(null);
- $this->getEditView()->initializeEditForm();
- $this->showEditView($sender, $param);
- }
-}
-
+<?php +/** + * TScaffoldView class. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.ActiveRecord.Scaffold + */ + +/** + * Import scaffold base, list, edit and search controls. + */ +Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldBase'); +Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldListView'); +Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldEditView'); +Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldSearch'); + +/** + * TScaffoldView is a composite control consisting of TScaffoldListView + * with a TScaffoldSearch. In addition, it will display a TScaffoldEditView + * when an "edit" command is raised from the TScaffoldListView (when the + * edit button is clicked). Futher more, the "add" button can be clicked + * that shows an empty data TScaffoldListView for creating new records. + * + * The {@link getListView ListView} property gives a TScaffoldListView for + * display the record data. The {@link getEditView EditView} is the + * TScaffoldEditView that renders the + * inputs for editing and adding records. The {@link getSearchControl SearchControl} + * is a TScaffoldSearch responsible to the search user interface. + * + * Set the {@link setRecordClass RecordClass} property to the name of + * the Active Record class to be displayed/edited/added. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.ActiveRecord.Scaffold + * @since 3.0 + */ +class TScaffoldView extends TScaffoldBase +{ + /** + * Copy basic record details to the list/edit/search controls. + */ + public function onPreRender($param) + { + parent::onPreRender($param); + $this->getListView()->copyFrom($this); + $this->getEditView()->copyFrom($this); + $this->getSearchControl()->copyFrom($this); + } + + /** + * @return TScaffoldListView scaffold list view. + */ + public function getListView() + { + $this->ensureChildControls(); + return $this->getRegisteredObject('_listView'); + } + + /** + * @return TScaffoldEditView scaffold edit view. + */ + public function getEditView() + { + $this->ensureChildControls(); + return $this->getRegisteredObject('_editView'); + } + + /** + * @return TScaffoldSearch scaffold search textbox and button. + */ + public function getSearchControl() + { + $this->ensureChildControls(); + return $this->getRegisteredObject('_search'); + } + + /** + * @return TButton "Add new record" button. + */ + public function getAddButton() + { + $this->ensureChildControls(); + return $this->getRegisteredObject('_newButton'); + } + + /** + * Handle the "edit" and "new" commands by displaying the edit view. + * Default command shows the list view. + */ + public function bubbleEvent($sender,$param) + { + switch(strtolower($param->getCommandName())) + { + case 'edit': + return $this->showEditView($sender, $param); + case 'new': + return $this->showAddView($sender, $param); + default: + return $this->showListView($sender, $param); + } + return false; + } + + /** + * Shows the edit record view. + */ + protected function showEditView($sender, $param) + { + $this->getListView()->setVisible(false); + $this->getEditView()->setVisible(true); + $this->_panForNewButton->setVisible(false); + $this->_panForSearch->setVisible(false); + $this->getEditView()->getCancelButton()->setVisible(true); + $this->getEditView()->getClearButton()->setVisible(false); + } + + /** + * Shows the view for listing the records. + */ + protected function showListView($sender, $param) + { + $this->getListView()->setVisible(true); + $this->getEditView()->setVisible(false); + $this->_panForNewButton->setVisible(true); + $this->_panForSearch->setVisible(true); + } + + /** + * Shows the add record view. + */ + protected function showAddView($sender, $param) + { + $this->getEditView()->setRecordPk(null); + $this->getEditView()->initializeEditForm(); + $this->showEditView($sender, $param); + } +} + diff --git a/framework/Data/ActiveRecord/TActiveRecordConfig.php b/framework/Data/ActiveRecord/TActiveRecordConfig.php index 478786d3..821c4223 100644 --- a/framework/Data/ActiveRecord/TActiveRecordConfig.php +++ b/framework/Data/ActiveRecord/TActiveRecordConfig.php @@ -1,201 +1,201 @@ -<?php
-/**
- * TActiveRecordConfig class file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
+<?php +/** + * TActiveRecordConfig class file. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.ActiveRecord
- */
-
-Prado::using('System.Data.TDataSourceConfig');
-Prado::using('System.Data.ActiveRecord.TActiveRecordManager');
-
-/**
- * TActiveRecordConfig module configuration class.
- *
- * Database configuration for the default ActiveRecord manager instance.
- *
- * Example: application.xml configuration
- * <code>
- * <modules>
- * <module class="System.Data.ActiveRecord.TActiveRecordConfig" EnableCache="true">
- * <database ConnectionString="mysql:host=localhost;dbname=test"
- * Username="dbuser" Password="dbpass" />
- * </module>
- * </modules>
- * </code>
- *
- * MySQL database definition:
- * <code>
- * CREATE TABLE `blogs` (
- * `blog_id` int(10) unsigned NOT NULL auto_increment,
- * `blog_name` varchar(255) NOT NULL,
- * `blog_author` varchar(255) NOT NULL,
- * PRIMARY KEY (`blog_id`)
- * ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
- * </code>
- *
- * Record php class:
- * <code>
- * class Blogs extends TActiveRecord
- * {
- * public $blog_id;
- * public $blog_name;
- * public $blog_author;
- *
- * public static function finder($className=__CLASS__)
- * {
- * return parent::finder($className);
- * }
- * }
- * </code>
- *
- * Usage example:
- * <code>
- * class Home extends TPage
- * {
- * function onLoad($param)
- * {
- * $blogs = Blogs::finder()->findAll();
- * print_r($blogs);
- * }
- * }
- * </code>
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.ActiveRecord
- * @since 3.1
- */
-class TActiveRecordConfig extends TDataSourceConfig
-{
- const DEFAULT_MANAGER_CLASS = 'System.Data.ActiveRecord.TActiveRecordManager';
- const DEFAULT_GATEWAY_CLASS = 'System.Data.ActiveRecord.TActiveRecordGateway';
-
- /**
- * Defaults to {@link TActiveRecordConfig::DEFAULT_GATEWAY_CLASS DEFAULT_MANAGER_CLASS}
- * @var string
- */
- private $_managerClass = self::DEFAULT_MANAGER_CLASS;
-
- /**
- * Defaults to {@link TActiveRecordConfig::DEFAULT_GATEWAY_CLASS DEFAULT_GATEWAY_CLASS}
- * @var string
- */
- private $_gatewayClass = self::DEFAULT_GATEWAY_CLASS;
-
- /**
- * @var TActiveRecordManager
- */
- private $_manager = null;
-
- private $_enableCache=false;
-
- /**
- * Defaults to '{@link TActiveRecordInvalidFinderResult::Null Null}'
- *
- * @var TActiveRecordInvalidFinderResult
- * @since 3.1.5
- */
- private $_invalidFinderResult = TActiveRecordInvalidFinderResult::Null;
-
- /**
- * Initialize the active record manager.
- * @param TXmlDocument xml configuration.
- */
- public function init($xml)
- {
- parent::init($xml);
- $manager = $this -> getManager();
- if($this->getEnableCache())
- $manager->setCache($this->getApplication()->getCache());
- $manager->setDbConnection($this->getDbConnection());
- $manager->setInvalidFinderResult($this->getInvalidFinderResult());
- $manager->setGatewayClass($this->getGatewayClass());
- }
-
- /**
- * @return TActiveRecordManager
- */
- public function getManager() {
- if($this->_manager === null)
- $this->_manager = Prado::createComponent($this -> getManagerClass());
- return TActiveRecordManager::getInstance($this->_manager);
- }
-
- /**
- * Set implementation class of ActiveRecordManager
- * @param string $value
- */
- public function setManagerClass($value)
- {
- $this->_managerClass = TPropertyValue::ensureString($value);
- }
-
- /**
- * @return string the implementation class of ActiveRecordManager. Defaults to {@link TActiveRecordConfig::DEFAULT_GATEWAY_CLASS DEFAULT_MANAGER_CLASS}
- */
- public function getManagerClass()
- {
- return $this->_managerClass;
- }
-
- /**
- * Set implementation class of ActiveRecordGateway
- * @param string $value
- */
- public function setGatewayClass($value)
- {
- $this->_gatewayClass = TPropertyValue::ensureString($value);
- }
-
- /**
- * @return string the implementation class of ActiveRecordGateway. Defaults to {@link TActiveRecordConfig::DEFAULT_GATEWAY_CLASS DEFAULT_GATEWAY_CLASS}
- */
- public function getGatewayClass()
- {
- return $this->_gatewayClass;
- }
-
- /**
- * Set true to cache the table meta data.
- * @param boolean true to cache sqlmap instance.
- */
- public function setEnableCache($value)
- {
- $this->_enableCache = TPropertyValue::ensureBoolean($value);
- }
-
- /**
- * @return boolean true if table meta data should be cached, false otherwise.
- */
- public function getEnableCache()
- {
- return $this->_enableCache;
- }
-
- /**
- * @return TActiveRecordInvalidFinderResult Defaults to '{@link TActiveRecordInvalidFinderResult::Null Null}'.
- * @see setInvalidFinderResult
- * @since 3.1.5
- */
- public function getInvalidFinderResult()
- {
- return $this->_invalidFinderResult;
- }
-
- /**
- * Define the way an active record finder react if an invalid magic-finder invoked
- *
- * @param TActiveRecordInvalidFinderResult
- * @see getInvalidFinderResult
- * @since 3.1.5
- */
- public function setInvalidFinderResult($value)
- {
- $this->_invalidFinderResult = TPropertyValue::ensureEnum($value, 'TActiveRecordInvalidFinderResult');
- }
-}
+ * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.ActiveRecord + */ + +Prado::using('System.Data.TDataSourceConfig'); +Prado::using('System.Data.ActiveRecord.TActiveRecordManager'); + +/** + * TActiveRecordConfig module configuration class. + * + * Database configuration for the default ActiveRecord manager instance. + * + * Example: application.xml configuration + * <code> + * <modules> + * <module class="System.Data.ActiveRecord.TActiveRecordConfig" EnableCache="true"> + * <database ConnectionString="mysql:host=localhost;dbname=test" + * Username="dbuser" Password="dbpass" /> + * </module> + * </modules> + * </code> + * + * MySQL database definition: + * <code> + * CREATE TABLE `blogs` ( + * `blog_id` int(10) unsigned NOT NULL auto_increment, + * `blog_name` varchar(255) NOT NULL, + * `blog_author` varchar(255) NOT NULL, + * PRIMARY KEY (`blog_id`) + * ) ENGINE=InnoDB DEFAULT CHARSET=utf8; + * </code> + * + * Record php class: + * <code> + * class Blogs extends TActiveRecord + * { + * public $blog_id; + * public $blog_name; + * public $blog_author; + * + * public static function finder($className=__CLASS__) + * { + * return parent::finder($className); + * } + * } + * </code> + * + * Usage example: + * <code> + * class Home extends TPage + * { + * function onLoad($param) + * { + * $blogs = Blogs::finder()->findAll(); + * print_r($blogs); + * } + * } + * </code> + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.ActiveRecord + * @since 3.1 + */ +class TActiveRecordConfig extends TDataSourceConfig +{ + const DEFAULT_MANAGER_CLASS = 'System.Data.ActiveRecord.TActiveRecordManager'; + const DEFAULT_GATEWAY_CLASS = 'System.Data.ActiveRecord.TActiveRecordGateway'; + + /** + * Defaults to {@link TActiveRecordConfig::DEFAULT_GATEWAY_CLASS DEFAULT_MANAGER_CLASS} + * @var string + */ + private $_managerClass = self::DEFAULT_MANAGER_CLASS; + + /** + * Defaults to {@link TActiveRecordConfig::DEFAULT_GATEWAY_CLASS DEFAULT_GATEWAY_CLASS} + * @var string + */ + private $_gatewayClass = self::DEFAULT_GATEWAY_CLASS; + + /** + * @var TActiveRecordManager + */ + private $_manager = null; + + private $_enableCache=false; + + /** + * Defaults to '{@link TActiveRecordInvalidFinderResult::Null Null}' + * + * @var TActiveRecordInvalidFinderResult + * @since 3.1.5 + */ + private $_invalidFinderResult = TActiveRecordInvalidFinderResult::Null; + + /** + * Initialize the active record manager. + * @param TXmlDocument xml configuration. + */ + public function init($xml) + { + parent::init($xml); + $manager = $this -> getManager(); + if($this->getEnableCache()) + $manager->setCache($this->getApplication()->getCache()); + $manager->setDbConnection($this->getDbConnection()); + $manager->setInvalidFinderResult($this->getInvalidFinderResult()); + $manager->setGatewayClass($this->getGatewayClass()); + } + + /** + * @return TActiveRecordManager + */ + public function getManager() { + if($this->_manager === null) + $this->_manager = Prado::createComponent($this -> getManagerClass()); + return TActiveRecordManager::getInstance($this->_manager); + } + + /** + * Set implementation class of ActiveRecordManager + * @param string $value + */ + public function setManagerClass($value) + { + $this->_managerClass = TPropertyValue::ensureString($value); + } + + /** + * @return string the implementation class of ActiveRecordManager. Defaults to {@link TActiveRecordConfig::DEFAULT_GATEWAY_CLASS DEFAULT_MANAGER_CLASS} + */ + public function getManagerClass() + { + return $this->_managerClass; + } + + /** + * Set implementation class of ActiveRecordGateway + * @param string $value + */ + public function setGatewayClass($value) + { + $this->_gatewayClass = TPropertyValue::ensureString($value); + } + + /** + * @return string the implementation class of ActiveRecordGateway. Defaults to {@link TActiveRecordConfig::DEFAULT_GATEWAY_CLASS DEFAULT_GATEWAY_CLASS} + */ + public function getGatewayClass() + { + return $this->_gatewayClass; + } + + /** + * Set true to cache the table meta data. + * @param boolean true to cache sqlmap instance. + */ + public function setEnableCache($value) + { + $this->_enableCache = TPropertyValue::ensureBoolean($value); + } + + /** + * @return boolean true if table meta data should be cached, false otherwise. + */ + public function getEnableCache() + { + return $this->_enableCache; + } + + /** + * @return TActiveRecordInvalidFinderResult Defaults to '{@link TActiveRecordInvalidFinderResult::Null Null}'. + * @see setInvalidFinderResult + * @since 3.1.5 + */ + public function getInvalidFinderResult() + { + return $this->_invalidFinderResult; + } + + /** + * Define the way an active record finder react if an invalid magic-finder invoked + * + * @param TActiveRecordInvalidFinderResult + * @see getInvalidFinderResult + * @since 3.1.5 + */ + public function setInvalidFinderResult($value) + { + $this->_invalidFinderResult = TPropertyValue::ensureEnum($value, 'TActiveRecordInvalidFinderResult'); + } +} diff --git a/framework/Data/ActiveRecord/TActiveRecordCriteria.php b/framework/Data/ActiveRecord/TActiveRecordCriteria.php index d15f83ee..cdd7f964 100644 --- a/framework/Data/ActiveRecord/TActiveRecordCriteria.php +++ b/framework/Data/ActiveRecord/TActiveRecordCriteria.php @@ -1,39 +1,39 @@ <?php -/**
- * TActiveRecordCriteria class file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
+/** + * TActiveRecordCriteria class file. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.ActiveRecord
- */
-
-Prado::using('System.Data.DataGateway.TSqlCriteria');
-
-/**
- * Search criteria for Active Record.
- *
- * Criteria object for active record finder methods. Usage:
- * <code>
- * $criteria = new TActiveRecordCriteria;
- * $criteria->Condition = 'username = :name AND password = :pass';
- * $criteria->Parameters[':name'] = 'admin';
- * $criteria->Parameters[':pass'] = 'prado';
- * $criteria->OrdersBy['level'] = 'desc';
- * $criteria->OrdersBy['name'] = 'asc';
- * $criteria->Limit = 10;
- * $criteria->Offset = 20;
- * </code>
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.ActiveRecord
- * @since 3.1
- */
-class TActiveRecordCriteria extends TSqlCriteria
-{
-
-}
+ * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.ActiveRecord + */ + +Prado::using('System.Data.DataGateway.TSqlCriteria'); + +/** + * Search criteria for Active Record. + * + * Criteria object for active record finder methods. Usage: + * <code> + * $criteria = new TActiveRecordCriteria; + * $criteria->Condition = 'username = :name AND password = :pass'; + * $criteria->Parameters[':name'] = 'admin'; + * $criteria->Parameters[':pass'] = 'prado'; + * $criteria->OrdersBy['level'] = 'desc'; + * $criteria->OrdersBy['name'] = 'asc'; + * $criteria->Limit = 10; + * $criteria->Offset = 20; + * </code> + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.ActiveRecord + * @since 3.1 + */ +class TActiveRecordCriteria extends TSqlCriteria +{ + +} diff --git a/framework/Data/ActiveRecord/TActiveRecordManager.php b/framework/Data/ActiveRecord/TActiveRecordManager.php index 00979d1c..6cca76e7 100644 --- a/framework/Data/ActiveRecord/TActiveRecordManager.php +++ b/framework/Data/ActiveRecord/TActiveRecordManager.php @@ -1,163 +1,163 @@ -<?php
-/**
- * TActiveRecordManager class file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
+<?php +/** + * TActiveRecordManager class file. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.ActiveRecord
- */
-
-Prado::using('System.Data.TDbConnection');
-Prado::using('System.Data.ActiveRecord.TActiveRecord');
-Prado::using('System.Data.ActiveRecord.Exceptions.TActiveRecordException');
-Prado::using('System.Data.ActiveRecord.TActiveRecordGateway');
-
-/**
- * TActiveRecordManager provides the default DB connection,
- * default active record gateway, and table meta data inspector.
- *
- * The default connection can be set as follows:
- * <code>
- * TActiveRecordManager::getInstance()->setDbConnection($conn);
- * </code>
- * All new active record created after setting the
- * {@link DbConnection setDbConnection()} will use that connection unless
- * the custom ActiveRecord class overrides the ActiveRecord::getDbConnection().
- *
- * Set the {@link setCache Cache} property to an ICache object to allow
- * the active record gateway to cache the table meta data information.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.ActiveRecord
- * @since 3.1
- */
-class TActiveRecordManager extends TComponent
-{
- const DEFAULT_GATEWAY_CLASS = 'System.Data.ActiveRecord.TActiveRecordGateway';
-
- /**
- * Defaults to {@link TActiveRecordManager::DEFAULT_GATEWAY_CLASS DEFAULT_GATEWAY_CLASS}
- * @var string
- */
- private $_gatewayClass = self::DEFAULT_GATEWAY_CLASS;
-
- private $_gateway;
- private $_meta=array();
- private $_connection;
-
- private $_cache;
-
- /**
- * Defaults to '{@link TActiveRecordInvalidFinderResult::Null Null}'
- *
- * @var TActiveRecordInvalidFinderResult
- * @since 3.1.5
- */
- private $_invalidFinderResult = TActiveRecordInvalidFinderResult::Null;
-
- /**
- * @return ICache application cache.
- */
- public function getCache()
- {
- return $this->_cache;
- }
-
- /**
- * @param ICache application cache
- */
- public function setCache($value)
- {
- $this->_cache=$value;
- }
-
- /**
- * @param TDbConnection default database connection
- */
- public function setDbConnection($conn)
- {
- $this->_connection=$conn;
- }
-
- /**
- * @return TDbConnection default database connection
- */
- public function getDbConnection()
- {
- return $this->_connection;
- }
-
- /**
- * @return TActiveRecordManager static instance of record manager.
- */
- public static function getInstance($self=null)
- {
- static $instance;
- if($self!==null)
- $instance=$self;
- else if($instance===null)
- $instance = new self;
- return $instance;
- }
-
- /**
- * @return TActiveRecordGateway record gateway.
- */
- public function getRecordGateway()
- {
- if($this->_gateway === null) {
- $this->_gateway = $this->createRecordGateway();
- }
- return $this->_gateway;
- }
-
- /**
- * @return TActiveRecordGateway default record gateway.
- */
- protected function createRecordGateway()
- {
- return Prado::createComponent($this->getGatewayClass(), $this);
- }
-
- /**
- * Set implementation class of ActiveRecordGateway
- * @param string $value
- */
- public function setGatewayClass($value)
- {
- $this->_gatewayClass = (string)$value;
- }
-
- /**
- * @return string the implementation class of ActiveRecordGateway. Defaults to {@link TActiveRecordManager::DEFAULT_GATEWAY_CLASS DEFAULT_GATEWAY_CLASS}
- */
- public function getGatewayClass()
- {
- return $this->_gatewayClass;
- }
-
- /**
- * @return TActiveRecordInvalidFinderResult Defaults to '{@link TActiveRecordInvalidFinderResult::Null Null}'.
- * @since 3.1.5
- * @see setInvalidFinderResult
- */
- public function getInvalidFinderResult()
- {
- return $this->_invalidFinderResult;
- }
-
- /**
- * Define the way an active record finder react if an invalid magic-finder invoked
- * @param TActiveRecordInvalidFinderResult
- * @since 3.1.5
- * @see getInvalidFinderResult
- */
- public function setInvalidFinderResult($value)
- {
- $this->_invalidFinderResult = TPropertyValue::ensureEnum($value, 'TActiveRecordInvalidFinderResult');
- }
-}
+ * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.ActiveRecord + */ + +Prado::using('System.Data.TDbConnection'); +Prado::using('System.Data.ActiveRecord.TActiveRecord'); +Prado::using('System.Data.ActiveRecord.Exceptions.TActiveRecordException'); +Prado::using('System.Data.ActiveRecord.TActiveRecordGateway'); + +/** + * TActiveRecordManager provides the default DB connection, + * default active record gateway, and table meta data inspector. + * + * The default connection can be set as follows: + * <code> + * TActiveRecordManager::getInstance()->setDbConnection($conn); + * </code> + * All new active record created after setting the + * {@link DbConnection setDbConnection()} will use that connection unless + * the custom ActiveRecord class overrides the ActiveRecord::getDbConnection(). + * + * Set the {@link setCache Cache} property to an ICache object to allow + * the active record gateway to cache the table meta data information. + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.ActiveRecord + * @since 3.1 + */ +class TActiveRecordManager extends TComponent +{ + const DEFAULT_GATEWAY_CLASS = 'System.Data.ActiveRecord.TActiveRecordGateway'; + + /** + * Defaults to {@link TActiveRecordManager::DEFAULT_GATEWAY_CLASS DEFAULT_GATEWAY_CLASS} + * @var string + */ + private $_gatewayClass = self::DEFAULT_GATEWAY_CLASS; + + private $_gateway; + private $_meta=array(); + private $_connection; + + private $_cache; + + /** + * Defaults to '{@link TActiveRecordInvalidFinderResult::Null Null}' + * + * @var TActiveRecordInvalidFinderResult + * @since 3.1.5 + */ + private $_invalidFinderResult = TActiveRecordInvalidFinderResult::Null; + + /** + * @return ICache application cache. + */ + public function getCache() + { + return $this->_cache; + } + + /** + * @param ICache application cache + */ + public function setCache($value) + { + $this->_cache=$value; + } + + /** + * @param TDbConnection default database connection + */ + public function setDbConnection($conn) + { + $this->_connection=$conn; + } + + /** + * @return TDbConnection default database connection + */ + public function getDbConnection() + { + return $this->_connection; + } + + /** + * @return TActiveRecordManager static instance of record manager. + */ + public static function getInstance($self=null) + { + static $instance; + if($self!==null) + $instance=$self; + else if($instance===null) + $instance = new self; + return $instance; + } + + /** + * @return TActiveRecordGateway record gateway. + */ + public function getRecordGateway() + { + if($this->_gateway === null) { + $this->_gateway = $this->createRecordGateway(); + } + return $this->_gateway; + } + + /** + * @return TActiveRecordGateway default record gateway. + */ + protected function createRecordGateway() + { + return Prado::createComponent($this->getGatewayClass(), $this); + } + + /** + * Set implementation class of ActiveRecordGateway + * @param string $value + */ + public function setGatewayClass($value) + { + $this->_gatewayClass = (string)$value; + } + + /** + * @return string the implementation class of ActiveRecordGateway. Defaults to {@link TActiveRecordManager::DEFAULT_GATEWAY_CLASS DEFAULT_GATEWAY_CLASS} + */ + public function getGatewayClass() + { + return $this->_gatewayClass; + } + + /** + * @return TActiveRecordInvalidFinderResult Defaults to '{@link TActiveRecordInvalidFinderResult::Null Null}'. + * @since 3.1.5 + * @see setInvalidFinderResult + */ + public function getInvalidFinderResult() + { + return $this->_invalidFinderResult; + } + + /** + * Define the way an active record finder react if an invalid magic-finder invoked + * @param TActiveRecordInvalidFinderResult + * @since 3.1.5 + * @see getInvalidFinderResult + */ + public function setInvalidFinderResult($value) + { + $this->_invalidFinderResult = TPropertyValue::ensureEnum($value, 'TActiveRecordInvalidFinderResult'); + } +} diff --git a/framework/Data/Common/Mssql/TMssqlCommandBuilder.php b/framework/Data/Common/Mssql/TMssqlCommandBuilder.php index 8a1e4dd6..3485adaa 100644 --- a/framework/Data/Common/Mssql/TMssqlCommandBuilder.php +++ b/framework/Data/Common/Mssql/TMssqlCommandBuilder.php @@ -1,173 +1,173 @@ -<?php
-/**
- * TMsssqlCommandBuilder class file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2012 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');
-
-/**
- * TMssqlCommandBuilder provides specifics methods to create limit/offset query commands
- * for MSSQL servers.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id: TDbCommandBuilder.php 1863 2007-04-12 12:43:49Z wei $
- * @package System.Data.Common
- * @since 3.1
- */
-class TMssqlCommandBuilder extends TDbCommandBuilder
-{
- /**
- * Overrides parent implementation. Uses "SELECT @@Identity".
- * @return integer last insert id, null if none is found.
- */
- public function getLastInsertID()
- {
- foreach($this->getTableInfo()->getColumns() as $column)
- {
- if($column->hasSequence())
- {
- $command = $this->getDbConnection()->createCommand('SELECT @@Identity');
- return intval($command->queryScalar());
- }
- }
- }
-
- /**
- * Overrides parent implementation. Alters the sql to apply $limit and $offset.
- * The idea for limit with offset is done by modifying the sql on the fly
- * with numerous assumptions on the structure of the sql string.
- * The modification is done with reference to the notes from
- * http://troels.arvin.dk/db/rdbms/#select-limit-offset
- *
- * <code>
- * SELECT * FROM (
- * SELECT TOP n * FROM (
- * SELECT TOP z columns -- (z=n+skip)
- * FROM tablename
- * ORDER BY key ASC
- * ) AS FOO ORDER BY key DESC -- ('FOO' may be anything)
- * ) AS BAR ORDER BY key ASC -- ('BAR' may be anything)
- * </code>
- *
- * <b>Regular expressions are used to alter the SQL query. The resulting SQL query
- * may be malformed for complex queries.</b> The following restrictions apply
- *
- * <ul>
- * <li>
- * In particular, <b>commas</b> should <b>NOT</b>
- * be used as part of the ordering expression or identifier. Commas must only be
- * used for separating the ordering clauses.
- * </li>
- * <li>
- * In the ORDER BY clause, the column name should NOT be be qualified
- * with a table name or view name. Alias the column names or use column index.
- * </li>
- * <li>
- * No clauses should follow the ORDER BY clause, e.g. no COMPUTE or FOR clauses.
- * </li>
- * </ul>
- *
- * @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;
- }
-}
-
+<?php +/** + * TMsssqlCommandBuilder class file. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 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'); + +/** + * TMssqlCommandBuilder provides specifics methods to create limit/offset query commands + * for MSSQL servers. + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id: TDbCommandBuilder.php 1863 2007-04-12 12:43:49Z wei $ + * @package System.Data.Common + * @since 3.1 + */ +class TMssqlCommandBuilder extends TDbCommandBuilder +{ + /** + * Overrides parent implementation. Uses "SELECT @@Identity". + * @return integer last insert id, null if none is found. + */ + public function getLastInsertID() + { + foreach($this->getTableInfo()->getColumns() as $column) + { + if($column->hasSequence()) + { + $command = $this->getDbConnection()->createCommand('SELECT @@Identity'); + return intval($command->queryScalar()); + } + } + } + + /** + * Overrides parent implementation. Alters the sql to apply $limit and $offset. + * The idea for limit with offset is done by modifying the sql on the fly + * with numerous assumptions on the structure of the sql string. + * The modification is done with reference to the notes from + * http://troels.arvin.dk/db/rdbms/#select-limit-offset + * + * <code> + * SELECT * FROM ( + * SELECT TOP n * FROM ( + * SELECT TOP z columns -- (z=n+skip) + * FROM tablename + * ORDER BY key ASC + * ) AS FOO ORDER BY key DESC -- ('FOO' may be anything) + * ) AS BAR ORDER BY key ASC -- ('BAR' may be anything) + * </code> + * + * <b>Regular expressions are used to alter the SQL query. The resulting SQL query + * may be malformed for complex queries.</b> The following restrictions apply + * + * <ul> + * <li> + * In particular, <b>commas</b> should <b>NOT</b> + * be used as part of the ordering expression or identifier. Commas must only be + * used for separating the ordering clauses. + * </li> + * <li> + * In the ORDER BY clause, the column name should NOT be be qualified + * with a table name or view name. Alias the column names or use column index. + * </li> + * <li> + * No clauses should follow the ORDER BY clause, e.g. no COMPUTE or FOR clauses. + * </li> + * </ul> + * + * @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; + } +} + diff --git a/framework/Data/Common/Mssql/TMssqlTableColumn.php b/framework/Data/Common/Mssql/TMssqlTableColumn.php index 5621bb6b..811d0f6e 100644 --- a/framework/Data/Common/Mssql/TMssqlTableColumn.php +++ b/framework/Data/Common/Mssql/TMssqlTableColumn.php @@ -1,64 +1,64 @@ -<?php
-/**
- * TMssqlTableColumn class file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
+<?php +/** + * TMssqlTableColumn class file. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/
- * @version $Id: TMssqlTableColumn.php 1863 2007-04-12 12:43:49Z wei $
- * @package System.Data.Common.Mssql
- */
-
-/**
- * Load common TDbTableCommon class.
- */
-Prado::using('System.Data.Common.TDbTableColumn');
-
-/**
- * Describes the column metadata of the schema for a Mssql database table.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id: TMssqlTableColumn.php 1863 2007-04-12 12:43:49Z wei $
- * @package System.Data.Common.Mssql
- * @since 3.1
- */
-class TMssqlTableColumn extends TDbTableColumn
-{
- private static $types = array();
-
- /**
- * Overrides parent implementation, returns PHP type from the db type.
- * @return boolean derived PHP primitive type from the column db type.
- */
- public function getPHPType()
- {
-
- return 'string';
- }
-
- /**
- * @return boolean true if the column has identity (auto-increment)
- */
- public function getAutoIncrement()
- {
- return $this->getInfo('AutoIncrement',false);
- }
-
- /**
- * @return boolean true if auto increments.
- */
- public function hasSequence()
- {
- return $this->getAutoIncrement();
- }
-
- /**
- * @return boolean true if db type is 'timestamp'.
- */
- public function getIsExcluded()
- {
- return strtolower($this->getDbType())==='timestamp';
- }
-}
-
+ * @license http://www.pradosoft.com/license/ + * @version $Id: TMssqlTableColumn.php 1863 2007-04-12 12:43:49Z wei $ + * @package System.Data.Common.Mssql + */ + +/** + * Load common TDbTableCommon class. + */ +Prado::using('System.Data.Common.TDbTableColumn'); + +/** + * Describes the column metadata of the schema for a Mssql database table. + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id: TMssqlTableColumn.php 1863 2007-04-12 12:43:49Z wei $ + * @package System.Data.Common.Mssql + * @since 3.1 + */ +class TMssqlTableColumn extends TDbTableColumn +{ + private static $types = array(); + + /** + * Overrides parent implementation, returns PHP type from the db type. + * @return boolean derived PHP primitive type from the column db type. + */ + public function getPHPType() + { + + return 'string'; + } + + /** + * @return boolean true if the column has identity (auto-increment) + */ + public function getAutoIncrement() + { + return $this->getInfo('AutoIncrement',false); + } + + /** + * @return boolean true if auto increments. + */ + public function hasSequence() + { + return $this->getAutoIncrement(); + } + + /** + * @return boolean true if db type is 'timestamp'. + */ + public function getIsExcluded() + { + return strtolower($this->getDbType())==='timestamp'; + } +} + diff --git a/framework/Data/Common/Mssql/TMssqlTableInfo.php b/framework/Data/Common/Mssql/TMssqlTableInfo.php index c65e3eaa..3b48e42d 100644 --- a/framework/Data/Common/Mssql/TMssqlTableInfo.php +++ b/framework/Data/Common/Mssql/TMssqlTableInfo.php @@ -1,64 +1,64 @@ -<?php
-/**
- * TMssqlTableInfo class file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
+<?php +/** + * TMssqlTableInfo class file. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/
- * @version $Id: TMssqlTableInfo.php 1861 2007-04-12 08:05:03Z wei $
- * @package System.Data.Common.Mssql
- */
-
-/**
- * Loads the base TDbTableInfo class and TMssqlTableColumn class.
- */
-Prado::using('System.Data.Common.TDbTableInfo');
-Prado::using('System.Data.Common.Mssql.TMssqlTableColumn');
-
-/**
- * TMssqlTableInfo class provides additional table information for Mssql database.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id: TMssqlTableInfo.php 1861 2007-04-12 08:05:03Z wei $
- * @package System.Data.Common.Mssql
- * @since 3.1
- */
-class TMssqlTableInfo extends TDbTableInfo
-{
- /**
- * @return string name of the schema this column belongs to.
- */
- public function getSchemaName()
- {
- return $this->getInfo('SchemaName');
- }
-
- /**
- * @return string catalog name (database name)
- */
- public function getCatalogName()
- {
- return $this->getInfo('CatalogName');
- }
-
- /**
- * @return string full name of the table, database dependent.
- */
- public function getTableFullName()
- {
- //MSSQL alway returns the catalog, schem and table names.
- 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);
- }
-}
-
+ * @license http://www.pradosoft.com/license/ + * @version $Id: TMssqlTableInfo.php 1861 2007-04-12 08:05:03Z wei $ + * @package System.Data.Common.Mssql + */ + +/** + * Loads the base TDbTableInfo class and TMssqlTableColumn class. + */ +Prado::using('System.Data.Common.TDbTableInfo'); +Prado::using('System.Data.Common.Mssql.TMssqlTableColumn'); + +/** + * TMssqlTableInfo class provides additional table information for Mssql database. + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id: TMssqlTableInfo.php 1861 2007-04-12 08:05:03Z wei $ + * @package System.Data.Common.Mssql + * @since 3.1 + */ +class TMssqlTableInfo extends TDbTableInfo +{ + /** + * @return string name of the schema this column belongs to. + */ + public function getSchemaName() + { + return $this->getInfo('SchemaName'); + } + + /** + * @return string catalog name (database name) + */ + public function getCatalogName() + { + return $this->getInfo('CatalogName'); + } + + /** + * @return string full name of the table, database dependent. + */ + public function getTableFullName() + { + //MSSQL alway returns the catalog, schem and table names. + 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); + } +} + diff --git a/framework/Data/Common/Mysql/TMysqlCommandBuilder.php b/framework/Data/Common/Mysql/TMysqlCommandBuilder.php index fa2b2c51..58854bbc 100644 --- a/framework/Data/Common/Mysql/TMysqlCommandBuilder.php +++ b/framework/Data/Common/Mysql/TMysqlCommandBuilder.php @@ -1,26 +1,26 @@ -<?php
-/**
- * TMysqlCommandBuilder class file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
+<?php +/** + * TMysqlCommandBuilder class file. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 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');
-
-/**
- * TMysqlCommandBuilder implements default TDbCommandBuilder
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id: TDbCommandBuilder.php 1863 2007-04-12 12:43:49Z wei $
- * @package System.Data.Common
- * @since 3.1
- */
-class TMysqlCommandBuilder extends TDbCommandBuilder
-{
-}
-
+ * @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'); + +/** + * TMysqlCommandBuilder implements default TDbCommandBuilder + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id: TDbCommandBuilder.php 1863 2007-04-12 12:43:49Z wei $ + * @package System.Data.Common + * @since 3.1 + */ +class TMysqlCommandBuilder extends TDbCommandBuilder +{ +} + diff --git a/framework/Data/Common/Mysql/TMysqlMetaData.php b/framework/Data/Common/Mysql/TMysqlMetaData.php index f9824015..151111af 100644 --- a/framework/Data/Common/Mysql/TMysqlMetaData.php +++ b/framework/Data/Common/Mysql/TMysqlMetaData.php @@ -1,386 +1,386 @@ -<?php
-/**
- * TMysqlMetaData class file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.Common.Mysql
- */
-
-/**
- * Load the base TDbMetaData class.
- */
-Prado::using('System.Data.Common.TDbMetaData');
-Prado::using('System.Data.Common.Mysql.TMysqlTableInfo');
-
-/**
- * TMysqlMetaData loads Mysql version 4.1.x and 5.x database table and column information.
- *
- * For Mysql version 4.1.x, PHP 5.1.3 or later is required.
- * See http://netevil.org/node.php?nid=795&SC=1
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.Common.Mysql
- * @since 3.1
- */
-class TMysqlMetaData extends TDbMetaData
-{
- private $_serverVersion=0;
-
- /**
- * @return string TDbTableInfo class name.
- */
- protected function getTableInfoClass()
- {
- return 'TMysqlTableInfo';
- }
-
- /**
- * Quotes a table name for use in a query.
- * @param string $name table name
- * @return string the properly quoted table name
- */
- public function quoteTableName($name)
- {
- return parent::quoteTableName($name, '`', '`');
- }
-
- /**
- * Quotes a column name for use in a query.
- * @param string $name column name
- * @return string the properly quoted column name
- */
- public function quoteColumnName($name)
- {
- return parent::quoteColumnName($name, '`', '`');
- }
-
- /**
- * Quotes a column alias for use in a query.
- * @param string $name column alias
- * @return string the properly quoted column alias
- */
- public function quoteColumnAlias($name)
- {
- return parent::quoteColumnAlias($name, '`', '`');
- }
-
- /**
- * Get the column definitions for given table.
- * @param string table name.
- * @return TMysqlTableInfo table information.
- */
- protected function createTableInfo($table)
- {
- list($schemaName,$tableName) = $this->getSchemaTableName($table);
- $find = $schemaName===null ? "`{$tableName}`" : "`{$schemaName}`.`{$tableName}`";
- $this->getDbConnection()->setActive(true);
- $sql = "SHOW FULL FIELDS FROM {$find}";
- $command = $this->getDbConnection()->createCommand($sql);
- $tableInfo = $this->createNewTableInfo($table);
- $index=0;
- foreach($command->query() as $col)
- {
- $col['index'] = $index++;
- $this->processColumn($tableInfo,$col);
- }
- if($index===0)
- throw new TDbException('dbmetadata_invalid_table_view', $table);
- return $tableInfo;
- }
-
- /**
- * @return float server version.
- */
- protected function getServerVersion()
- {
- if(!$this->_serverVersion)
- {
- $version = $this->getDbConnection()->getAttribute(PDO::ATTR_SERVER_VERSION);
- $digits=array();
- preg_match('/(\d+)\.(\d+)\.(\d+)/', $version, $digits);
- $this->_serverVersion=floatval($digits[1].'.'.$digits[2].$digits[3]);
- }
- return $this->_serverVersion;
- }
-
- /**
- * @param TMysqlTableInfo table information.
- * @param array column information.
- */
- protected function processColumn($tableInfo, $col)
- {
- $columnId = $col['Field'];
-
- $info['ColumnName'] = "`$columnId`"; //quote the column names!
- $info['ColumnId'] = $columnId;
- $info['ColumnIndex'] = $col['index'];
- if($col['Null']==='YES')
- $info['AllowNull'] = true;
- if(is_int(strpos(strtolower($col['Extra']), 'auto_increment')))
- $info['AutoIncrement']=true;
- if($col['Default']!=="")
- $info['DefaultValue'] = $col['Default'];
-
- if($col['Key']==='PRI' || in_array($columnId, $tableInfo->getPrimaryKeys()))
- $info['IsPrimaryKey'] = true;
- if($this->isForeignKeyColumn($columnId, $tableInfo))
- $info['IsForeignKey'] = true;
-
- $info['DbType'] = $col['Type'];
- $match=array();
- //find SET/ENUM values, column size, precision, and scale
- if(preg_match('/\((.*)\)/', $col['Type'], $match))
- {
- $info['DbType']= preg_replace('/\(.*\)/', '', $col['Type']);
-
- //find SET/ENUM values
- if($this->isEnumSetType($info['DbType']))
- $info['DbTypeValues'] = preg_split("/[',]/S", $match[1], -1, PREG_SPLIT_NO_EMPTY);
-
- //find column size, precision and scale
- $pscale = array();
- if(preg_match('/(\d+)(?:,(\d+))?+/', $match[1], $pscale))
- {
- if($this->isPrecisionType($info['DbType']))
- {
- $info['NumericPrecision'] = intval($pscale[1]);
- if(count($pscale) > 2)
- $info['NumericScale'] = intval($pscale[2]);
- }
- else
- $info['ColumnSize'] = intval($pscale[1]);
- }
- }
-
- $tableInfo->Columns[$columnId] = new TMysqlTableColumn($info);
- }
-
- /**
- * @return boolean true if column type if "numeric", "interval" or begins with "time".
- */
- protected function isPrecisionType($type)
- {
- $type = strtolower(trim($type));
- return $type==='decimal' || $type==='dec'
- || $type==='float' || $type==='double'
- || $type==='double precision' || $type==='real';
- }
-
- /**
- * @return boolean true if column type if "enum" or "set".
- */
- protected function isEnumSetType($type)
- {
- $type = strtolower(trim($type));
- return $type==='set' || $type==='enum';
- }
-
- /**
- * @param string table name, may be quoted with back-ticks and may contain database name.
- * @return array tuple ($schema,$table), $schema may be null.
- * @throws TDbException when table name contains invalid identifier bytes.
- */
- protected function getSchemaTableName($table)
- {
- //remove the back ticks and separate out the "database.table"
- $result = explode('.', str_replace('`', '', $table));
- foreach($result as $name)
- {
- if(!$this->isValidIdentifier($name))
- {
- $ref = 'http://dev.mysql.com/doc/refman/5.0/en/identifiers.html';
- throw new TDbException('dbcommon_invalid_identifier_name', $table, $ref);
- }
- }
- return count($result) > 1 ? $result : array(null, $result[0]);
- }
-
- /**
- * http://dev.mysql.com/doc/refman/5.0/en/identifiers.html
- * @param string identifier name
- * @param boolean true if valid identifier.
- */
- protected function isValidIdentifier($name)
- {
- return !preg_match('#/|\\|.|\x00|\xFF#', $name);
- }
-
- /**
- * @param string table schema name
- * @param string table name.
- * @return TMysqlTableInfo
- */
- protected function createNewTableInfo($table)
- {
- list($schemaName,$tableName) = $this->getSchemaTableName($table);
- $info['SchemaName'] = $schemaName;
- $info['TableName'] = $tableName;
- if($this->getIsView($schemaName,$tableName))
- $info['IsView'] = true;
- list($primary, $foreign) = $this->getConstraintKeys($schemaName, $tableName);
- $class = $this->getTableInfoClass();
- return new $class($info,$primary,$foreign);
- }
-
- /**
- * For MySQL version 5.0.1 or later we can use SHOW FULL TABLES
- * http://dev.mysql.com/doc/refman/5.0/en/show-tables.html
- *
- * For MySQL version 5.0.1 or ealier, this always return false.
- * @param string database name, null to use default connection database.
- * @param string table or view name.
- * @return boolean true if is view, false otherwise.
- * @throws TDbException if table or view does not exist.
- */
- protected function getIsView($schemaName,$tableName)
- {
- if($this->getServerVersion()<5.01)
- return false;
- if($schemaName!==null)
- $sql = "SHOW FULL TABLES FROM `{$schemaName}` LIKE :table";
- else
- $sql = "SHOW FULL TABLES LIKE :table";
-
- $command = $this->getDbConnection()->createCommand($sql);
- $command->bindValue(':table', $tableName);
- try
- {
- return count($result = $command->queryRow()) > 0 && $result['Table_type']==='VIEW';
- }
- catch(TDbException $e)
- {
- $table = $schemaName===null?$tableName:$schemaName.'.'.$tableName;
- throw new TDbException('dbcommon_invalid_table_name',$table,$e->getMessage());
- }
- }
-
- /**
- * 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($schemaName, $tableName)
- {
- $table = $schemaName===null ? "`{$tableName}`" : "`{$schemaName}`.`{$tableName}`";
- $sql = "SHOW INDEX FROM {$table}";
- $command = $this->getDbConnection()->createCommand($sql);
- $primary = array();
- foreach($command->query() as $row)
- {
- if($row['Key_name']==='PRIMARY')
- $primary[] = $row['Column_name'];
- }
- // MySQL version was increased to >=5.1.21 instead of 5.x
- // due to a MySQL bug (http://bugs.mysql.com/bug.php?id=19588)
- if($this->getServerVersion() >= 5.121)
- $foreign = $this->getForeignConstraints($schemaName,$tableName);
- else
- $foreign = $this->findForeignConstraints($schemaName,$tableName);
- 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($schemaName, $tableName)
- {
- $andSchema = $schemaName !== null ? 'AND TABLE_SCHEMA LIKE :schema' : 'AND TABLE_SCHEMA LIKE DATABASE()';
- $sql = <<<EOD
- SELECT
- CONSTRAINT_NAME as con,
- COLUMN_NAME as col,
- REFERENCED_TABLE_SCHEMA as fkschema,
- REFERENCED_TABLE_NAME as fktable,
- REFERENCED_COLUMN_NAME as fkcol
- FROM
- `INFORMATION_SCHEMA`.`KEY_COLUMN_USAGE`
- WHERE
- REFERENCED_TABLE_NAME IS NOT NULL
- AND TABLE_NAME LIKE :table
- $andSchema
-EOD;
- $command = $this->getDbConnection()->createCommand($sql);
- $command->bindValue(':table', $tableName);
- if($schemaName!==null)
- $command->bindValue(':schema', $schemaName);
- $fkeys=array();
- foreach($command->query() as $col)
- {
- $fkeys[$col['con']]['keys'][$col['col']] = $col['fkcol'];
- $fkeys[$col['con']]['table'] = $col['fktable'];
- }
- return count($fkeys) > 0 ? array_values($fkeys) : $fkeys;
- }
-
- /**
- * @param string database name
- * @param string table name
- * @return string SQL command to create the table.
- * @throws TDbException if PHP version is less than 5.1.3
- */
- protected function getShowCreateTable($schemaName, $tableName)
- {
- if(version_compare(PHP_VERSION,'5.1.3','<'))
- throw new TDbException('dbmetadata_requires_php_version', 'Mysql 4.1.x', '5.1.3');
-
- //See http://netevil.org/node.php?nid=795&SC=1
- $this->getDbConnection()->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
- if($schemaName!==null)
- $sql = "SHOW CREATE TABLE `{$schemaName}`.`{$tableName}`";
- else
- $sql = "SHOW CREATE TABLE `{$tableName}`";
- $command = $this->getDbConnection()->createCommand($sql);
- $result = $command->queryRow();
- return isset($result['Create Table']) ? $result['Create Table'] : (isset($result['Create View']) ? $result['Create View'] : '');
- }
-
- /**
- * Extract foreign key constraints by extracting the contraints from SHOW CREATE TABLE result.
- * @param string database name
- * @param string table name
- * @return array foreign relationship table name and keys.
- */
- protected function findForeignConstraints($schemaName, $tableName)
- {
- $sql = $this->getShowCreateTable($schemaName, $tableName);
- $matches =array();
- $regexp = '/FOREIGN KEY\s+\(([^\)]+)\)\s+REFERENCES\s+`?([^`]+)`?\s\(([^\)]+)\)/mi';
- preg_match_all($regexp,$sql,$matches,PREG_SET_ORDER);
- $foreign = array();
- foreach($matches as $match)
- {
- $fields = array_map('trim',explode(',',str_replace('`','',$match[1])));
- $fk_fields = array_map('trim',explode(',',str_replace('`','',$match[3])));
- $keys=array();
- foreach($fields as $k=>$v)
- $keys[$v] = $fk_fields[$k];
- $foreign[] = array('keys' => $keys, 'table' => trim($match[2]));
- }
- return $foreign;
- }
-
- /**
- * @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;
- }
-}
-
+<?php +/** + * TMysqlMetaData class file. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.Common.Mysql + */ + +/** + * Load the base TDbMetaData class. + */ +Prado::using('System.Data.Common.TDbMetaData'); +Prado::using('System.Data.Common.Mysql.TMysqlTableInfo'); + +/** + * TMysqlMetaData loads Mysql version 4.1.x and 5.x database table and column information. + * + * For Mysql version 4.1.x, PHP 5.1.3 or later is required. + * See http://netevil.org/node.php?nid=795&SC=1 + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.Common.Mysql + * @since 3.1 + */ +class TMysqlMetaData extends TDbMetaData +{ + private $_serverVersion=0; + + /** + * @return string TDbTableInfo class name. + */ + protected function getTableInfoClass() + { + return 'TMysqlTableInfo'; + } + + /** + * Quotes a table name for use in a query. + * @param string $name table name + * @return string the properly quoted table name + */ + public function quoteTableName($name) + { + return parent::quoteTableName($name, '`', '`'); + } + + /** + * Quotes a column name for use in a query. + * @param string $name column name + * @return string the properly quoted column name + */ + public function quoteColumnName($name) + { + return parent::quoteColumnName($name, '`', '`'); + } + + /** + * Quotes a column alias for use in a query. + * @param string $name column alias + * @return string the properly quoted column alias + */ + public function quoteColumnAlias($name) + { + return parent::quoteColumnAlias($name, '`', '`'); + } + + /** + * Get the column definitions for given table. + * @param string table name. + * @return TMysqlTableInfo table information. + */ + protected function createTableInfo($table) + { + list($schemaName,$tableName) = $this->getSchemaTableName($table); + $find = $schemaName===null ? "`{$tableName}`" : "`{$schemaName}`.`{$tableName}`"; + $this->getDbConnection()->setActive(true); + $sql = "SHOW FULL FIELDS FROM {$find}"; + $command = $this->getDbConnection()->createCommand($sql); + $tableInfo = $this->createNewTableInfo($table); + $index=0; + foreach($command->query() as $col) + { + $col['index'] = $index++; + $this->processColumn($tableInfo,$col); + } + if($index===0) + throw new TDbException('dbmetadata_invalid_table_view', $table); + return $tableInfo; + } + + /** + * @return float server version. + */ + protected function getServerVersion() + { + if(!$this->_serverVersion) + { + $version = $this->getDbConnection()->getAttribute(PDO::ATTR_SERVER_VERSION); + $digits=array(); + preg_match('/(\d+)\.(\d+)\.(\d+)/', $version, $digits); + $this->_serverVersion=floatval($digits[1].'.'.$digits[2].$digits[3]); + } + return $this->_serverVersion; + } + + /** + * @param TMysqlTableInfo table information. + * @param array column information. + */ + protected function processColumn($tableInfo, $col) + { + $columnId = $col['Field']; + + $info['ColumnName'] = "`$columnId`"; //quote the column names! + $info['ColumnId'] = $columnId; + $info['ColumnIndex'] = $col['index']; + if($col['Null']==='YES') + $info['AllowNull'] = true; + if(is_int(strpos(strtolower($col['Extra']), 'auto_increment'))) + $info['AutoIncrement']=true; + if($col['Default']!=="") + $info['DefaultValue'] = $col['Default']; + + if($col['Key']==='PRI' || in_array($columnId, $tableInfo->getPrimaryKeys())) + $info['IsPrimaryKey'] = true; + if($this->isForeignKeyColumn($columnId, $tableInfo)) + $info['IsForeignKey'] = true; + + $info['DbType'] = $col['Type']; + $match=array(); + //find SET/ENUM values, column size, precision, and scale + if(preg_match('/\((.*)\)/', $col['Type'], $match)) + { + $info['DbType']= preg_replace('/\(.*\)/', '', $col['Type']); + + //find SET/ENUM values + if($this->isEnumSetType($info['DbType'])) + $info['DbTypeValues'] = preg_split("/[',]/S", $match[1], -1, PREG_SPLIT_NO_EMPTY); + + //find column size, precision and scale + $pscale = array(); + if(preg_match('/(\d+)(?:,(\d+))?+/', $match[1], $pscale)) + { + if($this->isPrecisionType($info['DbType'])) + { + $info['NumericPrecision'] = intval($pscale[1]); + if(count($pscale) > 2) + $info['NumericScale'] = intval($pscale[2]); + } + else + $info['ColumnSize'] = intval($pscale[1]); + } + } + + $tableInfo->Columns[$columnId] = new TMysqlTableColumn($info); + } + + /** + * @return boolean true if column type if "numeric", "interval" or begins with "time". + */ + protected function isPrecisionType($type) + { + $type = strtolower(trim($type)); + return $type==='decimal' || $type==='dec' + || $type==='float' || $type==='double' + || $type==='double precision' || $type==='real'; + } + + /** + * @return boolean true if column type if "enum" or "set". + */ + protected function isEnumSetType($type) + { + $type = strtolower(trim($type)); + return $type==='set' || $type==='enum'; + } + + /** + * @param string table name, may be quoted with back-ticks and may contain database name. + * @return array tuple ($schema,$table), $schema may be null. + * @throws TDbException when table name contains invalid identifier bytes. + */ + protected function getSchemaTableName($table) + { + //remove the back ticks and separate out the "database.table" + $result = explode('.', str_replace('`', '', $table)); + foreach($result as $name) + { + if(!$this->isValidIdentifier($name)) + { + $ref = 'http://dev.mysql.com/doc/refman/5.0/en/identifiers.html'; + throw new TDbException('dbcommon_invalid_identifier_name', $table, $ref); + } + } + return count($result) > 1 ? $result : array(null, $result[0]); + } + + /** + * http://dev.mysql.com/doc/refman/5.0/en/identifiers.html + * @param string identifier name + * @param boolean true if valid identifier. + */ + protected function isValidIdentifier($name) + { + return !preg_match('#/|\\|.|\x00|\xFF#', $name); + } + + /** + * @param string table schema name + * @param string table name. + * @return TMysqlTableInfo + */ + protected function createNewTableInfo($table) + { + list($schemaName,$tableName) = $this->getSchemaTableName($table); + $info['SchemaName'] = $schemaName; + $info['TableName'] = $tableName; + if($this->getIsView($schemaName,$tableName)) + $info['IsView'] = true; + list($primary, $foreign) = $this->getConstraintKeys($schemaName, $tableName); + $class = $this->getTableInfoClass(); + return new $class($info,$primary,$foreign); + } + + /** + * For MySQL version 5.0.1 or later we can use SHOW FULL TABLES + * http://dev.mysql.com/doc/refman/5.0/en/show-tables.html + * + * For MySQL version 5.0.1 or ealier, this always return false. + * @param string database name, null to use default connection database. + * @param string table or view name. + * @return boolean true if is view, false otherwise. + * @throws TDbException if table or view does not exist. + */ + protected function getIsView($schemaName,$tableName) + { + if($this->getServerVersion()<5.01) + return false; + if($schemaName!==null) + $sql = "SHOW FULL TABLES FROM `{$schemaName}` LIKE :table"; + else + $sql = "SHOW FULL TABLES LIKE :table"; + + $command = $this->getDbConnection()->createCommand($sql); + $command->bindValue(':table', $tableName); + try + { + return count($result = $command->queryRow()) > 0 && $result['Table_type']==='VIEW'; + } + catch(TDbException $e) + { + $table = $schemaName===null?$tableName:$schemaName.'.'.$tableName; + throw new TDbException('dbcommon_invalid_table_name',$table,$e->getMessage()); + } + } + + /** + * 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($schemaName, $tableName) + { + $table = $schemaName===null ? "`{$tableName}`" : "`{$schemaName}`.`{$tableName}`"; + $sql = "SHOW INDEX FROM {$table}"; + $command = $this->getDbConnection()->createCommand($sql); + $primary = array(); + foreach($command->query() as $row) + { + if($row['Key_name']==='PRIMARY') + $primary[] = $row['Column_name']; + } + // MySQL version was increased to >=5.1.21 instead of 5.x + // due to a MySQL bug (http://bugs.mysql.com/bug.php?id=19588) + if($this->getServerVersion() >= 5.121) + $foreign = $this->getForeignConstraints($schemaName,$tableName); + else + $foreign = $this->findForeignConstraints($schemaName,$tableName); + 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($schemaName, $tableName) + { + $andSchema = $schemaName !== null ? 'AND TABLE_SCHEMA LIKE :schema' : 'AND TABLE_SCHEMA LIKE DATABASE()'; + $sql = <<<EOD + SELECT + CONSTRAINT_NAME as con, + COLUMN_NAME as col, + REFERENCED_TABLE_SCHEMA as fkschema, + REFERENCED_TABLE_NAME as fktable, + REFERENCED_COLUMN_NAME as fkcol + FROM + `INFORMATION_SCHEMA`.`KEY_COLUMN_USAGE` + WHERE + REFERENCED_TABLE_NAME IS NOT NULL + AND TABLE_NAME LIKE :table + $andSchema +EOD; + $command = $this->getDbConnection()->createCommand($sql); + $command->bindValue(':table', $tableName); + if($schemaName!==null) + $command->bindValue(':schema', $schemaName); + $fkeys=array(); + foreach($command->query() as $col) + { + $fkeys[$col['con']]['keys'][$col['col']] = $col['fkcol']; + $fkeys[$col['con']]['table'] = $col['fktable']; + } + return count($fkeys) > 0 ? array_values($fkeys) : $fkeys; + } + + /** + * @param string database name + * @param string table name + * @return string SQL command to create the table. + * @throws TDbException if PHP version is less than 5.1.3 + */ + protected function getShowCreateTable($schemaName, $tableName) + { + if(version_compare(PHP_VERSION,'5.1.3','<')) + throw new TDbException('dbmetadata_requires_php_version', 'Mysql 4.1.x', '5.1.3'); + + //See http://netevil.org/node.php?nid=795&SC=1 + $this->getDbConnection()->setAttribute(PDO::ATTR_EMULATE_PREPARES, true); + if($schemaName!==null) + $sql = "SHOW CREATE TABLE `{$schemaName}`.`{$tableName}`"; + else + $sql = "SHOW CREATE TABLE `{$tableName}`"; + $command = $this->getDbConnection()->createCommand($sql); + $result = $command->queryRow(); + return isset($result['Create Table']) ? $result['Create Table'] : (isset($result['Create View']) ? $result['Create View'] : ''); + } + + /** + * Extract foreign key constraints by extracting the contraints from SHOW CREATE TABLE result. + * @param string database name + * @param string table name + * @return array foreign relationship table name and keys. + */ + protected function findForeignConstraints($schemaName, $tableName) + { + $sql = $this->getShowCreateTable($schemaName, $tableName); + $matches =array(); + $regexp = '/FOREIGN KEY\s+\(([^\)]+)\)\s+REFERENCES\s+`?([^`]+)`?\s\(([^\)]+)\)/mi'; + preg_match_all($regexp,$sql,$matches,PREG_SET_ORDER); + $foreign = array(); + foreach($matches as $match) + { + $fields = array_map('trim',explode(',',str_replace('`','',$match[1]))); + $fk_fields = array_map('trim',explode(',',str_replace('`','',$match[3]))); + $keys=array(); + foreach($fields as $k=>$v) + $keys[$v] = $fk_fields[$k]; + $foreign[] = array('keys' => $keys, 'table' => trim($match[2])); + } + return $foreign; + } + + /** + * @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; + } +} + diff --git a/framework/Data/Common/Mysql/TMysqlTableColumn.php b/framework/Data/Common/Mysql/TMysqlTableColumn.php index 34dceaad..901f4f54 100644 --- a/framework/Data/Common/Mysql/TMysqlTableColumn.php +++ b/framework/Data/Common/Mysql/TMysqlTableColumn.php @@ -1,72 +1,72 @@ -<?php
-/**
- * TMysqlTableColumn class file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
+<?php +/** + * TMysqlTableColumn class file. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.Common.Mysql
- */
-
-/**
- * Load common TDbTableCommon class.
- */
-Prado::using('System.Data.Common.TDbTableColumn');
-
-/**
- * Describes the column metadata of the schema for a Mysql database table.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.Common.Mysql
- * @since 3.1
- */
-class TMysqlTableColumn extends TDbTableColumn
-{
- private static $types = array(
- 'integer' => array('bit', 'tinyint', 'smallint', 'mediumint', 'int', 'integer', 'bigint'),
- 'boolean' => array('boolean', 'bool'),
- 'float' => array('float', 'double', 'double precision', 'decimal', 'dec', 'numeric', 'fixed')
- );
-
- /**
- * Overrides parent implementation, returns PHP type from the db type.
- * @return boolean derived PHP primitive type from the column db type.
- */
- public function getPHPType()
- {
- $dbtype = trim(str_replace(array('unsigned', 'zerofill'),array('','',),strtolower($this->getDbType())));
- if($dbtype==='tinyint' && $this->getColumnSize()===1)
- return 'boolean';
- foreach(self::$types as $type => $dbtypes)
- {
- if(in_array($dbtype, $dbtypes))
- return $type;
- }
- return 'string';
- }
-
- /**
- * @return boolean true if column will auto-increment when the column value is inserted as null.
- */
- public function getAutoIncrement()
- {
- return $this->getInfo('AutoIncrement', false);
- }
-
- /**
- * @return boolean true if auto increment is true.
- */
- public function hasSequence()
- {
- return $this->getAutoIncrement();
- }
-
- public function getDbTypeValues()
- {
- return $this->getInfo('DbTypeValues');
- }
-}
-
+ * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.Common.Mysql + */ + +/** + * Load common TDbTableCommon class. + */ +Prado::using('System.Data.Common.TDbTableColumn'); + +/** + * Describes the column metadata of the schema for a Mysql database table. + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.Common.Mysql + * @since 3.1 + */ +class TMysqlTableColumn extends TDbTableColumn +{ + private static $types = array( + 'integer' => array('bit', 'tinyint', 'smallint', 'mediumint', 'int', 'integer', 'bigint'), + 'boolean' => array('boolean', 'bool'), + 'float' => array('float', 'double', 'double precision', 'decimal', 'dec', 'numeric', 'fixed') + ); + + /** + * Overrides parent implementation, returns PHP type from the db type. + * @return boolean derived PHP primitive type from the column db type. + */ + public function getPHPType() + { + $dbtype = trim(str_replace(array('unsigned', 'zerofill'),array('','',),strtolower($this->getDbType()))); + if($dbtype==='tinyint' && $this->getColumnSize()===1) + return 'boolean'; + foreach(self::$types as $type => $dbtypes) + { + if(in_array($dbtype, $dbtypes)) + return $type; + } + return 'string'; + } + + /** + * @return boolean true if column will auto-increment when the column value is inserted as null. + */ + public function getAutoIncrement() + { + return $this->getInfo('AutoIncrement', false); + } + + /** + * @return boolean true if auto increment is true. + */ + public function hasSequence() + { + return $this->getAutoIncrement(); + } + + public function getDbTypeValues() + { + return $this->getInfo('DbTypeValues'); + } +} + diff --git a/framework/Data/Common/Mysql/TMysqlTableInfo.php b/framework/Data/Common/Mysql/TMysqlTableInfo.php index 30a3d0a1..40b421db 100644 --- a/framework/Data/Common/Mysql/TMysqlTableInfo.php +++ b/framework/Data/Common/Mysql/TMysqlTableInfo.php @@ -1,59 +1,59 @@ -<?php
-/**
- * TMysqlTableInfo class file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
+<?php +/** + * TMysqlTableInfo class file. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.Common.Mysql
- */
-
-/**
- * Loads the base TDbTableInfo class and TMysqlTableColumn class.
- */
-Prado::using('System.Data.Common.TDbTableInfo');
-Prado::using('System.Data.Common.Mysql.TMysqlTableColumn');
-
-/**
- * TMysqlTableInfo class provides additional table information for MySQL database.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.Common.Mysql
- * @since 3.1
- */
-class TMysqlTableInfo extends TDbTableInfo
-{
- /**
- * @return string name of the schema this column belongs to.
- */
- public function getSchemaName()
- {
- return $this->getInfo('SchemaName');
- }
-
- /**
- * @return string full name of the table, database dependent.
- */
- public function getTableFullName()
- {
- if(($schema=$this->getSchemaName())!==null)
- return '`'.$schema.'`.`'.$this->getTableName().'`';
- else
- return '`'.$this->getTableName().'`';
- }
-
- /**
- * @param TDbConnection database connection.
- * @return TDbCommandBuilder new command builder
- */
- public function createCommandBuilder($connection)
- {
- Prado::using('System.Data.Common.Mysql.TMysqlCommandBuilder');
- return new TMysqlCommandBuilder($connection,$this);
- }
-}
-
-?>
+ * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.Common.Mysql + */ + +/** + * Loads the base TDbTableInfo class and TMysqlTableColumn class. + */ +Prado::using('System.Data.Common.TDbTableInfo'); +Prado::using('System.Data.Common.Mysql.TMysqlTableColumn'); + +/** + * TMysqlTableInfo class provides additional table information for MySQL database. + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.Common.Mysql + * @since 3.1 + */ +class TMysqlTableInfo extends TDbTableInfo +{ + /** + * @return string name of the schema this column belongs to. + */ + public function getSchemaName() + { + return $this->getInfo('SchemaName'); + } + + /** + * @return string full name of the table, database dependent. + */ + public function getTableFullName() + { + if(($schema=$this->getSchemaName())!==null) + return '`'.$schema.'`.`'.$this->getTableName().'`'; + else + return '`'.$this->getTableName().'`'; + } + + /** + * @param TDbConnection database connection. + * @return TDbCommandBuilder new command builder + */ + public function createCommandBuilder($connection) + { + Prado::using('System.Data.Common.Mysql.TMysqlCommandBuilder'); + return new TMysqlCommandBuilder($connection,$this); + } +} + +?> diff --git a/framework/Data/Common/Pgsql/TPgsqlCommandBuilder.php b/framework/Data/Common/Pgsql/TPgsqlCommandBuilder.php index 8f4147e5..f9938d61 100644 --- a/framework/Data/Common/Pgsql/TPgsqlCommandBuilder.php +++ b/framework/Data/Common/Pgsql/TPgsqlCommandBuilder.php @@ -1,69 +1,69 @@ -<?php
-/**
- * TPgsqlCommandBuilder class file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
+<?php +/** + * TPgsqlCommandBuilder class file. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 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');
-
-/**
- * TPgsqlCommandBuilder provides specifics methods to create limit/offset query commands
- * for Pgsql database.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id: TDbCommandBuilder.php 1863 2007-04-12 12:43:49Z wei $
- * @package System.Data.Common
- * @since 3.1
- */
-class TPgsqlCommandBuilder extends TDbCommandBuilder
-{
- /**
- * Overrides parent implementation. Only column of type text or character (and its variants)
- * accepts the LIKE criteria.
- * @param array list of column id for potential search condition.
- * @param string string of keywords
- * @return string SQL search condition matching on a set of columns.
- */
- public function getSearchExpression($fields, $keywords)
- {
- $columns = array();
- foreach($fields as $field)
- {
- if($this->isSearchableColumn($this->getTableInfo()->getColumn($field)))
- $columns[] = $field;
- }
- return parent::getSearchExpression($columns, $keywords);
- }
- /**
- *
- * @return boolean true if column can be used for LIKE searching.
- */
- protected function isSearchableColumn($column)
- {
- $type = strtolower($column->getDbType());
- return $type === 'character varying' || $type === 'varchar' ||
- $type === 'character' || $type === 'char' || $type === 'text';
- }
-
- /**
- * Overrides parent implementation to use PostgreSQL's ILIKE instead of LIKE (case-sensitive).
- * @param string column name.
- * @param array keywords
- * @return string search condition for all words in one column.
- */
- protected function getSearchCondition($column, $words)
- {
- $conditions=array();
- foreach($words as $word)
- $conditions[] = $column.' ILIKE '.$this->getDbConnection()->quoteString('%'.$word.'%');
- return '('.implode(' AND ', $conditions).')';
- }
-
-}
-
+ * @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'); + +/** + * TPgsqlCommandBuilder provides specifics methods to create limit/offset query commands + * for Pgsql database. + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id: TDbCommandBuilder.php 1863 2007-04-12 12:43:49Z wei $ + * @package System.Data.Common + * @since 3.1 + */ +class TPgsqlCommandBuilder extends TDbCommandBuilder +{ + /** + * Overrides parent implementation. Only column of type text or character (and its variants) + * accepts the LIKE criteria. + * @param array list of column id for potential search condition. + * @param string string of keywords + * @return string SQL search condition matching on a set of columns. + */ + public function getSearchExpression($fields, $keywords) + { + $columns = array(); + foreach($fields as $field) + { + if($this->isSearchableColumn($this->getTableInfo()->getColumn($field))) + $columns[] = $field; + } + return parent::getSearchExpression($columns, $keywords); + } + /** + * + * @return boolean true if column can be used for LIKE searching. + */ + protected function isSearchableColumn($column) + { + $type = strtolower($column->getDbType()); + return $type === 'character varying' || $type === 'varchar' || + $type === 'character' || $type === 'char' || $type === 'text'; + } + + /** + * Overrides parent implementation to use PostgreSQL's ILIKE instead of LIKE (case-sensitive). + * @param string column name. + * @param array keywords + * @return string search condition for all words in one column. + */ + protected function getSearchCondition($column, $words) + { + $conditions=array(); + foreach($words as $word) + $conditions[] = $column.' ILIKE '.$this->getDbConnection()->quoteString('%'.$word.'%'); + return '('.implode(' AND ', $conditions).')'; + } + +} + diff --git a/framework/Data/Common/Pgsql/TPgsqlMetaData.php b/framework/Data/Common/Pgsql/TPgsqlMetaData.php index 85785606..86a9892e 100644 --- a/framework/Data/Common/Pgsql/TPgsqlMetaData.php +++ b/framework/Data/Common/Pgsql/TPgsqlMetaData.php @@ -1,422 +1,422 @@ -<?php
-/**
- * TPgsqlMetaData class file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.Common.Pgsql
- */
-
-/**
- * Load the base TDbMetaData class.
- */
-Prado::using('System.Data.Common.TDbMetaData');
-Prado::using('System.Data.Common.Pgsql.TPgsqlTableInfo');
-
-/**
- * TPgsqlMetaData loads PostgreSQL database table and column information.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.Common.Pgsql
- * @since 3.1
- */
-class TPgsqlMetaData extends TDbMetaData
-{
- private $_defaultSchema = 'public';
-
- /**
- * @return string TDbTableInfo class name.
- */
- protected function getTableInfoClass()
- {
- return 'TPgsqlTableInfo';
- }
-
- /**
- * Quotes a table name for use in a query.
- * @param string $name table name
- * @return string the properly quoted table name
- */
- public function quoteTableName($name)
- {
- return parent::quoteTableName($name, '"', '"');
- }
-
- /**
- * Quotes a column name for use in a query.
- * @param string $name column name
- * @return string the properly quoted column name
- */
- public function quoteColumnName($name)
- {
- return parent::quoteColumnName($name, '"', '"');
- }
-
- /**
- * Quotes a column alias for use in a query.
- * @param string $name column alias
- * @return string the properly quoted column alias
- */
- public function quoteColumnAlias($name)
- {
- return parent::quoteColumnAlias($name, '"', '"');
- }
-
- /**
- * @param string default schema.
- */
- public function setDefaultSchema($schema)
- {
- $this->_defaultSchema=$schema;
- }
-
- /**
- * @return string default schema.
- */
- public function getDefaultSchema()
- {
- return $this->_defaultSchema;
- }
-
- /**
- * @param string table name with optional schema name prefix, uses default schema name prefix is not provided.
- * @return array tuple as ($schemaName,$tableName)
- */
- protected function getSchemaTableName($table)
- {
- if(count($parts= explode('.', str_replace('"','',$table))) > 1)
- return array($parts[0], $parts[1]);
- else
- return array($this->getDefaultSchema(),$parts[0]);
- }
-
- /**
- * Get the column definitions for given table.
- * @param string table name.
- * @return TPgsqlTableInfo table information.
- */
- protected function createTableInfo($table)
- {
- list($schemaName,$tableName) = $this->getSchemaTableName($table);
-
- // This query is made much more complex by the addition of the 'attisserial' field.
- // The subquery to get that field checks to see if there is an internally dependent
- // sequence on the field.
- $sql =
-<<<EOD
- SELECT
- a.attname,
- pg_catalog.format_type(a.atttypid, a.atttypmod) as type,
- a.atttypmod,
- a.attnotnull, a.atthasdef, adef.adsrc,
- (
- SELECT 1 FROM pg_catalog.pg_depend pd, pg_catalog.pg_class pc
- WHERE pd.objid=pc.oid
- AND pd.classid=pc.tableoid
- AND pd.refclassid=pc.tableoid
- AND pd.refobjid=a.attrelid
- AND pd.refobjsubid=a.attnum
- AND pd.deptype='i'
- AND pc.relkind='S'
- ) IS NOT NULL AS attisserial
-
- FROM
- pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_attrdef adef
- ON a.attrelid=adef.adrelid
- AND a.attnum=adef.adnum
- LEFT JOIN pg_catalog.pg_type t ON a.atttypid=t.oid
- WHERE
- a.attrelid = (SELECT oid FROM pg_catalog.pg_class WHERE relname=:table
- AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE
- nspname = :schema))
- AND a.attnum > 0 AND NOT a.attisdropped
- ORDER BY a.attnum
-EOD;
- $this->getDbConnection()->setActive(true);
- $command = $this->getDbConnection()->createCommand($sql);
- $command->bindValue(':table', $tableName);
- $command->bindValue(':schema', $schemaName);
- $tableInfo = $this->createNewTableInfo($schemaName, $tableName);
- $index=0;
- foreach($command->query() as $col)
- {
- $col['index'] = $index++;
- $this->processColumn($tableInfo, $col);
- }
- if($index===0)
- throw new TDbException('dbmetadata_invalid_table_view', $table);
- return $tableInfo;
- }
-
- /**
- * @param string table schema name
- * @param string table name.
- * @return TPgsqlTableInfo
- */
- protected function createNewTableInfo($schemaName,$tableName)
- {
- $info['SchemaName'] = $this->assertIdentifier($schemaName);
- $info['TableName'] = $this->assertIdentifier($tableName);
- if($this->getIsView($schemaName,$tableName))
- $info['IsView'] = true;
- list($primary, $foreign) = $this->getConstraintKeys($schemaName, $tableName);
- $class = $this->getTableInfoClass();
- return new $class($info,$primary,$foreign);
- }
-
- /**
- * @param string table name, schema name or column name.
- * @return string a valid identifier.
- * @throws TDbException when table name contains a double quote (").
- */
- protected function assertIdentifier($name)
- {
- if(strpos($name, '"')!==false)
- {
- $ref = 'http://www.postgresql.org/docs/7.4/static/sql-syntax.html#SQL-SYNTAX-IDENTIFIERS';
- throw new TDbException('dbcommon_invalid_identifier_name', $name, $ref);
- }
- return $name;
- }
-
- /**
- * @param string table schema name
- * @param string table name.
- * @return boolean true if the table is a view.
- */
- protected function getIsView($schemaName,$tableName)
- {
- $sql =
-<<<EOD
- SELECT count(c.relname) FROM pg_catalog.pg_class c
- LEFT JOIN pg_catalog.pg_namespace n ON (n.oid = c.relnamespace)
- WHERE (n.nspname=:schema) AND (c.relkind = 'v'::"char") AND c.relname = :table
-EOD;
- $this->getDbConnection()->setActive(true);
- $command = $this->getDbConnection()->createCommand($sql);
- $command->bindValue(':schema',$schemaName);
- $command->bindValue(':table', $tableName);
- return intval($command->queryScalar()) === 1;
- }
-
- /**
- * @param TPgsqlTableInfo table information.
- * @param array column information.
- */
- protected function processColumn($tableInfo, $col)
- {
- $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;
- if(in_array($columnId, $tableInfo->getPrimaryKeys()))
- $info['IsPrimaryKey'] = true;
- if($this->isForeignKeyColumn($columnId, $tableInfo))
- $info['IsForeignKey'] = true;
-
- if($col['atttypmod'] > 0)
- $info['ColumnSize'] = $col['atttypmod'] - 4;
- if($col['atthasdef'])
- $info['DefaultValue'] = $col['adsrc'];
- if($col['attisserial'] || substr($col['adsrc'],0,8) === 'nextval(')
- {
- if(($sequence = $this->getSequenceName($tableInfo, $col['adsrc']))!==null)
- {
- $info['SequenceName'] = $sequence;
- unset($info['DefaultValue']);
- }
- }
- $matches = array();
- if(preg_match('/\((\d+)(?:,(\d+))?+\)/', $col['type'], $matches))
- {
- $info['DbType'] = preg_replace('/\(\d+(?:,\d+)?\)/','',$col['type']);
- if($this->isPrecisionType($info['DbType']))
- {
- $info['NumericPrecision'] = intval($matches[1]);
- if(count($matches) > 2)
- $info['NumericScale'] = intval($matches[2]);
- }
- else
- $info['ColumnSize'] = intval($matches[1]);
- }
- else
- $info['DbType'] = $col['type'];
-
- $tableInfo->Columns[$columnId] = new TPgsqlTableColumn($info);
- }
-
- /**
- * @return string serial name if found, null otherwise.
- */
- protected function getSequenceName($tableInfo,$src)
- {
- $matches = array();
- if(preg_match('/nextval\([^\']*\'([^\']+)\'[^\)]*\)/i',$src,$matches))
- {
- if(is_int(strpos($matches[1], '.')))
- return $matches[1];
- else
- return $tableInfo->getSchemaName().'.'.$matches[1];
- }
- }
-
- /**
- * @return boolean true if column type if "numeric", "interval" or begins with "time".
- */
- protected function isPrecisionType($type)
- {
- $type = strtolower(trim($type));
- return $type==='numeric' || $type==='interval' || strpos($type, 'time')===0;
- }
-
- /**
- * 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($schemaName, $tableName)
- {
- $sql =
-<<<EOD
- SELECT conname, consrc, contype, indkey, indisclustered FROM (
- SELECT
- conname,
- CASE WHEN contype='f' THEN
- pg_catalog.pg_get_constraintdef(oid)
- ELSE
- 'CHECK (' || consrc || ')'
- END AS consrc,
- contype,
- conrelid AS relid,
- NULL AS indkey,
- FALSE AS indisclustered
- FROM
- pg_catalog.pg_constraint
- WHERE
- contype IN ('f', 'c')
- UNION ALL
- SELECT
- pc.relname,
- NULL,
- CASE WHEN indisprimary THEN
- 'p'
- ELSE
- 'u'
- END,
- pi.indrelid,
- indkey,
- pi.indisclustered
- FROM
- pg_catalog.pg_class pc,
- pg_catalog.pg_index pi
- WHERE
- pc.oid=pi.indexrelid
- AND EXISTS (
- SELECT 1 FROM pg_catalog.pg_depend d JOIN pg_catalog.pg_constraint c
- ON (d.refclassid = c.tableoid AND d.refobjid = c.oid)
- WHERE d.classid = pc.tableoid AND d.objid = pc.oid AND d.deptype = 'i' AND c.contype IN ('u', 'p')
- )
- ) AS sub
- WHERE relid = (SELECT oid FROM pg_catalog.pg_class WHERE relname=:table
- AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace
- WHERE nspname=:schema))
- ORDER BY
- 1
-EOD;
- $this->getDbConnection()->setActive(true);
- $command = $this->getDbConnection()->createCommand($sql);
- $command->bindValue(':table', $tableName);
- $command->bindValue(':schema', $schemaName);
- $primary = array();
- $foreign = array();
- foreach($command->query() as $row)
- {
- switch($row['contype'])
- {
- case 'p':
- $primary = $this->getPrimaryKeys($tableName, $schemaName, $row['indkey']);
- break;
- case 'f':
- if(($fkey = $this->getForeignKeys($row['consrc']))!==null)
- $foreign[] = $fkey;
- break;
- }
- }
- return array($primary,$foreign);
- }
-
- /**
- * Gets the primary key field names
- * @param string pgsql primary key definition
- * @return array primary key field names.
- */
- protected function getPrimaryKeys($tableName, $schemaName, $columnIndex)
- {
- $index = join(', ', explode(' ', $columnIndex));
- $sql =
-<<<EOD
- SELECT attnum, attname FROM pg_catalog.pg_attribute WHERE
- attrelid=(
- SELECT oid FROM pg_catalog.pg_class WHERE relname=:table AND relnamespace=(
- SELECT oid FROM pg_catalog.pg_namespace WHERE nspname=:schema
- )
- )
- AND attnum IN ({$index})
-EOD;
- $command = $this->getDbConnection()->createCommand($sql);
- $command->bindValue(':table', $tableName);
- $command->bindValue(':schema', $schemaName);
-// $command->bindValue(':columnIndex', join(', ', explode(' ', $columnIndex)));
- $primary = array();
- foreach($command->query() as $row)
- {
- $primary[] = $row['attname'];
- }
-
- return $primary;
- }
-
- /**
- * Gets foreign relationship constraint keys and table name
- * @param string pgsql foreign key definition
- * @return array foreign relationship table name and keys, null otherwise
- */
- protected function getForeignKeys($src)
- {
- $matches = array();
- $brackets = '\(([^\)]+)\)';
- $find = "/FOREIGN\s+KEY\s+{$brackets}\s+REFERENCES\s+([^\(]+){$brackets}/i";
- if(preg_match($find, $src, $matches))
- {
- $keys = preg_split('/,\s+/', $matches[1]);
- $fkeys = array();
- foreach(preg_split('/,\s+/', $matches[3]) as $i => $fkey)
- $fkeys[$keys[$i]] = $fkey;
- return array('table' => str_replace('"','',$matches[2]), 'keys' => $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;
- }
-}
-
+<?php +/** + * TPgsqlMetaData class file. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.Common.Pgsql + */ + +/** + * Load the base TDbMetaData class. + */ +Prado::using('System.Data.Common.TDbMetaData'); +Prado::using('System.Data.Common.Pgsql.TPgsqlTableInfo'); + +/** + * TPgsqlMetaData loads PostgreSQL database table and column information. + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.Common.Pgsql + * @since 3.1 + */ +class TPgsqlMetaData extends TDbMetaData +{ + private $_defaultSchema = 'public'; + + /** + * @return string TDbTableInfo class name. + */ + protected function getTableInfoClass() + { + return 'TPgsqlTableInfo'; + } + + /** + * Quotes a table name for use in a query. + * @param string $name table name + * @return string the properly quoted table name + */ + public function quoteTableName($name) + { + return parent::quoteTableName($name, '"', '"'); + } + + /** + * Quotes a column name for use in a query. + * @param string $name column name + * @return string the properly quoted column name + */ + public function quoteColumnName($name) + { + return parent::quoteColumnName($name, '"', '"'); + } + + /** + * Quotes a column alias for use in a query. + * @param string $name column alias + * @return string the properly quoted column alias + */ + public function quoteColumnAlias($name) + { + return parent::quoteColumnAlias($name, '"', '"'); + } + + /** + * @param string default schema. + */ + public function setDefaultSchema($schema) + { + $this->_defaultSchema=$schema; + } + + /** + * @return string default schema. + */ + public function getDefaultSchema() + { + return $this->_defaultSchema; + } + + /** + * @param string table name with optional schema name prefix, uses default schema name prefix is not provided. + * @return array tuple as ($schemaName,$tableName) + */ + protected function getSchemaTableName($table) + { + if(count($parts= explode('.', str_replace('"','',$table))) > 1) + return array($parts[0], $parts[1]); + else + return array($this->getDefaultSchema(),$parts[0]); + } + + /** + * Get the column definitions for given table. + * @param string table name. + * @return TPgsqlTableInfo table information. + */ + protected function createTableInfo($table) + { + list($schemaName,$tableName) = $this->getSchemaTableName($table); + + // This query is made much more complex by the addition of the 'attisserial' field. + // The subquery to get that field checks to see if there is an internally dependent + // sequence on the field. + $sql = +<<<EOD + SELECT + a.attname, + pg_catalog.format_type(a.atttypid, a.atttypmod) as type, + a.atttypmod, + a.attnotnull, a.atthasdef, adef.adsrc, + ( + SELECT 1 FROM pg_catalog.pg_depend pd, pg_catalog.pg_class pc + WHERE pd.objid=pc.oid + AND pd.classid=pc.tableoid + AND pd.refclassid=pc.tableoid + AND pd.refobjid=a.attrelid + AND pd.refobjsubid=a.attnum + AND pd.deptype='i' + AND pc.relkind='S' + ) IS NOT NULL AS attisserial + + FROM + pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_attrdef adef + ON a.attrelid=adef.adrelid + AND a.attnum=adef.adnum + LEFT JOIN pg_catalog.pg_type t ON a.atttypid=t.oid + WHERE + a.attrelid = (SELECT oid FROM pg_catalog.pg_class WHERE relname=:table + AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE + nspname = :schema)) + AND a.attnum > 0 AND NOT a.attisdropped + ORDER BY a.attnum +EOD; + $this->getDbConnection()->setActive(true); + $command = $this->getDbConnection()->createCommand($sql); + $command->bindValue(':table', $tableName); + $command->bindValue(':schema', $schemaName); + $tableInfo = $this->createNewTableInfo($schemaName, $tableName); + $index=0; + foreach($command->query() as $col) + { + $col['index'] = $index++; + $this->processColumn($tableInfo, $col); + } + if($index===0) + throw new TDbException('dbmetadata_invalid_table_view', $table); + return $tableInfo; + } + + /** + * @param string table schema name + * @param string table name. + * @return TPgsqlTableInfo + */ + protected function createNewTableInfo($schemaName,$tableName) + { + $info['SchemaName'] = $this->assertIdentifier($schemaName); + $info['TableName'] = $this->assertIdentifier($tableName); + if($this->getIsView($schemaName,$tableName)) + $info['IsView'] = true; + list($primary, $foreign) = $this->getConstraintKeys($schemaName, $tableName); + $class = $this->getTableInfoClass(); + return new $class($info,$primary,$foreign); + } + + /** + * @param string table name, schema name or column name. + * @return string a valid identifier. + * @throws TDbException when table name contains a double quote ("). + */ + protected function assertIdentifier($name) + { + if(strpos($name, '"')!==false) + { + $ref = 'http://www.postgresql.org/docs/7.4/static/sql-syntax.html#SQL-SYNTAX-IDENTIFIERS'; + throw new TDbException('dbcommon_invalid_identifier_name', $name, $ref); + } + return $name; + } + + /** + * @param string table schema name + * @param string table name. + * @return boolean true if the table is a view. + */ + protected function getIsView($schemaName,$tableName) + { + $sql = +<<<EOD + SELECT count(c.relname) FROM pg_catalog.pg_class c + LEFT JOIN pg_catalog.pg_namespace n ON (n.oid = c.relnamespace) + WHERE (n.nspname=:schema) AND (c.relkind = 'v'::"char") AND c.relname = :table +EOD; + $this->getDbConnection()->setActive(true); + $command = $this->getDbConnection()->createCommand($sql); + $command->bindValue(':schema',$schemaName); + $command->bindValue(':table', $tableName); + return intval($command->queryScalar()) === 1; + } + + /** + * @param TPgsqlTableInfo table information. + * @param array column information. + */ + protected function processColumn($tableInfo, $col) + { + $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; + if(in_array($columnId, $tableInfo->getPrimaryKeys())) + $info['IsPrimaryKey'] = true; + if($this->isForeignKeyColumn($columnId, $tableInfo)) + $info['IsForeignKey'] = true; + + if($col['atttypmod'] > 0) + $info['ColumnSize'] = $col['atttypmod'] - 4; + if($col['atthasdef']) + $info['DefaultValue'] = $col['adsrc']; + if($col['attisserial'] || substr($col['adsrc'],0,8) === 'nextval(') + { + if(($sequence = $this->getSequenceName($tableInfo, $col['adsrc']))!==null) + { + $info['SequenceName'] = $sequence; + unset($info['DefaultValue']); + } + } + $matches = array(); + if(preg_match('/\((\d+)(?:,(\d+))?+\)/', $col['type'], $matches)) + { + $info['DbType'] = preg_replace('/\(\d+(?:,\d+)?\)/','',$col['type']); + if($this->isPrecisionType($info['DbType'])) + { + $info['NumericPrecision'] = intval($matches[1]); + if(count($matches) > 2) + $info['NumericScale'] = intval($matches[2]); + } + else + $info['ColumnSize'] = intval($matches[1]); + } + else + $info['DbType'] = $col['type']; + + $tableInfo->Columns[$columnId] = new TPgsqlTableColumn($info); + } + + /** + * @return string serial name if found, null otherwise. + */ + protected function getSequenceName($tableInfo,$src) + { + $matches = array(); + if(preg_match('/nextval\([^\']*\'([^\']+)\'[^\)]*\)/i',$src,$matches)) + { + if(is_int(strpos($matches[1], '.'))) + return $matches[1]; + else + return $tableInfo->getSchemaName().'.'.$matches[1]; + } + } + + /** + * @return boolean true if column type if "numeric", "interval" or begins with "time". + */ + protected function isPrecisionType($type) + { + $type = strtolower(trim($type)); + return $type==='numeric' || $type==='interval' || strpos($type, 'time')===0; + } + + /** + * 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($schemaName, $tableName) + { + $sql = +<<<EOD + SELECT conname, consrc, contype, indkey, indisclustered FROM ( + SELECT + conname, + CASE WHEN contype='f' THEN + pg_catalog.pg_get_constraintdef(oid) + ELSE + 'CHECK (' || consrc || ')' + END AS consrc, + contype, + conrelid AS relid, + NULL AS indkey, + FALSE AS indisclustered + FROM + pg_catalog.pg_constraint + WHERE + contype IN ('f', 'c') + UNION ALL + SELECT + pc.relname, + NULL, + CASE WHEN indisprimary THEN + 'p' + ELSE + 'u' + END, + pi.indrelid, + indkey, + pi.indisclustered + FROM + pg_catalog.pg_class pc, + pg_catalog.pg_index pi + WHERE + pc.oid=pi.indexrelid + AND EXISTS ( + SELECT 1 FROM pg_catalog.pg_depend d JOIN pg_catalog.pg_constraint c + ON (d.refclassid = c.tableoid AND d.refobjid = c.oid) + WHERE d.classid = pc.tableoid AND d.objid = pc.oid AND d.deptype = 'i' AND c.contype IN ('u', 'p') + ) + ) AS sub + WHERE relid = (SELECT oid FROM pg_catalog.pg_class WHERE relname=:table + AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace + WHERE nspname=:schema)) + ORDER BY + 1 +EOD; + $this->getDbConnection()->setActive(true); + $command = $this->getDbConnection()->createCommand($sql); + $command->bindValue(':table', $tableName); + $command->bindValue(':schema', $schemaName); + $primary = array(); + $foreign = array(); + foreach($command->query() as $row) + { + switch($row['contype']) + { + case 'p': + $primary = $this->getPrimaryKeys($tableName, $schemaName, $row['indkey']); + break; + case 'f': + if(($fkey = $this->getForeignKeys($row['consrc']))!==null) + $foreign[] = $fkey; + break; + } + } + return array($primary,$foreign); + } + + /** + * Gets the primary key field names + * @param string pgsql primary key definition + * @return array primary key field names. + */ + protected function getPrimaryKeys($tableName, $schemaName, $columnIndex) + { + $index = join(', ', explode(' ', $columnIndex)); + $sql = +<<<EOD + SELECT attnum, attname FROM pg_catalog.pg_attribute WHERE + attrelid=( + SELECT oid FROM pg_catalog.pg_class WHERE relname=:table AND relnamespace=( + SELECT oid FROM pg_catalog.pg_namespace WHERE nspname=:schema + ) + ) + AND attnum IN ({$index}) +EOD; + $command = $this->getDbConnection()->createCommand($sql); + $command->bindValue(':table', $tableName); + $command->bindValue(':schema', $schemaName); +// $command->bindValue(':columnIndex', join(', ', explode(' ', $columnIndex))); + $primary = array(); + foreach($command->query() as $row) + { + $primary[] = $row['attname']; + } + + return $primary; + } + + /** + * Gets foreign relationship constraint keys and table name + * @param string pgsql foreign key definition + * @return array foreign relationship table name and keys, null otherwise + */ + protected function getForeignKeys($src) + { + $matches = array(); + $brackets = '\(([^\)]+)\)'; + $find = "/FOREIGN\s+KEY\s+{$brackets}\s+REFERENCES\s+([^\(]+){$brackets}/i"; + if(preg_match($find, $src, $matches)) + { + $keys = preg_split('/,\s+/', $matches[1]); + $fkeys = array(); + foreach(preg_split('/,\s+/', $matches[3]) as $i => $fkey) + $fkeys[$keys[$i]] = $fkey; + return array('table' => str_replace('"','',$matches[2]), 'keys' => $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; + } +} + diff --git a/framework/Data/Common/Pgsql/TPgsqlTableColumn.php b/framework/Data/Common/Pgsql/TPgsqlTableColumn.php index 46eab9c1..fe8ff499 100644 --- a/framework/Data/Common/Pgsql/TPgsqlTableColumn.php +++ b/framework/Data/Common/Pgsql/TPgsqlTableColumn.php @@ -1,49 +1,49 @@ <?php -/**
- * TPgsqlTableColumn class file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
+/** + * TPgsqlTableColumn class file. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.Common.Pgsql
- */
-
-/**
- * Load common TDbTableCommon class.
- */
-Prado::using('System.Data.Common.TDbTableColumn');
-
-/**
- * Describes the column metadata of the schema for a PostgreSQL database table.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.Common.Pgsql
- * @since 3.1
- */
-class TPgsqlTableColumn extends TDbTableColumn
-{
- private static $types=array(
- 'integer' => array('bit', 'bit varying', 'real', 'serial', 'int', 'integer'),
- 'boolean' => array('boolean'),
- 'float' => array('bigint', 'bigserial', 'double precision', 'money', 'numeric')
- );
-
- /**
- * Overrides parent implementation, returns PHP type from the db type.
- * @return boolean derived PHP primitive type from the column db type.
- */
- public function getPHPType()
- {
- $dbtype = strtolower($this->getDbType());
- foreach(self::$types as $type => $dbtypes)
- {
- if(in_array($dbtype, $dbtypes))
- return $type;
- }
- return 'string';
- }
-}
+ * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.Common.Pgsql + */ + +/** + * Load common TDbTableCommon class. + */ +Prado::using('System.Data.Common.TDbTableColumn'); + +/** + * Describes the column metadata of the schema for a PostgreSQL database table. + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.Common.Pgsql + * @since 3.1 + */ +class TPgsqlTableColumn extends TDbTableColumn +{ + private static $types=array( + 'integer' => array('bit', 'bit varying', 'real', 'serial', 'int', 'integer'), + 'boolean' => array('boolean'), + 'float' => array('bigint', 'bigserial', 'double precision', 'money', 'numeric') + ); + + /** + * Overrides parent implementation, returns PHP type from the db type. + * @return boolean derived PHP primitive type from the column db type. + */ + public function getPHPType() + { + $dbtype = strtolower($this->getDbType()); + foreach(self::$types as $type => $dbtypes) + { + if(in_array($dbtype, $dbtypes)) + return $type; + } + return 'string'; + } +} diff --git a/framework/Data/Common/Pgsql/TPgsqlTableInfo.php b/framework/Data/Common/Pgsql/TPgsqlTableInfo.php index 11e5a613..6ad941d7 100644 --- a/framework/Data/Common/Pgsql/TPgsqlTableInfo.php +++ b/framework/Data/Common/Pgsql/TPgsqlTableInfo.php @@ -1,58 +1,58 @@ <?php -/**
- * TPgsqlTableInfo class file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
+/** + * TPgsqlTableInfo class file. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.Common.Pgsql
- */
-
-/**
- * Loads the base TDbTableInfo class and TPgsqlTableColumn class.
- */
-Prado::using('System.Data.Common.TDbTableInfo');
-Prado::using('System.Data.Common.Pgsql.TPgsqlTableColumn');
-
-/**
- * TPgsqlTableInfo class provides additional table information for PostgreSQL database.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.Common.Pgsql
- * @since 3.1
- */
-class TPgsqlTableInfo extends TDbTableInfo
-{
- /**
- * @return string name of the schema this column belongs to.
- */
- public function getSchemaName()
- {
- return $this->getInfo('SchemaName');
- }
-
- /**
- * @return string full name of the table, database dependent.
- */
- public function getTableFullName()
- {
- if(($schema=$this->getSchemaName())!==null)
- return $schema.'.'.$this->getTableName();
- else
- return $this->getTableName();
- }
-
- /**
- * @param TDbConnection database connection.
- * @return TDbCommandBuilder new command builder
- */
- public function createCommandBuilder($connection)
- {
- Prado::using('System.Data.Common.Pgsql.TPgsqlCommandBuilder');
- return new TPgsqlCommandBuilder($connection,$this);
- }
-}
+ * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.Common.Pgsql + */ + +/** + * Loads the base TDbTableInfo class and TPgsqlTableColumn class. + */ +Prado::using('System.Data.Common.TDbTableInfo'); +Prado::using('System.Data.Common.Pgsql.TPgsqlTableColumn'); + +/** + * TPgsqlTableInfo class provides additional table information for PostgreSQL database. + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.Common.Pgsql + * @since 3.1 + */ +class TPgsqlTableInfo extends TDbTableInfo +{ + /** + * @return string name of the schema this column belongs to. + */ + public function getSchemaName() + { + return $this->getInfo('SchemaName'); + } + + /** + * @return string full name of the table, database dependent. + */ + public function getTableFullName() + { + if(($schema=$this->getSchemaName())!==null) + return $schema.'.'.$this->getTableName(); + else + return $this->getTableName(); + } + + /** + * @param TDbConnection database connection. + * @return TDbCommandBuilder new command builder + */ + public function createCommandBuilder($connection) + { + Prado::using('System.Data.Common.Pgsql.TPgsqlCommandBuilder'); + return new TPgsqlCommandBuilder($connection,$this); + } +} diff --git a/framework/Data/Common/Sqlite/TSqliteCommandBuilder.php b/framework/Data/Common/Sqlite/TSqliteCommandBuilder.php index d162ed60..d7d82812 100644 --- a/framework/Data/Common/Sqlite/TSqliteCommandBuilder.php +++ b/framework/Data/Common/Sqlite/TSqliteCommandBuilder.php @@ -1,47 +1,47 @@ -<?php
-/**
- * TSqliteCommandBuilder class file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
+<?php +/** + * TSqliteCommandBuilder class file. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 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');
-
-/**
- * TSqliteCommandBuilder provides specifics methods to create limit/offset query commands
- * for Sqlite database.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id: TDbCommandBuilder.php 1863 2007-04-12 12:43:49Z wei $
- * @package System.Data.Common
- * @since 3.1
- */
-class TSqliteCommandBuilder extends TDbCommandBuilder
-{
- /**
- * Alters the sql to apply $limit and $offset.
- * @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)
- {
- $limitStr = ' LIMIT '.$limit;
- $offsetStr = $offset >= 0 ? ' OFFSET '.$offset : '';
- return $sql.$limitStr.$offsetStr;
- }
- else
- return $sql;
- }
-}
-
+ * @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'); + +/** + * TSqliteCommandBuilder provides specifics methods to create limit/offset query commands + * for Sqlite database. + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id: TDbCommandBuilder.php 1863 2007-04-12 12:43:49Z wei $ + * @package System.Data.Common + * @since 3.1 + */ +class TSqliteCommandBuilder extends TDbCommandBuilder +{ + /** + * Alters the sql to apply $limit and $offset. + * @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) + { + $limitStr = ' LIMIT '.$limit; + $offsetStr = $offset >= 0 ? ' OFFSET '.$offset : ''; + return $sql.$limitStr.$offsetStr; + } + else + return $sql; + } +} + diff --git a/framework/Data/Common/Sqlite/TSqliteMetaData.php b/framework/Data/Common/Sqlite/TSqliteMetaData.php index 5acfad9d..d101d179 100644 --- a/framework/Data/Common/Sqlite/TSqliteMetaData.php +++ b/framework/Data/Common/Sqlite/TSqliteMetaData.php @@ -1,210 +1,210 @@ -<?php
-/**
- * TSqliteMetaData class file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id: TSqliteMetaData.php 1861 2007-04-12 08:05:03Z wei $
- * @package System.Data.Common.Sqlite
- */
-
-/**
- * Load the base TDbMetaData class.
- */
-Prado::using('System.Data.Common.TDbMetaData');
-Prado::using('System.Data.Common.Sqlite.TSqliteTableInfo');
-
-/**
- * TSqliteMetaData loads SQLite database table and column information.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id: TSqliteMetaData.php 1861 2007-04-12 08:05:03Z wei $
- * @package System.Data.Commom.Sqlite
- * @since 3.1
- */
-class TSqliteMetaData extends TDbMetaData
-{
- /**
- * @return string TDbTableInfo class name.
- */
- protected function getTableInfoClass()
- {
- return 'TSqliteTableInfo';
- }
-
- /**
- * Quotes a table name for use in a query.
- * @param string $name table name
- * @return string the properly quoted table name
- */
- public function quoteTableName($name)
- {
- return parent::quoteTableName($name, "'", "'");
- }
-
- /**
- * Quotes a column name for use in a query.
- * @param string $name column name
- * @return string the properly quoted column name
- */
- public function quoteColumnName($name)
- {
- return parent::quoteColumnName($name, '"', '"');
- }
-
- /**
- * Quotes a column alias for use in a query.
- * @param string $name column alias
- * @return string the properly quoted column alias
- */
- public function quoteColumnAlias($name)
- {
- return parent::quoteColumnAlias($name, '"', '"');
- }
-
- /**
- * Get the column definitions for given table.
- * @param string table name.
- * @return TPgsqlTableInfo table information.
- */
- protected function createTableInfo($tableName)
- {
- $tableName = str_replace("'",'',$tableName);
- $this->getDbConnection()->setActive(true);
- $table = $this->getDbConnection()->quoteString($tableName);
- $sql = "PRAGMA table_info({$table})";
- $command = $this->getDbConnection()->createCommand($sql);
- $foreign = $this->getForeignKeys($table);
- $index=0;
- $columns=array();
- $primary=array();
- foreach($command->query() as $col)
- {
- $col['index'] = $index++;
- $column = $this->processColumn($col, $foreign);
- $columns[$col['name']] = $column;
- if($column->getIsPrimaryKey())
- $primary[] = $col['name'];
- }
- $info['TableName'] = $tableName;
- if($this->getIsView($tableName))
- $info['IsView'] = true;
- if(count($columns)===0)
- throw new TDbException('dbmetadata_invalid_table_view', $tableName);
- $class = $this->getTableInfoClass();
- $tableInfo = new $class($info,$primary,$foreign);
- $tableInfo->getColumns()->copyFrom($columns);
- return $tableInfo;
- }
-
- /**
- * @param string table name.
- * @return boolean true if the table is a view.
- */
- protected function getIsView($tableName)
- {
- $sql = 'SELECT count(*) FROM sqlite_master WHERE type="view" AND name= :table';
- $this->getDbConnection()->setActive(true);
- $command = $this->getDbConnection()->createCommand($sql);
- $command->bindValue(':table', $tableName);
- return intval($command->queryScalar()) === 1;
- }
-
- /**
- * @param array column information.
- * @param array foreign key details.
- * @return TSqliteTableColumn column details.
- */
- protected function processColumn($col, $foreign)
- {
- $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')
- $info['AllowNull'] = true;
-
- if($col['pk']==='1')
- $info['IsPrimaryKey'] = true;
- if($this->isForeignKeyColumn($columnId, $foreign))
- $info['IsForeignKey'] = true;
-
- if($col['dflt_value']!==null)
- $info['DefaultValue'] = $col['dflt_value'];
-
- $type = strtolower($col['type']);
- $info['AutoIncrement'] = $type==='integer' && $col['pk']==='1';
-
- $info['DbType'] = $type;
- $match=array();
- if(is_int($pos=strpos($type, '(')) && preg_match('/\((.*)\)/', $type, $match))
- {
- $ps = explode(',', $match[1]);
- if(count($ps)===2)
- {
- $info['NumericPrecision'] = intval($ps[0]);
- $info['NumericScale'] = intval($ps[1]);
- }
- else
- $info['ColumnSize']=intval($match[1]);
- $info['DbType'] = substr($type,0,$pos);
- }
-
- return new TSqliteTableColumn($info);
- }
-
- /**
- *
- *
- * @param string quoted table name.
- * @return array foreign key details.
- */
- protected function getForeignKeys($table)
- {
- $sql = "PRAGMA foreign_key_list({$table})";
- $command = $this->getDbConnection()->createCommand($sql);
- $fkeys = array();
- foreach($command->query() as $col)
- {
- $fkeys[$col['table']]['keys'][$col['from']] = $col['to'];
- $fkeys[$col['table']]['table'] = $col['table'];
- }
- return count($fkeys) > 0 ? array_values($fkeys) : $fkeys;
- }
-
- /**
- * @param string column name.
- * @param array foreign key column names.
- * @return boolean true if column is a foreign key.
- */
- protected function isForeignKeyColumn($columnId, $foreign)
- {
- foreach($foreign as $fk)
- {
- if(in_array($columnId, array_keys($fk['keys'])))
- return true;
- }
- 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
- CONSTRAINT fk_foo_id REFERENCES foo(id) ON DELETE CASCADE
-);
-*/
-
+<?php +/** + * TSqliteMetaData class file. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TSqliteMetaData.php 1861 2007-04-12 08:05:03Z wei $ + * @package System.Data.Common.Sqlite + */ + +/** + * Load the base TDbMetaData class. + */ +Prado::using('System.Data.Common.TDbMetaData'); +Prado::using('System.Data.Common.Sqlite.TSqliteTableInfo'); + +/** + * TSqliteMetaData loads SQLite database table and column information. + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id: TSqliteMetaData.php 1861 2007-04-12 08:05:03Z wei $ + * @package System.Data.Commom.Sqlite + * @since 3.1 + */ +class TSqliteMetaData extends TDbMetaData +{ + /** + * @return string TDbTableInfo class name. + */ + protected function getTableInfoClass() + { + return 'TSqliteTableInfo'; + } + + /** + * Quotes a table name for use in a query. + * @param string $name table name + * @return string the properly quoted table name + */ + public function quoteTableName($name) + { + return parent::quoteTableName($name, "'", "'"); + } + + /** + * Quotes a column name for use in a query. + * @param string $name column name + * @return string the properly quoted column name + */ + public function quoteColumnName($name) + { + return parent::quoteColumnName($name, '"', '"'); + } + + /** + * Quotes a column alias for use in a query. + * @param string $name column alias + * @return string the properly quoted column alias + */ + public function quoteColumnAlias($name) + { + return parent::quoteColumnAlias($name, '"', '"'); + } + + /** + * Get the column definitions for given table. + * @param string table name. + * @return TPgsqlTableInfo table information. + */ + protected function createTableInfo($tableName) + { + $tableName = str_replace("'",'',$tableName); + $this->getDbConnection()->setActive(true); + $table = $this->getDbConnection()->quoteString($tableName); + $sql = "PRAGMA table_info({$table})"; + $command = $this->getDbConnection()->createCommand($sql); + $foreign = $this->getForeignKeys($table); + $index=0; + $columns=array(); + $primary=array(); + foreach($command->query() as $col) + { + $col['index'] = $index++; + $column = $this->processColumn($col, $foreign); + $columns[$col['name']] = $column; + if($column->getIsPrimaryKey()) + $primary[] = $col['name']; + } + $info['TableName'] = $tableName; + if($this->getIsView($tableName)) + $info['IsView'] = true; + if(count($columns)===0) + throw new TDbException('dbmetadata_invalid_table_view', $tableName); + $class = $this->getTableInfoClass(); + $tableInfo = new $class($info,$primary,$foreign); + $tableInfo->getColumns()->copyFrom($columns); + return $tableInfo; + } + + /** + * @param string table name. + * @return boolean true if the table is a view. + */ + protected function getIsView($tableName) + { + $sql = 'SELECT count(*) FROM sqlite_master WHERE type="view" AND name= :table'; + $this->getDbConnection()->setActive(true); + $command = $this->getDbConnection()->createCommand($sql); + $command->bindValue(':table', $tableName); + return intval($command->queryScalar()) === 1; + } + + /** + * @param array column information. + * @param array foreign key details. + * @return TSqliteTableColumn column details. + */ + protected function processColumn($col, $foreign) + { + $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') + $info['AllowNull'] = true; + + if($col['pk']==='1') + $info['IsPrimaryKey'] = true; + if($this->isForeignKeyColumn($columnId, $foreign)) + $info['IsForeignKey'] = true; + + if($col['dflt_value']!==null) + $info['DefaultValue'] = $col['dflt_value']; + + $type = strtolower($col['type']); + $info['AutoIncrement'] = $type==='integer' && $col['pk']==='1'; + + $info['DbType'] = $type; + $match=array(); + if(is_int($pos=strpos($type, '(')) && preg_match('/\((.*)\)/', $type, $match)) + { + $ps = explode(',', $match[1]); + if(count($ps)===2) + { + $info['NumericPrecision'] = intval($ps[0]); + $info['NumericScale'] = intval($ps[1]); + } + else + $info['ColumnSize']=intval($match[1]); + $info['DbType'] = substr($type,0,$pos); + } + + return new TSqliteTableColumn($info); + } + + /** + * + * + * @param string quoted table name. + * @return array foreign key details. + */ + protected function getForeignKeys($table) + { + $sql = "PRAGMA foreign_key_list({$table})"; + $command = $this->getDbConnection()->createCommand($sql); + $fkeys = array(); + foreach($command->query() as $col) + { + $fkeys[$col['table']]['keys'][$col['from']] = $col['to']; + $fkeys[$col['table']]['table'] = $col['table']; + } + return count($fkeys) > 0 ? array_values($fkeys) : $fkeys; + } + + /** + * @param string column name. + * @param array foreign key column names. + * @return boolean true if column is a foreign key. + */ + protected function isForeignKeyColumn($columnId, $foreign) + { + foreach($foreign as $fk) + { + if(in_array($columnId, array_keys($fk['keys']))) + return true; + } + 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 + CONSTRAINT fk_foo_id REFERENCES foo(id) ON DELETE CASCADE +); +*/ + diff --git a/framework/Data/Common/Sqlite/TSqliteTableColumn.php b/framework/Data/Common/Sqlite/TSqliteTableColumn.php index 915debbe..6783e1ad 100644 --- a/framework/Data/Common/Sqlite/TSqliteTableColumn.php +++ b/framework/Data/Common/Sqlite/TSqliteTableColumn.php @@ -1,64 +1,64 @@ -<?php
-/**
- * TSqliteTableColumn class file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
+<?php +/** + * TSqliteTableColumn class file. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/
- * @version $Id: TSqliteTableColumn.php 1861 2007-04-12 08:05:03Z wei $
- * @package System.Data.Common.Sqlite
- */
-
-/**
- * Load common TDbTableCommon class.
- */
-Prado::using('System.Data.Common.TDbTableColumn');
-
-/**
- * Describes the column metadata of the schema for a PostgreSQL database table.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id: TSqliteTableColumn.php 1861 2007-04-12 08:05:03Z wei $
- * @package System.Data.Common.Sqlite
- * @since 3.1
- */
-class TSqliteTableColumn extends TDbTableColumn
-{
- /**
- * @TODO add sqlite types.
- */
- 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()
- {
- $dbtype = strtolower($this->getDbType());
- foreach(self::$types as $type => $dbtypes)
- {
- if(in_array($dbtype, $dbtypes))
- return $type;
- }
- return 'string';
- }
-
- /**
- * @return boolean true if column will auto-increment when the column value is inserted as null.
- */
- public function getAutoIncrement()
- {
- return $this->getInfo('AutoIncrement', false);
- }
-
- /**
- * @return boolean true if auto increment is true.
- */
- public function hasSequence()
- {
- return $this->getAutoIncrement();
- }
-}
-
+ * @license http://www.pradosoft.com/license/ + * @version $Id: TSqliteTableColumn.php 1861 2007-04-12 08:05:03Z wei $ + * @package System.Data.Common.Sqlite + */ + +/** + * Load common TDbTableCommon class. + */ +Prado::using('System.Data.Common.TDbTableColumn'); + +/** + * Describes the column metadata of the schema for a PostgreSQL database table. + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id: TSqliteTableColumn.php 1861 2007-04-12 08:05:03Z wei $ + * @package System.Data.Common.Sqlite + * @since 3.1 + */ +class TSqliteTableColumn extends TDbTableColumn +{ + /** + * @TODO add sqlite types. + */ + 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() + { + $dbtype = strtolower($this->getDbType()); + foreach(self::$types as $type => $dbtypes) + { + if(in_array($dbtype, $dbtypes)) + return $type; + } + return 'string'; + } + + /** + * @return boolean true if column will auto-increment when the column value is inserted as null. + */ + public function getAutoIncrement() + { + return $this->getInfo('AutoIncrement', false); + } + + /** + * @return boolean true if auto increment is true. + */ + public function hasSequence() + { + return $this->getAutoIncrement(); + } +} + diff --git a/framework/Data/Common/Sqlite/TSqliteTableInfo.php b/framework/Data/Common/Sqlite/TSqliteTableInfo.php index fe9ab846..420dce85 100644 --- a/framework/Data/Common/Sqlite/TSqliteTableInfo.php +++ b/framework/Data/Common/Sqlite/TSqliteTableInfo.php @@ -1,47 +1,47 @@ -<?php
-/**
- * TSqliteTableInfo class file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
+<?php +/** + * TSqliteTableInfo class file. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/
- * @version $Id: TSqliteTableInfo.php 1861 2007-04-12 08:05:03Z wei $
- * @package System.Data.Common.Sqlite
- */
-
-/**
- * Loads the base TDbTableInfo class and TSqliteTableColumn class.
- */
-Prado::using('System.Data.Common.TDbTableInfo');
-Prado::using('System.Data.Common.Sqlite.TSqliteTableColumn');
-
-/**
- * TSqliteTableInfo class provides additional table information for PostgreSQL database.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id: TSqliteTableInfo.php 1861 2007-04-12 08:05:03Z wei $
- * @package System.Data.Common.Sqlite
- * @since 3.1
- */
-class TSqliteTableInfo extends TDbTableInfo
-{
- /**
- * @param TDbConnection database connection.
- * @return TDbCommandBuilder new command builder
- */
- public function createCommandBuilder($connection)
- {
- Prado::using('System.Data.Common.Sqlite.TSqliteCommandBuilder');
- return new TSqliteCommandBuilder($connection,$this);
- }
-
- /**
- * @return string full name of the table, database dependent.
- */
- public function getTableFullName()
- {
- return "'".$this->getTableName()."'";
- }
-}
-
+ * @license http://www.pradosoft.com/license/ + * @version $Id: TSqliteTableInfo.php 1861 2007-04-12 08:05:03Z wei $ + * @package System.Data.Common.Sqlite + */ + +/** + * Loads the base TDbTableInfo class and TSqliteTableColumn class. + */ +Prado::using('System.Data.Common.TDbTableInfo'); +Prado::using('System.Data.Common.Sqlite.TSqliteTableColumn'); + +/** + * TSqliteTableInfo class provides additional table information for PostgreSQL database. + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id: TSqliteTableInfo.php 1861 2007-04-12 08:05:03Z wei $ + * @package System.Data.Common.Sqlite + * @since 3.1 + */ +class TSqliteTableInfo extends TDbTableInfo +{ + /** + * @param TDbConnection database connection. + * @return TDbCommandBuilder new command builder + */ + public function createCommandBuilder($connection) + { + Prado::using('System.Data.Common.Sqlite.TSqliteCommandBuilder'); + return new TSqliteCommandBuilder($connection,$this); + } + + /** + * @return string full name of the table, database dependent. + */ + public function getTableFullName() + { + return "'".$this->getTableName()."'"; + } +} + diff --git a/framework/Data/Common/TDbCommandBuilder.php b/framework/Data/Common/TDbCommandBuilder.php index 212e5f02..ebdede99 100644 --- a/framework/Data/Common/TDbCommandBuilder.php +++ b/framework/Data/Common/TDbCommandBuilder.php @@ -1,507 +1,507 @@ -<?php
-/**
- * TDbCommandBuilder class file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.Common
- */
-
-/**
- * TDbCommandBuilder provides basic methods to create query commands for tables
- * giving by {@link setTableInfo TableInfo} the property.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.Common
- * @since 3.1
- */
-class TDbCommandBuilder extends TComponent
-{
- private $_connection;
- private $_tableInfo;
-
- /**
- * @param TDbConnection database connection.
- * @param TDbTableInfo table information.
- */
- public function __construct($connection=null, $tableInfo=null)
- {
- $this->setDbConnection($connection);
- $this->setTableInfo($tableInfo);
- }
-
- /**
- * @return TDbConnection database connection.
- */
- public function getDbConnection()
- {
- return $this->_connection;
- }
-
- /**
- * @param TDbConnection database connection.
- */
- public function setDbConnection($value)
- {
- $this->_connection=$value;
- }
-
- /**
- * @param TDbTableInfo table information.
- */
- public function setTableInfo($value)
- {
- $this->_tableInfo=$value;
- }
-
- /**
- * @param TDbTableInfo table information.
- */
- public function getTableInfo()
- {
- 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.
- * @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 ? (int)$limit : -1;
- $offset = $offset!==null ? (int)$offset : -1;
- $limitStr = $limit >= 0 ? ' LIMIT '.$limit : '';
- $offsetStr = $offset >= 0 ? ' OFFSET '.$offset : '';
- return $sql.$limitStr.$offsetStr;
- }
-
- /**
- * @param string SQL string without existing ordering.
- * @param array pairs of column names as key and direction as value.
- * @return string modified SQL applied with ORDER BY.
- */
- public function applyOrdering($sql, $ordering)
- {
- $orders=array();
- foreach($ordering as $name => $direction)
- {
- $direction = strtolower($direction) == 'desc' ? 'DESC' : 'ASC';
- if(false !== strpos($name, '(') && false !== strpos($name, ')')) {
- // key is a function (bad practice, but we need to handle it)
- $key = $name;
- } else {
- // key is a column
- $key = $this->getTableInfo()->getColumn($name)->getColumnName();
- }
- $orders[] = $key.' '.$direction;
- }
- if(count($orders) > 0)
- $sql .= ' ORDER BY '.implode(', ', $orders);
- return $sql;
- }
-
- /**
- * Computes the SQL condition for search a set of column using regular expression
- * (or LIKE, depending on database implementation) to match a string of
- * keywords (default matches all keywords).
- * @param array list of column id for potential search condition.
- * @param string string of keywords
- * @return string SQL search condition matching on a set of columns.
- */
- public function getSearchExpression($fields, $keywords)
- {
- if(strlen(trim($keywords)) == 0) return '';
- $words = preg_split('/\s/u', $keywords);
- $conditions = array();
- foreach($fields as $field)
- {
- $column = $this->getTableInfo()->getColumn($field)->getColumnName();
- $conditions[] = $this->getSearchCondition($column, $words);
- }
- return '('.implode(' OR ', $conditions).')';
- }
-
- /**
- * @param string column name.
- * @param array keywords
- * @return string search condition for all words in one column.
- */
- protected function getSearchCondition($column, $words)
- {
- $conditions=array();
- foreach($words as $word)
- $conditions[] = $column.' LIKE '.$this->getDbConnection()->quoteString('%'.$word.'%');
- return '('.implode(' AND ', $conditions).')';
- }
-
- /**
- *
- * Different behavior depends on type of passed data
- * string
- * usage without modification
- *
- * null
- * will be expanded to full list of quoted table column names (quoting depends on database)
- *
- * array
- * - Column names will be quoted if used as key or value of array
- * <code>
- * array('col1', 'col2', 'col2')
- * // SELECT `col1`, `col2`, `col3` FROM...
- * </code>
- *
- * - Column aliasing
- * <code>
- * array('mycol1' => 'col1', 'mycol2' => 'COUNT(*)')
- * // SELECT `col1` AS mycol1, COUNT(*) AS mycol2 FROM...
- * </code>
- *
- * - NULL and scalar values (strings will be quoted depending on database)
- * <code>
- * array('col1' => 'my custom string', 'col2' => 1.0, 'col3' => 'NULL')
- * // SELECT "my custom string" AS `col1`, 1.0 AS `col2`, NULL AS `col3` FROM...
- * </code>
- *
- * - If the *-wildcard char is used as key or value, add the full list of quoted table column names
- * <code>
- * array('col1' => 'NULL', '*')
- * // SELECT `col1`, `col2`, `col3`, NULL AS `col1` FROM...
- * </code>
- * @param mixed $value
- * @return array of generated fields - use implode(', ', $selectfieldlist) to collapse field list for usage
- * @since 3.1.7
- * @todo add support for table aliasing
- * @todo add support for quoting of column aliasing
- */
- public function getSelectFieldList($data='*') {
- if(is_scalar($data)) {
- $tmp = explode(',', $data);
- $result = array();
- foreach($tmp as $v)
- $result[] = trim($v);
- return $result;
- }
-
- $bHasWildcard = false;
- $result = array();
- if(is_array($data) || $data instanceof Traversable) {
- $columns = $this->getTableInfo()->getColumns();
- foreach($data as $key=>$value) {
- if($key==='*' || $value==='*') {
- $bHasWildcard = true;
- continue;
- }
-
- if(strToUpper($key)==='NULL') {
- $result[] = 'NULL';
- continue;
- }
-
- if(strpos($key, '(')!==false && strpos($key, ')')!==false) {
- $result[] = $key;
- continue;
- }
-
- if(stripos($key, 'AS')!==false) {
- $result[] = $key;
- continue;
- }
-
- if(stripos($value, 'AS')!==false) {
- $result[] = $value;
- continue;
- }
-
- $v = isset($columns[$value]);
- $k = isset($columns[$key]);
- if(is_integer($key) && $v) {
- $key = $value;
- $k = $v;
- }
-
- if(strToUpper($value)==='NULL') {
- if($k)
- $result[] = 'NULL AS ' . $columns[$key]->getColumnName();
- else
- $result[] = 'NULL' . (is_string($key) ? (' AS ' . (string)$key) : '');
- continue;
- }
-
- if(strpos($value, '(')!==false && strpos($value, ')')!==false) {
- if($k)
- $result[] = $value . ' AS ' . $columns[$key]->getColumnName();
- else
- $result[] = $value . (is_string($key) ? (' AS ' . (string)$key) : '');
- continue;
- }
-
- if($v && $key==$value) {
- $result[] = $columns[$value]->getColumnName();
- continue;
- }
-
- if($k && $value==null) {
- $result[] = $columns[$key]->getColumnName();
- continue;
- }
-
- if(is_string($key) && $v) {
- $result[] = $columns[$value]->getColumnName() . ' AS ' . $key;
- continue;
- }
-
- if(is_numeric($value) && $k) {
- $result[] = $value . ' AS ' . $columns[$key]->getColumnName();
- continue;
- }
-
- if(is_string($value) && $k) {
- $result[] = $this->getDbConnection()->quoteString($value) . ' AS ' . $columns[$key]->getColumnName();
- continue;
- }
-
- if(!$v && !$k && is_integer($key)) {
- $result[] = is_numeric($value) ? $value : $this->getDbConnection()->quoteString((string)$value);
- continue;
- }
-
- $result[] = (is_numeric($value) ? $value : $this->getDbConnection()->quoteString((string)$value)) . ' AS ' . $key;
- }
- }
-
- if($data===null || count($result) == 0 || $bHasWildcard)
- $result = $result = array_merge($this->getTableInfo()->getColumnNames(), $result);
-
- return $result;
- }
-
- /**
- * Appends the $where condition to the string "SELECT * FROM tableName WHERE ".
- * The tableName is obtained from the {@link setTableInfo TableInfo} property.
- * @param string query condition
- * @param array condition parameters.
- * @return TDbCommand query command.
- */
- public function createFindCommand($where='1=1', $parameters=array(), $ordering=array(), $limit=-1, $offset=-1, $select='*')
- {
- $table = $this->getTableInfo()->getTableFullName();
- $fields = implode(', ', $this -> getSelectFieldList($select));
- $sql = "SELECT {$fields} FROM {$table}";
- if(!empty($where))
- $sql .= " WHERE {$where}";
- return $this->applyCriterias($sql, $parameters, $ordering, $limit, $offset);
- }
-
- public function applyCriterias($sql, $parameters=array(),$ordering=array(), $limit=-1, $offset=-1)
- {
- if(count($ordering) > 0)
- $sql = $this->applyOrdering($sql, $ordering);
- if($limit>=0 || $offset>=0)
- $sql = $this->applyLimitOffset($sql, $limit, $offset);
- $command = $this->createCommand($sql);
- $this->bindArrayValues($command, $parameters);
- return $command;
- }
-
- /**
- * Creates a count(*) command for the table described in {@link setTableInfo TableInfo}.
- * @param string count condition.
- * @param array binding parameters.
- * @return TDbCommand count command.
- */
- public function createCountCommand($where='1=1', $parameters=array(),$ordering=array(), $limit=-1, $offset=-1)
- {
- return $this->createFindCommand($where, $parameters, $ordering, $limit, $offset, 'COUNT(*)');
- }
-
- /**
- * Creates a delete command for the table described in {@link setTableInfo TableInfo}.
- * The conditions for delete is given by the $where argument and the parameters
- * for the condition is given by $parameters.
- * @param string delete condition.
- * @param array delete parameters.
- * @return TDbCommand delete command.
- */
- public function createDeleteCommand($where,$parameters=array())
- {
- $table = $this->getTableInfo()->getTableFullName();
- if (!empty($where))
- $where = ' WHERE '.$where;
- $command = $this->createCommand("DELETE FROM {$table}".$where);
- $this->bindArrayValues($command, $parameters);
- return $command;
- }
-
- /**
- * Creates an insert command for the table described in {@link setTableInfo TableInfo} for the given data.
- * Each array key in the $data array must correspond to the column name of the table
- * (if a column allows to be null, it may be omitted) to be inserted with
- * the corresponding array value.
- * @param array name-value pairs of new data to be inserted.
- * @return TDbCommand insert command
- */
- public function createInsertCommand($data)
- {
- $table = $this->getTableInfo()->getTableFullName();
- list($fields, $bindings) = $this->getInsertFieldBindings($data);
- $command = $this->createCommand("INSERT INTO {$table}({$fields}) VALUES ($bindings)");
- $this->bindColumnValues($command, $data);
- return $command;
- }
-
- /**
- * Creates an update command for the table described in {@link setTableInfo TableInfo} for the given data.
- * Each array key in the $data array must correspond to the column name to be updated with the corresponding array value.
- * @param array name-value pairs of data to be updated.
- * @param string update condition.
- * @param array update parameters.
- * @return TDbCommand update command.
- */
- public function createUpdateCommand($data, $where, $parameters=array())
- {
- $table = $this->getTableInfo()->getTableFullName();
- if($this->hasIntegerKey($parameters))
- $fields = implode(', ', $this->getColumnBindings($data, true));
- else
- $fields = implode(', ', $this->getColumnBindings($data));
-
- if (!empty($where))
- $where = ' WHERE '.$where;
- $command = $this->createCommand("UPDATE {$table} SET {$fields}".$where);
- $this->bindArrayValues($command, array_merge($data, $parameters));
- return $command;
- }
-
- /**
- * Returns a list of insert field name and a list of binding names.
- * @param object array or object to be inserted.
- * @return array tuple ($fields, $bindings)
- */
- protected function getInsertFieldBindings($values)
- {
- $fields = array(); $bindings=array();
- foreach(array_keys($values) as $name)
- {
- $fields[] = $this->getTableInfo()->getColumn($name)->getColumnName();
- $bindings[] = ':'.$name;
- }
- return array(implode(', ',$fields), implode(', ', $bindings));
- }
-
- /**
- * Create a name-value or position-value if $position=true binding strings.
- * @param array data for binding.
- * @param boolean true to bind as position values.
- * @return string update column names with corresponding binding substrings.
- */
- protected function getColumnBindings($values, $position=false)
- {
- $bindings=array();
- foreach(array_keys($values) as $name)
- {
- $column = $this->getTableInfo()->getColumn($name)->getColumnName();
- $bindings[] = $position ? $column.' = ?' : $column.' = :'.$name;
- }
- return $bindings;
- }
-
- /**
- * @param string SQL query string.
- * @return TDbCommand corresponding database command.
- */
- public function createCommand($sql)
- {
- $this->getDbConnection()->setActive(true);
- return $this->getDbConnection()->createCommand($sql);
- }
-
- /**
- * Bind the name-value pairs of $values where the array keys correspond to column names.
- * @param TDbCommand database command.
- * @param array name-value pairs.
- */
- public function bindColumnValues($command, $values)
- {
- foreach($values as $name=>$value)
- {
- $column = $this->getTableInfo()->getColumn($name);
- if($value === null && $column->getAllowNull())
- $command->bindValue(':'.$name, null, PDO::PARAM_NULL);
- else
- $command->bindValue(':'.$name, $value, $column->getPdoType());
- }
- }
-
- /**
- * @param TDbCommand database command
- * @param array values for binding.
- */
- public function bindArrayValues($command, $values)
- {
- if($this->hasIntegerKey($values))
- {
- $values = array_values($values);
- for($i = 0, $max=count($values); $i<$max; $i++)
- $command->bindValue($i+1, $values[$i], $this->getPdoType($values[$i]));
- }
- else
- {
- foreach($values as $name=>$value)
- {
- $prop = $name[0]===':' ? $name : ':'.$name;
- $command->bindValue($prop, $value, $this->getPdoType($value));
- }
- }
- }
-
- /**
- * @param mixed PHP value
- * @return integer PDO parameter types.
- */
- public static function getPdoType($value)
- {
- switch(gettype($value))
- {
- case 'boolean': return PDO::PARAM_BOOL;
- case 'integer': return PDO::PARAM_INT;
- case 'string' : return PDO::PARAM_STR;
- case 'NULL' : return PDO::PARAM_NULL;
- }
- }
-
- /**
- * @param array
- * @return boolean true if any array key is an integer.
- */
- protected function hasIntegerKey($array)
- {
- foreach($array as $k=>$v)
- {
- if(gettype($k)==='integer')
- return true;
- }
- return false;
- }
-}
+<?php +/** + * TDbCommandBuilder class file. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.Common + */ + +/** + * TDbCommandBuilder provides basic methods to create query commands for tables + * giving by {@link setTableInfo TableInfo} the property. + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.Common + * @since 3.1 + */ +class TDbCommandBuilder extends TComponent +{ + private $_connection; + private $_tableInfo; + + /** + * @param TDbConnection database connection. + * @param TDbTableInfo table information. + */ + public function __construct($connection=null, $tableInfo=null) + { + $this->setDbConnection($connection); + $this->setTableInfo($tableInfo); + } + + /** + * @return TDbConnection database connection. + */ + public function getDbConnection() + { + return $this->_connection; + } + + /** + * @param TDbConnection database connection. + */ + public function setDbConnection($value) + { + $this->_connection=$value; + } + + /** + * @param TDbTableInfo table information. + */ + public function setTableInfo($value) + { + $this->_tableInfo=$value; + } + + /** + * @param TDbTableInfo table information. + */ + public function getTableInfo() + { + 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. + * @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 ? (int)$limit : -1; + $offset = $offset!==null ? (int)$offset : -1; + $limitStr = $limit >= 0 ? ' LIMIT '.$limit : ''; + $offsetStr = $offset >= 0 ? ' OFFSET '.$offset : ''; + return $sql.$limitStr.$offsetStr; + } + + /** + * @param string SQL string without existing ordering. + * @param array pairs of column names as key and direction as value. + * @return string modified SQL applied with ORDER BY. + */ + public function applyOrdering($sql, $ordering) + { + $orders=array(); + foreach($ordering as $name => $direction) + { + $direction = strtolower($direction) == 'desc' ? 'DESC' : 'ASC'; + if(false !== strpos($name, '(') && false !== strpos($name, ')')) { + // key is a function (bad practice, but we need to handle it) + $key = $name; + } else { + // key is a column + $key = $this->getTableInfo()->getColumn($name)->getColumnName(); + } + $orders[] = $key.' '.$direction; + } + if(count($orders) > 0) + $sql .= ' ORDER BY '.implode(', ', $orders); + return $sql; + } + + /** + * Computes the SQL condition for search a set of column using regular expression + * (or LIKE, depending on database implementation) to match a string of + * keywords (default matches all keywords). + * @param array list of column id for potential search condition. + * @param string string of keywords + * @return string SQL search condition matching on a set of columns. + */ + public function getSearchExpression($fields, $keywords) + { + if(strlen(trim($keywords)) == 0) return ''; + $words = preg_split('/\s/u', $keywords); + $conditions = array(); + foreach($fields as $field) + { + $column = $this->getTableInfo()->getColumn($field)->getColumnName(); + $conditions[] = $this->getSearchCondition($column, $words); + } + return '('.implode(' OR ', $conditions).')'; + } + + /** + * @param string column name. + * @param array keywords + * @return string search condition for all words in one column. + */ + protected function getSearchCondition($column, $words) + { + $conditions=array(); + foreach($words as $word) + $conditions[] = $column.' LIKE '.$this->getDbConnection()->quoteString('%'.$word.'%'); + return '('.implode(' AND ', $conditions).')'; + } + + /** + * + * Different behavior depends on type of passed data + * string + * usage without modification + * + * null + * will be expanded to full list of quoted table column names (quoting depends on database) + * + * array + * - Column names will be quoted if used as key or value of array + * <code> + * array('col1', 'col2', 'col2') + * // SELECT `col1`, `col2`, `col3` FROM... + * </code> + * + * - Column aliasing + * <code> + * array('mycol1' => 'col1', 'mycol2' => 'COUNT(*)') + * // SELECT `col1` AS mycol1, COUNT(*) AS mycol2 FROM... + * </code> + * + * - NULL and scalar values (strings will be quoted depending on database) + * <code> + * array('col1' => 'my custom string', 'col2' => 1.0, 'col3' => 'NULL') + * // SELECT "my custom string" AS `col1`, 1.0 AS `col2`, NULL AS `col3` FROM... + * </code> + * + * - If the *-wildcard char is used as key or value, add the full list of quoted table column names + * <code> + * array('col1' => 'NULL', '*') + * // SELECT `col1`, `col2`, `col3`, NULL AS `col1` FROM... + * </code> + * @param mixed $value + * @return array of generated fields - use implode(', ', $selectfieldlist) to collapse field list for usage + * @since 3.1.7 + * @todo add support for table aliasing + * @todo add support for quoting of column aliasing + */ + public function getSelectFieldList($data='*') { + if(is_scalar($data)) { + $tmp = explode(',', $data); + $result = array(); + foreach($tmp as $v) + $result[] = trim($v); + return $result; + } + + $bHasWildcard = false; + $result = array(); + if(is_array($data) || $data instanceof Traversable) { + $columns = $this->getTableInfo()->getColumns(); + foreach($data as $key=>$value) { + if($key==='*' || $value==='*') { + $bHasWildcard = true; + continue; + } + + if(strToUpper($key)==='NULL') { + $result[] = 'NULL'; + continue; + } + + if(strpos($key, '(')!==false && strpos($key, ')')!==false) { + $result[] = $key; + continue; + } + + if(stripos($key, 'AS')!==false) { + $result[] = $key; + continue; + } + + if(stripos($value, 'AS')!==false) { + $result[] = $value; + continue; + } + + $v = isset($columns[$value]); + $k = isset($columns[$key]); + if(is_integer($key) && $v) { + $key = $value; + $k = $v; + } + + if(strToUpper($value)==='NULL') { + if($k) + $result[] = 'NULL AS ' . $columns[$key]->getColumnName(); + else + $result[] = 'NULL' . (is_string($key) ? (' AS ' . (string)$key) : ''); + continue; + } + + if(strpos($value, '(')!==false && strpos($value, ')')!==false) { + if($k) + $result[] = $value . ' AS ' . $columns[$key]->getColumnName(); + else + $result[] = $value . (is_string($key) ? (' AS ' . (string)$key) : ''); + continue; + } + + if($v && $key==$value) { + $result[] = $columns[$value]->getColumnName(); + continue; + } + + if($k && $value==null) { + $result[] = $columns[$key]->getColumnName(); + continue; + } + + if(is_string($key) && $v) { + $result[] = $columns[$value]->getColumnName() . ' AS ' . $key; + continue; + } + + if(is_numeric($value) && $k) { + $result[] = $value . ' AS ' . $columns[$key]->getColumnName(); + continue; + } + + if(is_string($value) && $k) { + $result[] = $this->getDbConnection()->quoteString($value) . ' AS ' . $columns[$key]->getColumnName(); + continue; + } + + if(!$v && !$k && is_integer($key)) { + $result[] = is_numeric($value) ? $value : $this->getDbConnection()->quoteString((string)$value); + continue; + } + + $result[] = (is_numeric($value) ? $value : $this->getDbConnection()->quoteString((string)$value)) . ' AS ' . $key; + } + } + + if($data===null || count($result) == 0 || $bHasWildcard) + $result = $result = array_merge($this->getTableInfo()->getColumnNames(), $result); + + return $result; + } + + /** + * Appends the $where condition to the string "SELECT * FROM tableName WHERE ". + * The tableName is obtained from the {@link setTableInfo TableInfo} property. + * @param string query condition + * @param array condition parameters. + * @return TDbCommand query command. + */ + public function createFindCommand($where='1=1', $parameters=array(), $ordering=array(), $limit=-1, $offset=-1, $select='*') + { + $table = $this->getTableInfo()->getTableFullName(); + $fields = implode(', ', $this -> getSelectFieldList($select)); + $sql = "SELECT {$fields} FROM {$table}"; + if(!empty($where)) + $sql .= " WHERE {$where}"; + return $this->applyCriterias($sql, $parameters, $ordering, $limit, $offset); + } + + public function applyCriterias($sql, $parameters=array(),$ordering=array(), $limit=-1, $offset=-1) + { + if(count($ordering) > 0) + $sql = $this->applyOrdering($sql, $ordering); + if($limit>=0 || $offset>=0) + $sql = $this->applyLimitOffset($sql, $limit, $offset); + $command = $this->createCommand($sql); + $this->bindArrayValues($command, $parameters); + return $command; + } + + /** + * Creates a count(*) command for the table described in {@link setTableInfo TableInfo}. + * @param string count condition. + * @param array binding parameters. + * @return TDbCommand count command. + */ + public function createCountCommand($where='1=1', $parameters=array(),$ordering=array(), $limit=-1, $offset=-1) + { + return $this->createFindCommand($where, $parameters, $ordering, $limit, $offset, 'COUNT(*)'); + } + + /** + * Creates a delete command for the table described in {@link setTableInfo TableInfo}. + * The conditions for delete is given by the $where argument and the parameters + * for the condition is given by $parameters. + * @param string delete condition. + * @param array delete parameters. + * @return TDbCommand delete command. + */ + public function createDeleteCommand($where,$parameters=array()) + { + $table = $this->getTableInfo()->getTableFullName(); + if (!empty($where)) + $where = ' WHERE '.$where; + $command = $this->createCommand("DELETE FROM {$table}".$where); + $this->bindArrayValues($command, $parameters); + return $command; + } + + /** + * Creates an insert command for the table described in {@link setTableInfo TableInfo} for the given data. + * Each array key in the $data array must correspond to the column name of the table + * (if a column allows to be null, it may be omitted) to be inserted with + * the corresponding array value. + * @param array name-value pairs of new data to be inserted. + * @return TDbCommand insert command + */ + public function createInsertCommand($data) + { + $table = $this->getTableInfo()->getTableFullName(); + list($fields, $bindings) = $this->getInsertFieldBindings($data); + $command = $this->createCommand("INSERT INTO {$table}({$fields}) VALUES ($bindings)"); + $this->bindColumnValues($command, $data); + return $command; + } + + /** + * Creates an update command for the table described in {@link setTableInfo TableInfo} for the given data. + * Each array key in the $data array must correspond to the column name to be updated with the corresponding array value. + * @param array name-value pairs of data to be updated. + * @param string update condition. + * @param array update parameters. + * @return TDbCommand update command. + */ + public function createUpdateCommand($data, $where, $parameters=array()) + { + $table = $this->getTableInfo()->getTableFullName(); + if($this->hasIntegerKey($parameters)) + $fields = implode(', ', $this->getColumnBindings($data, true)); + else + $fields = implode(', ', $this->getColumnBindings($data)); + + if (!empty($where)) + $where = ' WHERE '.$where; + $command = $this->createCommand("UPDATE {$table} SET {$fields}".$where); + $this->bindArrayValues($command, array_merge($data, $parameters)); + return $command; + } + + /** + * Returns a list of insert field name and a list of binding names. + * @param object array or object to be inserted. + * @return array tuple ($fields, $bindings) + */ + protected function getInsertFieldBindings($values) + { + $fields = array(); $bindings=array(); + foreach(array_keys($values) as $name) + { + $fields[] = $this->getTableInfo()->getColumn($name)->getColumnName(); + $bindings[] = ':'.$name; + } + return array(implode(', ',$fields), implode(', ', $bindings)); + } + + /** + * Create a name-value or position-value if $position=true binding strings. + * @param array data for binding. + * @param boolean true to bind as position values. + * @return string update column names with corresponding binding substrings. + */ + protected function getColumnBindings($values, $position=false) + { + $bindings=array(); + foreach(array_keys($values) as $name) + { + $column = $this->getTableInfo()->getColumn($name)->getColumnName(); + $bindings[] = $position ? $column.' = ?' : $column.' = :'.$name; + } + return $bindings; + } + + /** + * @param string SQL query string. + * @return TDbCommand corresponding database command. + */ + public function createCommand($sql) + { + $this->getDbConnection()->setActive(true); + return $this->getDbConnection()->createCommand($sql); + } + + /** + * Bind the name-value pairs of $values where the array keys correspond to column names. + * @param TDbCommand database command. + * @param array name-value pairs. + */ + public function bindColumnValues($command, $values) + { + foreach($values as $name=>$value) + { + $column = $this->getTableInfo()->getColumn($name); + if($value === null && $column->getAllowNull()) + $command->bindValue(':'.$name, null, PDO::PARAM_NULL); + else + $command->bindValue(':'.$name, $value, $column->getPdoType()); + } + } + + /** + * @param TDbCommand database command + * @param array values for binding. + */ + public function bindArrayValues($command, $values) + { + if($this->hasIntegerKey($values)) + { + $values = array_values($values); + for($i = 0, $max=count($values); $i<$max; $i++) + $command->bindValue($i+1, $values[$i], $this->getPdoType($values[$i])); + } + else + { + foreach($values as $name=>$value) + { + $prop = $name[0]===':' ? $name : ':'.$name; + $command->bindValue($prop, $value, $this->getPdoType($value)); + } + } + } + + /** + * @param mixed PHP value + * @return integer PDO parameter types. + */ + public static function getPdoType($value) + { + switch(gettype($value)) + { + case 'boolean': return PDO::PARAM_BOOL; + case 'integer': return PDO::PARAM_INT; + case 'string' : return PDO::PARAM_STR; + case 'NULL' : return PDO::PARAM_NULL; + } + } + + /** + * @param array + * @return boolean true if any array key is an integer. + */ + protected function hasIntegerKey($array) + { + foreach($array as $k=>$v) + { + if(gettype($k)==='integer') + return true; + } + return false; + } +} diff --git a/framework/Data/Common/TDbMetaData.php b/framework/Data/Common/TDbMetaData.php index fdf367c9..5fc4fec9 100644 --- a/framework/Data/Common/TDbMetaData.php +++ b/framework/Data/Common/TDbMetaData.php @@ -1,183 +1,183 @@ -<?php
-/**
- * TDbMetaData class file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
+<?php +/** + * TDbMetaData class file. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.Common
- */
-
-/**
- * TDbMetaData is the base class for retrieving metadata information, such as
- * table and columns information, from a database connection.
- *
- * Use the {@link getTableInfo} method to retrieve a table information.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.Common
- * @since 3.1
- */
-abstract class TDbMetaData extends TComponent
-{
- private $_tableInfoCache=array();
- private $_connection;
-
- /**
- * @var array
- */
- protected static $delimiterIdentifier = array('[', ']', '"', '`', "'");
-
- /**
- * @param TDbConnection database connection.
- */
- public function __construct($conn)
- {
- $this->_connection=$conn;
- }
-
- /**
- * @return TDbConnection database connection.
- */
- public function getDbConnection()
- {
- return $this->_connection;
- }
-
- /**
- * Obtain database specific TDbMetaData class using the driver name of the database connection.
- * @param TDbConnection database connection.
- * @return TDbMetaData database specific TDbMetaData.
- */
- public static function getInstance($conn)
- {
- $conn->setActive(true); //must be connected before retrieving driver name
- $driver = $conn->getDriverName();
- switch(strtolower($driver))
- {
- case 'pgsql':
- Prado::using('System.Data.Common.Pgsql.TPgsqlMetaData');
- return new TPgsqlMetaData($conn);
- case 'mysqli':
- case 'mysql':
- Prado::using('System.Data.Common.Mysql.TMysqlMetaData');
- return new TMysqlMetaData($conn);
- case 'sqlite': //sqlite 3
- case 'sqlite2': //sqlite 2
- Prado::using('System.Data.Common.Sqlite.TSqliteMetaData');
- return new TSqliteMetaData($conn);
- case 'mssql': // Mssql driver on windows hosts
- case 'dblib': // dblib drivers on linux (and maybe others os) hosts
- Prado::using('System.Data.Common.Mssql.TMssqlMetaData');
- return new TMssqlMetaData($conn);
- case 'oci':
- Prado::using('System.Data.Common.Oracle.TOracleMetaData');
- return new TOracleMetaData($conn);
-// case 'ibm':
-// Prado::using('System.Data.Common.IbmDb2.TIbmDb2MetaData');
-// return new TIbmDb2MetaData($conn);
- default:
- throw new TDbException('ar_invalid_database_driver',$driver);
- }
- }
-
- /**
- * Obtains table meta data information for the current connection and given table name.
- * @param string table or view name
- * @return TDbTableInfo table information.
- */
- public function getTableInfo($tableName=null)
- {
- $key = $tableName===null?$this->getDbConnection()->getConnectionString():$tableName;
- if(!isset($this->_tableInfoCache[$key]))
- {
- $class = $this->getTableInfoClass();
- $tableInfo = $tableName===null ? new $class : $this->createTableInfo($tableName);
- $this->_tableInfoCache[$key] = $tableInfo;
- }
- return $this->_tableInfoCache[$key];
- }
-
- /**
- * Creates a command builder for a given table name.
- * @param string table name.
- * @return TDbCommandBuilder command builder instance for the given table.
- */
- public function createCommandBuilder($tableName=null)
- {
- return $this->getTableInfo($tableName)->createCommandBuilder($this->getDbConnection());
- }
-
- /**
- * This method should be implemented by decendent classes.
- * @return TDbTableInfo driver dependent create builder.
- */
- abstract protected function createTableInfo($tableName);
-
- /**
- * @return string TDbTableInfo class name.
- */
- protected function getTableInfoClass()
- {
- return 'TDbTableInfo';
- }
-
- /**
- * Quotes a table name for use in a query.
- * @param string $name table name
- * @param string $lft left delimiter
- * @param string $rgt right delimiter
- * @return string the properly quoted table name
- */
- public function quoteTableName($name)
- {
- $name = str_replace(self::$delimiterIdentifier, '', $name);
-
- $args = func_get_args();
- $rgt = $lft = isset($args[1]) ? $args[1] : '';
- $rgt = isset($args[2]) ? $args[2] : $rgt;
-
- if(strpos($name, '.')===false)
- return $lft . $name . $rgt;
- $names=explode('.', $name);
- foreach($names as &$n)
- $n = $lft . $n . $rgt;
- return implode('.', $names);
- }
-
- /**
- * Quotes a column name for use in a query.
- * @param string $name column name
- * @param string $lft left delimiter
- * @param string $rgt right delimiter
- * @return string the properly quoted column name
- */
- public function quoteColumnName($name)
- {
- $args = func_get_args();
- $rgt = $lft = isset($args[1]) ? $args[1] : '';
- $rgt = isset($args[2]) ? $args[2] : $rgt;
-
- return $lft . str_replace(self::$delimiterIdentifier, '', $name) . $rgt;
- }
-
- /**
- * Quotes a column alias for use in a query.
- * @param string $name column alias
- * @param string $lft left delimiter
- * @param string $rgt right delimiter
- * @return string the properly quoted column alias
- */
- public function quoteColumnAlias($name)
- {
- $args = func_get_args();
- $rgt = $lft = isset($args[1]) ? $args[1] : '';
- $rgt = isset($args[2]) ? $args[2] : $rgt;
-
- return $lft . str_replace(self::$delimiterIdentifier, '', $name) . $rgt;
- }
-}
-
+ * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.Common + */ + +/** + * TDbMetaData is the base class for retrieving metadata information, such as + * table and columns information, from a database connection. + * + * Use the {@link getTableInfo} method to retrieve a table information. + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.Common + * @since 3.1 + */ +abstract class TDbMetaData extends TComponent +{ + private $_tableInfoCache=array(); + private $_connection; + + /** + * @var array + */ + protected static $delimiterIdentifier = array('[', ']', '"', '`', "'"); + + /** + * @param TDbConnection database connection. + */ + public function __construct($conn) + { + $this->_connection=$conn; + } + + /** + * @return TDbConnection database connection. + */ + public function getDbConnection() + { + return $this->_connection; + } + + /** + * Obtain database specific TDbMetaData class using the driver name of the database connection. + * @param TDbConnection database connection. + * @return TDbMetaData database specific TDbMetaData. + */ + public static function getInstance($conn) + { + $conn->setActive(true); //must be connected before retrieving driver name + $driver = $conn->getDriverName(); + switch(strtolower($driver)) + { + case 'pgsql': + Prado::using('System.Data.Common.Pgsql.TPgsqlMetaData'); + return new TPgsqlMetaData($conn); + case 'mysqli': + case 'mysql': + Prado::using('System.Data.Common.Mysql.TMysqlMetaData'); + return new TMysqlMetaData($conn); + case 'sqlite': //sqlite 3 + case 'sqlite2': //sqlite 2 + Prado::using('System.Data.Common.Sqlite.TSqliteMetaData'); + return new TSqliteMetaData($conn); + case 'mssql': // Mssql driver on windows hosts + case 'dblib': // dblib drivers on linux (and maybe others os) hosts + Prado::using('System.Data.Common.Mssql.TMssqlMetaData'); + return new TMssqlMetaData($conn); + case 'oci': + Prado::using('System.Data.Common.Oracle.TOracleMetaData'); + return new TOracleMetaData($conn); +// case 'ibm': +// Prado::using('System.Data.Common.IbmDb2.TIbmDb2MetaData'); +// return new TIbmDb2MetaData($conn); + default: + throw new TDbException('ar_invalid_database_driver',$driver); + } + } + + /** + * Obtains table meta data information for the current connection and given table name. + * @param string table or view name + * @return TDbTableInfo table information. + */ + public function getTableInfo($tableName=null) + { + $key = $tableName===null?$this->getDbConnection()->getConnectionString():$tableName; + if(!isset($this->_tableInfoCache[$key])) + { + $class = $this->getTableInfoClass(); + $tableInfo = $tableName===null ? new $class : $this->createTableInfo($tableName); + $this->_tableInfoCache[$key] = $tableInfo; + } + return $this->_tableInfoCache[$key]; + } + + /** + * Creates a command builder for a given table name. + * @param string table name. + * @return TDbCommandBuilder command builder instance for the given table. + */ + public function createCommandBuilder($tableName=null) + { + return $this->getTableInfo($tableName)->createCommandBuilder($this->getDbConnection()); + } + + /** + * This method should be implemented by decendent classes. + * @return TDbTableInfo driver dependent create builder. + */ + abstract protected function createTableInfo($tableName); + + /** + * @return string TDbTableInfo class name. + */ + protected function getTableInfoClass() + { + return 'TDbTableInfo'; + } + + /** + * Quotes a table name for use in a query. + * @param string $name table name + * @param string $lft left delimiter + * @param string $rgt right delimiter + * @return string the properly quoted table name + */ + public function quoteTableName($name) + { + $name = str_replace(self::$delimiterIdentifier, '', $name); + + $args = func_get_args(); + $rgt = $lft = isset($args[1]) ? $args[1] : ''; + $rgt = isset($args[2]) ? $args[2] : $rgt; + + if(strpos($name, '.')===false) + return $lft . $name . $rgt; + $names=explode('.', $name); + foreach($names as &$n) + $n = $lft . $n . $rgt; + return implode('.', $names); + } + + /** + * Quotes a column name for use in a query. + * @param string $name column name + * @param string $lft left delimiter + * @param string $rgt right delimiter + * @return string the properly quoted column name + */ + public function quoteColumnName($name) + { + $args = func_get_args(); + $rgt = $lft = isset($args[1]) ? $args[1] : ''; + $rgt = isset($args[2]) ? $args[2] : $rgt; + + return $lft . str_replace(self::$delimiterIdentifier, '', $name) . $rgt; + } + + /** + * Quotes a column alias for use in a query. + * @param string $name column alias + * @param string $lft left delimiter + * @param string $rgt right delimiter + * @return string the properly quoted column alias + */ + public function quoteColumnAlias($name) + { + $args = func_get_args(); + $rgt = $lft = isset($args[1]) ? $args[1] : ''; + $rgt = isset($args[2]) ? $args[2] : $rgt; + + return $lft . str_replace(self::$delimiterIdentifier, '', $name) . $rgt; + } +} + diff --git a/framework/Data/Common/TDbTableColumn.php b/framework/Data/Common/TDbTableColumn.php index 8a0832d8..3967ec0c 100644 --- a/framework/Data/Common/TDbTableColumn.php +++ b/framework/Data/Common/TDbTableColumn.php @@ -1,199 +1,199 @@ -<?php
-/**
- * TDbTableColumn class file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
+<?php +/** + * TDbTableColumn class file. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.Common
- */
-
-/**
- * TDbTableColumn class describes the column meta data of the schema for a database table.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.Common
- * @since 3.1
- */
-class TDbTableColumn extends TComponent
-{
- const UNDEFINED_VALUE= INF; //use infinity for undefined value
-
- private $_info=array();
-
- /**
- * Sets the table column meta data.
- * @param array table column information.
- */
- public function __construct($columnInfo)
- {
- $this->_info=$columnInfo;
- }
-
- /**
- * @param string information array key name
- * @param mixed default value if information array value is null
- * @return mixed information array value.
- */
- protected function getInfo($name,$default=null)
- {
- return isset($this->_info[$name]) ? $this->_info[$name] : $default;
- }
-
- /**
- * @param string information array key name
- * @param mixed new information array value.
- */
- protected function setInfo($name,$value)
- {
- $this->_info[$name]=$value;
- }
-
- /**
- * Returns the derived PHP primitive type from the db type. Default returns 'string'.
- * @return string derived PHP primitive type from the column db type.
- */
- public function getPHPType()
- {
- return 'string';
- }
-
- /**
- * @param integer PDO bind param/value types, default returns string.
- */
- public function getPdoType()
- {
- switch($this->getPHPType())
- {
- case 'boolean': return PDO::PARAM_BOOL;
- case 'integer': return PDO::PARAM_INT;
- case 'string' : return PDO::PARAM_STR;
- }
- return PDO::PARAM_STR;
- }
-
- /**
- * @return string name of the column in the table (identifier quoted).
- */
- public function getColumnName()
- {
- 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.
- */
- public function getColumnSize()
- {
- return $this->getInfo('ColumnSize');
- }
-
- /**
- * @return integer zero-based ordinal position of the column in the table.
- */
- public function getColumnIndex()
- {
- return $this->getInfo('ColumnIndex');
- }
-
- /**
- * @return string column type.
- */
- public function getDbType()
- {
- return $this->getInfo('DbType');
- }
-
- /**
- * @return boolean specifies whether value Null is allowed, default is false.
- */
- public function getAllowNull()
- {
- return $this->getInfo('AllowNull',false);
- }
-
- /**
- * @return mixed default column value if column value was null.
- */
- public function getDefaultValue()
- {
- return $this->getInfo('DefaultValue', self::UNDEFINED_VALUE);
- }
-
- /**
- * @return string precision of the column data, if the data is numeric.
- */
- public function getNumericPrecision()
- {
- return $this->getInfo('NumericPrecision');
- }
-
- /**
- * @return string scale of the column data, if the data is numeric.
- */
- public function getNumericScale()
- {
- 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.
- */
- public function getIsPrimaryKey()
- {
- return $this->getInfo('IsPrimaryKey',false);
- }
-
- /**
- * @return boolean whether this column is a foreign key, default is false.
- */
- public function getIsForeignKey()
- {
- return $this->getInfo('IsForeignKey',false);
- }
-
- /**
- * @param string sequence name, only applicable if column is a sequence
- */
- public function getSequenceName()
- {
- return $this->getInfo('SequenceName');
- }
-
- /**
- * @return boolean whether the column is a sequence.
- */
- public function hasSequence()
- {
- return $this->getSequenceName()!==null;
- }
-
- /**
- * @return boolean whether this column is excluded from insert and update.
- */
- public function getIsExcluded()
- {
- return false;
- }
-}
-
+ * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.Common + */ + +/** + * TDbTableColumn class describes the column meta data of the schema for a database table. + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.Common + * @since 3.1 + */ +class TDbTableColumn extends TComponent +{ + const UNDEFINED_VALUE= INF; //use infinity for undefined value + + private $_info=array(); + + /** + * Sets the table column meta data. + * @param array table column information. + */ + public function __construct($columnInfo) + { + $this->_info=$columnInfo; + } + + /** + * @param string information array key name + * @param mixed default value if information array value is null + * @return mixed information array value. + */ + protected function getInfo($name,$default=null) + { + return isset($this->_info[$name]) ? $this->_info[$name] : $default; + } + + /** + * @param string information array key name + * @param mixed new information array value. + */ + protected function setInfo($name,$value) + { + $this->_info[$name]=$value; + } + + /** + * Returns the derived PHP primitive type from the db type. Default returns 'string'. + * @return string derived PHP primitive type from the column db type. + */ + public function getPHPType() + { + return 'string'; + } + + /** + * @param integer PDO bind param/value types, default returns string. + */ + public function getPdoType() + { + switch($this->getPHPType()) + { + case 'boolean': return PDO::PARAM_BOOL; + case 'integer': return PDO::PARAM_INT; + case 'string' : return PDO::PARAM_STR; + } + return PDO::PARAM_STR; + } + + /** + * @return string name of the column in the table (identifier quoted). + */ + public function getColumnName() + { + 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. + */ + public function getColumnSize() + { + return $this->getInfo('ColumnSize'); + } + + /** + * @return integer zero-based ordinal position of the column in the table. + */ + public function getColumnIndex() + { + return $this->getInfo('ColumnIndex'); + } + + /** + * @return string column type. + */ + public function getDbType() + { + return $this->getInfo('DbType'); + } + + /** + * @return boolean specifies whether value Null is allowed, default is false. + */ + public function getAllowNull() + { + return $this->getInfo('AllowNull',false); + } + + /** + * @return mixed default column value if column value was null. + */ + public function getDefaultValue() + { + return $this->getInfo('DefaultValue', self::UNDEFINED_VALUE); + } + + /** + * @return string precision of the column data, if the data is numeric. + */ + public function getNumericPrecision() + { + return $this->getInfo('NumericPrecision'); + } + + /** + * @return string scale of the column data, if the data is numeric. + */ + public function getNumericScale() + { + 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. + */ + public function getIsPrimaryKey() + { + return $this->getInfo('IsPrimaryKey',false); + } + + /** + * @return boolean whether this column is a foreign key, default is false. + */ + public function getIsForeignKey() + { + return $this->getInfo('IsForeignKey',false); + } + + /** + * @param string sequence name, only applicable if column is a sequence + */ + public function getSequenceName() + { + return $this->getInfo('SequenceName'); + } + + /** + * @return boolean whether the column is a sequence. + */ + public function hasSequence() + { + return $this->getSequenceName()!==null; + } + + /** + * @return boolean whether this column is excluded from insert and update. + */ + public function getIsExcluded() + { + return false; + } +} + diff --git a/framework/Data/Common/TDbTableInfo.php b/framework/Data/Common/TDbTableInfo.php index 5342212e..c5c79e5b 100644 --- a/framework/Data/Common/TDbTableInfo.php +++ b/framework/Data/Common/TDbTableInfo.php @@ -1,166 +1,166 @@ <?php -/**
- * TDbTableInfo class file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
+/** + * TDbTableInfo class file. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.Common
- */
-
-/**
- * TDbTableInfo class describes the meta data of a database table.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.Common
- * @since 3.1
- */
-class TDbTableInfo extends TComponent
-{
- private $_info=array();
-
- private $_primaryKeys;
- private $_foreignKeys;
-
- private $_columns;
-
+ * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.Common + */ + +/** + * TDbTableInfo class describes the meta data of a database table. + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.Common + * @since 3.1 + */ +class TDbTableInfo extends TComponent +{ + private $_info=array(); + + private $_primaryKeys; + private $_foreignKeys; + + private $_columns; + private $_lowercase; /** * @var null|array * @since 3.1.7 - */
- private $_names = null;
-
- /**
- * Sets the database table meta data information.
- * @param array table column information.
- */
- public function __construct($tableInfo=array(),$primary=array(),$foreign=array())
- {
- $this->_info=$tableInfo;
- $this->_primaryKeys=$primary;
- $this->_foreignKeys=$foreign;
- $this->_columns=new TMap;
- }
-
- /**
- * @param TDbConnection database connection.
- * @return TDbCommandBuilder new command builder
- */
- public function createCommandBuilder($connection)
- {
- Prado::using('System.Data.Common.TDbCommandBuilder');
- return new TDbCommandBuilder($connection,$this);
- }
-
- /**
- * @param string information array key name
- * @param mixed default value if information array value is null
- * @return mixed information array value.
- */
- protected function getInfo($name,$default=null)
- {
- return isset($this->_info[$name]) ? $this->_info[$name] : $default;
- }
-
- /**
- * @param string information array key name
- * @param mixed new information array value.
- */
- protected function setInfo($name,$value)
- {
- $this->_info[$name]=$value;
- }
-
- /**
- * @return string name of the table this column belongs to.
- */
- public function getTableName()
- {
- return $this->getInfo('TableName');
- }
-
- /**
- * @return string full name of the table, database dependent.
- */
- public function getTableFullName()
- {
- return $this->getTableName();
- }
-
- /**
- * @return boolean whether the table is a view, default is false.
- */
- public function getIsView()
- {
- return $this->getInfo('IsView',false);
- }
-
- /**
- * @return TMap TDbTableColumn column meta data.
- */
- public function getColumns()
- {
- return $this->_columns;
- }
-
- /**
- * @param string column id
- * @return TDbTableColumn column information.
- */
- public function getColumn($name)
- {
- if(($column = $this->_columns->itemAt($name))!==null)
- return $column;
- throw new TDbException('dbtableinfo_invalid_column_name', $name, $this->getTableFullName());
- }
-
- /**
- * @param array list of column Id, empty to get all columns.
- * @return array table column names (identifier quoted)
- */
- public function getColumnNames()
+ */ + private $_names = null; + + /** + * Sets the database table meta data information. + * @param array table column information. + */ + public function __construct($tableInfo=array(),$primary=array(),$foreign=array()) + { + $this->_info=$tableInfo; + $this->_primaryKeys=$primary; + $this->_foreignKeys=$foreign; + $this->_columns=new TMap; + } + + /** + * @param TDbConnection database connection. + * @return TDbCommandBuilder new command builder + */ + public function createCommandBuilder($connection) + { + Prado::using('System.Data.Common.TDbCommandBuilder'); + return new TDbCommandBuilder($connection,$this); + } + + /** + * @param string information array key name + * @param mixed default value if information array value is null + * @return mixed information array value. + */ + protected function getInfo($name,$default=null) + { + return isset($this->_info[$name]) ? $this->_info[$name] : $default; + } + + /** + * @param string information array key name + * @param mixed new information array value. + */ + protected function setInfo($name,$value) + { + $this->_info[$name]=$value; + } + + /** + * @return string name of the table this column belongs to. + */ + public function getTableName() + { + return $this->getInfo('TableName'); + } + + /** + * @return string full name of the table, database dependent. + */ + public function getTableFullName() + { + return $this->getTableName(); + } + + /** + * @return boolean whether the table is a view, default is false. + */ + public function getIsView() + { + return $this->getInfo('IsView',false); + } + + /** + * @return TMap TDbTableColumn column meta data. + */ + public function getColumns() + { + return $this->_columns; + } + + /** + * @param string column id + * @return TDbTableColumn column information. + */ + public function getColumn($name) + { + if(($column = $this->_columns->itemAt($name))!==null) + return $column; + throw new TDbException('dbtableinfo_invalid_column_name', $name, $this->getTableFullName()); + } + + /** + * @param array list of column Id, empty to get all columns. + * @return array table column names (identifier quoted) + */ + public function getColumnNames() { if($this->_names===null) - {
- $this->_names=array();
- foreach($this->getColumns() as $column)
+ { + $this->_names=array(); + foreach($this->getColumns() as $column) $this->_names[] = $column->getColumnName(); - }
- return $this->_names;
- }
-
- /**
- * @return string[] names of primary key columns.
- */
- public function getPrimaryKeys()
- {
- return $this->_primaryKeys;
- }
-
- /**
- * @return array tuples of foreign table and column name.
- */
- public function getForeignKeys()
- {
- return $this->_foreignKeys;
- }
-
- /**
- * @return array lowercased column key names mapped to normal column ids.
- */
- public function getLowerCaseColumnNames()
- {
- if($this->_lowercase===null)
- {
- $this->_lowercase=array();
- foreach($this->getColumns()->getKeys() as $key)
- $this->_lowercase[strtolower($key)] = $key;
- }
- return $this->_lowercase;
- }
+ } + return $this->_names; + } + + /** + * @return string[] names of primary key columns. + */ + public function getPrimaryKeys() + { + return $this->_primaryKeys; + } + + /** + * @return array tuples of foreign table and column name. + */ + public function getForeignKeys() + { + return $this->_foreignKeys; + } + + /** + * @return array lowercased column key names mapped to normal column ids. + */ + public function getLowerCaseColumnNames() + { + if($this->_lowercase===null) + { + $this->_lowercase=array(); + foreach($this->getColumns()->getKeys() as $key) + $this->_lowercase[strtolower($key)] = $key; + } + return $this->_lowercase; + } }
\ No newline at end of file diff --git a/framework/Data/DataGateway/TDataGatewayCommand.php b/framework/Data/DataGateway/TDataGatewayCommand.php index fa699979..299d39cd 100644 --- a/framework/Data/DataGateway/TDataGatewayCommand.php +++ b/framework/Data/DataGateway/TDataGatewayCommand.php @@ -1,541 +1,541 @@ -<?php
-/**
- * TDataGatewayCommand, TDataGatewayEventParameter and TDataGatewayResultEventParameter class file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.DataGateway
- */
-
-/**
- * TDataGatewayCommand is command builder and executor class for
- * TTableGateway and TActiveRecordGateway.
- *
- * TDataGatewayCommand builds the TDbCommand for TTableGateway
- * and TActiveRecordGateway commands such as find(), update(), insert(), etc,
- * using the TDbCommandBuilder classes (database specific TDbCommandBuilder
- * classes are used).
- *
- * Once the command is built and the query parameters are binded, the
- * {@link OnCreateCommand} event is raised. Event handlers for the OnCreateCommand
- * event should not alter the Command property nor the Criteria property of the
- * TDataGatewayEventParameter.
- *
- * TDataGatewayCommand excutes the TDbCommands and returns the result obtained from the
- * database (returned value depends on the method executed). The
- * {@link OnExecuteCommand} event is raised after the command is executed and resulting
- * data is set in the TDataGatewayResultEventParameter object's Result property.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.DataGateway
- * @since 3.1
- */
-class TDataGatewayCommand extends TComponent
-{
- private $_builder;
-
- /**
- * @param TDbCommandBuilder database specific database command builder.
- */
- public function __construct($builder)
- {
- $this->_builder = $builder;
- }
-
- /**
- * @return TDbTableInfo
- */
- public function getTableInfo()
- {
- return $this->_builder->getTableInfo();
- }
-
- /**
- * @return TDbConnection
- */
- public function getDbConnection()
- {
- return $this->_builder->getDbConnection();
- }
-
- /**
- * @return TDbCommandBuilder
- */
- public function getBuilder()
- {
- return $this->_builder;
- }
-
- /**
- * Executes a delete command.
- * @param TSqlCriteria delete conditions and parameters.
- * @return integer number of records affected.
- */
- public function delete($criteria)
- {
- $where = $criteria->getCondition();
- $parameters = $criteria->getParameters()->toArray();
- $command = $this->getBuilder()->createDeleteCommand($where, $parameters);
- $this->onCreateCommand($command,$criteria);
- $command->prepare();
- return $command->execute();
- }
-
- /**
- * Updates the table with new data.
- * @param array date for update.
- * @param TSqlCriteria update conditions and parameters.
- * @return integer number of records affected.
- */
- public function update($data, $criteria)
- {
- $where = $criteria->getCondition();
- $parameters = $criteria->getParameters()->toArray();
- $command = $this->getBuilder()->createUpdateCommand($data,$where, $parameters);
- $this->onCreateCommand($command,$criteria);
- $command->prepare();
- return $this->onExecuteCommand($command, $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.
- * @return array matching record.
- */
- public function find($criteria)
- {
- $command = $this->getFindCommand($criteria);
- return $this->onExecuteCommand($command, $command->queryRow());
- }
-
- /**
- * Find one or more matching records.
- * @param TSqlCriteria $criteria
- * @return TDbDataReader record reader.
- */
- public function findAll($criteria)
- {
- $command = $this->getFindCommand($criteria);
- return $this->onExecuteCommand($command, $command->query());
- }
-
- /**
- * Build the find command from the criteria. Limit, Offset and Ordering are applied if applicable.
- * @param TSqlCriteria $criteria
- * @return TDbCommand.
- */
- protected function getFindCommand($criteria)
- {
- if($criteria===null)
- return $this->getBuilder()->createFindCommand();
- $where = $criteria->getCondition();
- $parameters = $criteria->getParameters()->toArray();
- $ordering = $criteria->getOrdersBy();
- $limit = $criteria->getLimit();
- $offset = $criteria->getOffset();
- $select = $criteria->getSelect();
- $command = $this->getBuilder()->createFindCommand($where,$parameters,$ordering,$limit,$offset,$select);
- $this->onCreateCommand($command, $criteria);
- return $command;
- }
-
- /**
- * @param mixed primary key value, or composite key values as array.
- * @return array matching record.
- */
- public function findByPk($keys)
- {
- list($where, $parameters) = $this->getPrimaryKeyCondition((array)$keys);
- $command = $this->getBuilder()->createFindCommand($where, $parameters);
- $this->onCreateCommand($command, new TSqlCriteria($where,$parameters));
- return $this->onExecuteCommand($command, $command->queryRow());
- }
-
- /**
- * @param array multiple primary key values or composite value arrays
- * @return TDbDataReader record reader.
- */
- public function findAllByPk($keys)
- {
- $where = $this->getCompositeKeyCondition((array)$keys);
- $command = $this->getBuilder()->createFindCommand($where);
- $this->onCreateCommand($command, new TSqlCriteria($where,$keys));
- return $this->onExecuteCommand($command,$command->query());
- }
-
- public function findAllByIndex($criteria,$fields,$values)
- {
- $index = $this->getIndexKeyCondition($this->getTableInfo(),$fields,$values);
- if(strlen($where = $criteria->getCondition())>0)
- $criteria->setCondition("({$index}) AND ({$where})");
- else
- $criteria->setCondition($index);
- $command = $this->getFindCommand($criteria);
- $this->onCreateCommand($command, $criteria);
- return $this->onExecuteCommand($command,$command->query());
- }
-
- /**
- * @param array multiple primary key values or composite value arrays
- * @return integer number of rows affected.
- */
- public function deleteByPk($keys)
- {
- $where = $this->getCompositeKeyCondition((array)$keys);
- $command = $this->getBuilder()->createDeleteCommand($where);
- $this->onCreateCommand($command, new TSqlCriteria($where,$keys));
- $command->prepare();
- return $this->onExecuteCommand($command,$command->execute());
- }
-
- public function getIndexKeyCondition($table,$fields,$values)
- {
- if (!count($values))
- return 'FALSE';
- $columns = array();
- $tableName = $table->getTableFullName();
- foreach($fields as $field)
- $columns[] = $tableName.'.'.$table->getColumn($field)->getColumnName();
- return '('.implode(', ',$columns).') IN '.$this->quoteTuple($values);
- }
-
- /**
- * Construct a "pk IN ('key1', 'key2', ...)" criteria.
- * @param array values for IN predicate
- * @param string SQL string for primary keys IN a list.
- */
- protected function getCompositeKeyCondition($values)
- {
- $primary = $this->getTableInfo()->getPrimaryKeys();
- $count = count($primary);
- if($count===0)
- {
- throw new TDbException('dbtablegateway_no_primary_key_found',
- $this->getTableInfo()->getTableFullName());
- }
- if(!is_array($values) || count($values) === 0)
- {
- throw new TDbException('dbtablegateway_missing_pk_values',
- $this->getTableInfo()->getTableFullName());
- }
- if($count>1 && (!isset($values[0]) || !is_array($values[0])))
- $values = array($values);
- if($count > 1 && count($values[0]) !== $count)
- {
- throw new TDbException('dbtablegateway_pk_value_count_mismatch',
- $this->getTableInfo()->getTableFullName());
- }
- return $this->getIndexKeyCondition($this->getTableInfo(),$primary, $values);
- }
-
- /**
- * @param TDbConnection database connection.
- * @param array values
- * @return string quoted recursive tuple values, e.g. "('val1', 'val2')".
- */
- protected function quoteTuple($array)
- {
- $conn = $this->getDbConnection();
- $data = array();
- foreach($array as $k=>$v)
- $data[] = is_array($v) ? $this->quoteTuple($v) : $conn->quoteString($v);
- return '('.implode(', ', $data).')';
- }
-
- /**
- * Create the condition and parameters for find by primary.
- * @param array primary key values
- * @return array tuple($where, $parameters)
- */
- protected function getPrimaryKeyCondition($values)
- {
- $primary = $this->getTableInfo()->getPrimaryKeys();
- if(count($primary)===0)
- {
- throw new TDbException('dbtablegateway_no_primary_key_found',
- $this->getTableInfo()->getTableFullName());
- }
- $criteria=array();
- $bindings=array();
- $i = 0;
- foreach($primary as $key)
- {
- $column = $this->getTableInfo()->getColumn($key)->getColumnName();
- $criteria[] = $column.' = :'.$key;
- $bindings[$key] = isset($values[$key])?$values[$key]:$values[$i++];
- }
- return array(implode(' AND ', $criteria), $bindings);
- }
-
- /**
- * Find one matching records for arbituary SQL.
- * @param TSqlCriteria $criteria
- * @return TDbDataReader record reader.
- */
- public function findBySql($criteria)
- {
- $command = $this->getSqlCommand($criteria);
- return $this->onExecuteCommand($command, $command->queryRow());
- }
-
- /**
- * Find zero or more matching records for arbituary SQL.
- * @param TSqlCriteria $criteria
- * @return TDbDataReader record reader.
- */
- public function findAllBySql($criteria)
- {
- $command = $this->getSqlCommand($criteria);
- return $this->onExecuteCommand($command, $command->query());
- }
-
- /**
- * Build sql command from the criteria. Limit, Offset and Ordering are applied if applicable.
- * @param TSqlCriteria $criteria
- * @return TDbCommand command corresponding to the criteria.
- */
- protected function getSqlCommand($criteria)
- {
- $sql = $criteria->getCondition();
- $ordering = $criteria->getOrdersBy();
- $limit = $criteria->getLimit();
- $offset = $criteria->getOffset();
- if(count($ordering) > 0)
- $sql = $this->getBuilder()->applyOrdering($sql, $ordering);
- if($limit>=0 || $offset>=0)
- $sql = $this->getBuilder()->applyLimitOffset($sql, $limit, $offset);
- $command = $this->getBuilder()->createCommand($sql);
- $this->getBuilder()->bindArrayValues($command, $criteria->getParameters()->toArray());
- $this->onCreateCommand($command, $criteria);
- return $command;
- }
-
- /**
- * @param TSqlCriteria $criteria
- * @return integer number of records.
- */
- public function count($criteria)
- {
- if($criteria===null)
- return (int)$this->getBuilder()->createCountCommand()->queryScalar();
- $where = $criteria->getCondition();
- $parameters = $criteria->getParameters()->toArray();
- $ordering = $criteria->getOrdersBy();
- $limit = $criteria->getLimit();
- $offset = $criteria->getOffset();
- $command = $this->getBuilder()->createCountCommand($where,$parameters,$ordering,$limit,$offset);
- $this->onCreateCommand($command, $criteria);
- return $this->onExecuteCommand($command, (int)$command->queryScalar());
- }
-
- /**
- * Inserts a new record into the table. Each array key must
- * correspond to a column name in the table unless a null value is permitted.
- * @param array new record data.
- * @return mixed last insert id if one column contains a serial or sequence,
- * otherwise true if command executes successfully and affected 1 or more rows.
- */
- public function insert($data)
- {
- $command=$this->getBuilder()->createInsertCommand($data);
- $this->onCreateCommand($command, new TSqlCriteria(null,$data));
- $command->prepare();
- if($this->onExecuteCommand($command, $command->execute()) > 0)
- {
- $value = $this->getLastInsertId();
- return $value !== null ? $value : true;
- }
- return false;
- }
-
- /**
- * 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()
- {
- return $this->getBuilder()->getLastInsertID();
- }
-
- /**
- * @param string __call method name
- * @param string criteria conditions
- * @param array method arguments
- * @return TActiveRecordCriteria criteria created from the method name and its arguments.
- */
- public 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 TDbException('dbtablegateway_mismatch_args_exception',
- $method,count($fields),count($args));
- }
- return new TSqlCriteria(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
- */
- protected function extractMatchingConditions($method, $condition)
- {
- $table = $this->getTableInfo();
- $columns = $table->getLowerCaseColumnNames();
- $regexp = '/('.implode('|', array_keys($columns)).')(and|_and_|or|_or_)?/i';
- $matches = array();
- if(!preg_match_all($regexp, strtolower($condition), $matches,PREG_SET_ORDER))
- {
- throw new TDbException('dbtablegateway_mismatch_column_name',
- $method, implode(', ', $columns), $table->getTableFullName());
- }
-
- $fields = array();
- foreach($matches as $match)
- {
- $key = $columns[$match[1]];
- $column = $table->getColumn($key)->getColumnName();
- $sql = $column . ' = ? ';
- if(count($match) > 2)
- $sql .= strtoupper(str_replace('_', '', $match[2]));
- $fields[] = $sql;
- }
- return $fields;
- }
-
- /**
- * Raised when a command is prepared and parameter binding is completed.
- * The parameter object is TDataGatewayEventParameter of which the
- * {@link TDataGatewayEventParameter::getCommand Command} property can be
- * inspected to obtain the sql query to be executed.
- * @param TDataGatewayCommand originator $sender
- * @param TDataGatewayEventParameter
- */
- public function onCreateCommand($command, $criteria)
- {
- $this->raiseEvent('OnCreateCommand', $this, new TDataGatewayEventParameter($command,$criteria));
- }
-
- /**
- * Raised when a command is executed and the result from the database was returned.
- * The parameter object is TDataGatewayResultEventParameter of which the
- * {@link TDataGatewayEventParameter::getResult Result} property contains
- * the data return from the database. The data returned can be changed
- * by setting the {@link TDataGatewayEventParameter::setResult Result} property.
- * @param TDataGatewayCommand originator $sender
- * @param TDataGatewayResultEventParameter
- */
- public function onExecuteCommand($command, $result)
- {
- $parameter = new TDataGatewayResultEventParameter($command, $result);
- $this->raiseEvent('OnExecuteCommand', $this, $parameter);
- return $parameter->getResult();
- }
-}
-
-/**
- * TDataGatewayEventParameter class contains the TDbCommand to be executed as
- * well as the criteria object.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.DataGateway
- * @since 3.1
- */
-class TDataGatewayEventParameter extends TEventParameter
-{
- private $_command;
- private $_criteria;
-
- public function __construct($command,$criteria)
- {
- $this->_command=$command;
- $this->_criteria=$criteria;
- }
-
- /**
- * The database command to be executed. Do not rebind the parameters or change
- * the sql query string.
- * @return TDbCommand command to be executed.
- */
- public function getCommand()
- {
- return $this->_command;
- }
-
- /**
- * @return TSqlCriteria criteria used to bind the sql query parameters.
- */
- public function getCriteria()
- {
- return $this->_criteria;
- }
-}
-
-/**
- * TDataGatewayResultEventParameter contains the TDbCommand executed and the resulting
- * data returned from the database. The data can be changed by changing the
- * {@link setResult Result} property.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.DataGateway
- * @since 3.1
- */
-class TDataGatewayResultEventParameter extends TEventParameter
-{
- private $_command;
- private $_result;
-
- public function __construct($command,$result)
- {
- $this->_command=$command;
- $this->_result=$result;
- }
-
- /**
- * @return TDbCommand database command executed.
- */
- public function getCommand()
- {
- return $this->_command;
- }
-
- /**
- * @return mixed result returned from executing the command.
- */
- public function getResult()
- {
- return $this->_result;
- }
-
- /**
- * @param mixed change the result returned by the gateway.
- */
- public function setResult($value)
- {
- $this->_result=$value;
- }
-}
-
-?>
+<?php +/** + * TDataGatewayCommand, TDataGatewayEventParameter and TDataGatewayResultEventParameter class file. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.DataGateway + */ + +/** + * TDataGatewayCommand is command builder and executor class for + * TTableGateway and TActiveRecordGateway. + * + * TDataGatewayCommand builds the TDbCommand for TTableGateway + * and TActiveRecordGateway commands such as find(), update(), insert(), etc, + * using the TDbCommandBuilder classes (database specific TDbCommandBuilder + * classes are used). + * + * Once the command is built and the query parameters are binded, the + * {@link OnCreateCommand} event is raised. Event handlers for the OnCreateCommand + * event should not alter the Command property nor the Criteria property of the + * TDataGatewayEventParameter. + * + * TDataGatewayCommand excutes the TDbCommands and returns the result obtained from the + * database (returned value depends on the method executed). The + * {@link OnExecuteCommand} event is raised after the command is executed and resulting + * data is set in the TDataGatewayResultEventParameter object's Result property. + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.DataGateway + * @since 3.1 + */ +class TDataGatewayCommand extends TComponent +{ + private $_builder; + + /** + * @param TDbCommandBuilder database specific database command builder. + */ + public function __construct($builder) + { + $this->_builder = $builder; + } + + /** + * @return TDbTableInfo + */ + public function getTableInfo() + { + return $this->_builder->getTableInfo(); + } + + /** + * @return TDbConnection + */ + public function getDbConnection() + { + return $this->_builder->getDbConnection(); + } + + /** + * @return TDbCommandBuilder + */ + public function getBuilder() + { + return $this->_builder; + } + + /** + * Executes a delete command. + * @param TSqlCriteria delete conditions and parameters. + * @return integer number of records affected. + */ + public function delete($criteria) + { + $where = $criteria->getCondition(); + $parameters = $criteria->getParameters()->toArray(); + $command = $this->getBuilder()->createDeleteCommand($where, $parameters); + $this->onCreateCommand($command,$criteria); + $command->prepare(); + return $command->execute(); + } + + /** + * Updates the table with new data. + * @param array date for update. + * @param TSqlCriteria update conditions and parameters. + * @return integer number of records affected. + */ + public function update($data, $criteria) + { + $where = $criteria->getCondition(); + $parameters = $criteria->getParameters()->toArray(); + $command = $this->getBuilder()->createUpdateCommand($data,$where, $parameters); + $this->onCreateCommand($command,$criteria); + $command->prepare(); + return $this->onExecuteCommand($command, $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. + * @return array matching record. + */ + public function find($criteria) + { + $command = $this->getFindCommand($criteria); + return $this->onExecuteCommand($command, $command->queryRow()); + } + + /** + * Find one or more matching records. + * @param TSqlCriteria $criteria + * @return TDbDataReader record reader. + */ + public function findAll($criteria) + { + $command = $this->getFindCommand($criteria); + return $this->onExecuteCommand($command, $command->query()); + } + + /** + * Build the find command from the criteria. Limit, Offset and Ordering are applied if applicable. + * @param TSqlCriteria $criteria + * @return TDbCommand. + */ + protected function getFindCommand($criteria) + { + if($criteria===null) + return $this->getBuilder()->createFindCommand(); + $where = $criteria->getCondition(); + $parameters = $criteria->getParameters()->toArray(); + $ordering = $criteria->getOrdersBy(); + $limit = $criteria->getLimit(); + $offset = $criteria->getOffset(); + $select = $criteria->getSelect(); + $command = $this->getBuilder()->createFindCommand($where,$parameters,$ordering,$limit,$offset,$select); + $this->onCreateCommand($command, $criteria); + return $command; + } + + /** + * @param mixed primary key value, or composite key values as array. + * @return array matching record. + */ + public function findByPk($keys) + { + list($where, $parameters) = $this->getPrimaryKeyCondition((array)$keys); + $command = $this->getBuilder()->createFindCommand($where, $parameters); + $this->onCreateCommand($command, new TSqlCriteria($where,$parameters)); + return $this->onExecuteCommand($command, $command->queryRow()); + } + + /** + * @param array multiple primary key values or composite value arrays + * @return TDbDataReader record reader. + */ + public function findAllByPk($keys) + { + $where = $this->getCompositeKeyCondition((array)$keys); + $command = $this->getBuilder()->createFindCommand($where); + $this->onCreateCommand($command, new TSqlCriteria($where,$keys)); + return $this->onExecuteCommand($command,$command->query()); + } + + public function findAllByIndex($criteria,$fields,$values) + { + $index = $this->getIndexKeyCondition($this->getTableInfo(),$fields,$values); + if(strlen($where = $criteria->getCondition())>0) + $criteria->setCondition("({$index}) AND ({$where})"); + else + $criteria->setCondition($index); + $command = $this->getFindCommand($criteria); + $this->onCreateCommand($command, $criteria); + return $this->onExecuteCommand($command,$command->query()); + } + + /** + * @param array multiple primary key values or composite value arrays + * @return integer number of rows affected. + */ + public function deleteByPk($keys) + { + $where = $this->getCompositeKeyCondition((array)$keys); + $command = $this->getBuilder()->createDeleteCommand($where); + $this->onCreateCommand($command, new TSqlCriteria($where,$keys)); + $command->prepare(); + return $this->onExecuteCommand($command,$command->execute()); + } + + public function getIndexKeyCondition($table,$fields,$values) + { + if (!count($values)) + return 'FALSE'; + $columns = array(); + $tableName = $table->getTableFullName(); + foreach($fields as $field) + $columns[] = $tableName.'.'.$table->getColumn($field)->getColumnName(); + return '('.implode(', ',$columns).') IN '.$this->quoteTuple($values); + } + + /** + * Construct a "pk IN ('key1', 'key2', ...)" criteria. + * @param array values for IN predicate + * @param string SQL string for primary keys IN a list. + */ + protected function getCompositeKeyCondition($values) + { + $primary = $this->getTableInfo()->getPrimaryKeys(); + $count = count($primary); + if($count===0) + { + throw new TDbException('dbtablegateway_no_primary_key_found', + $this->getTableInfo()->getTableFullName()); + } + if(!is_array($values) || count($values) === 0) + { + throw new TDbException('dbtablegateway_missing_pk_values', + $this->getTableInfo()->getTableFullName()); + } + if($count>1 && (!isset($values[0]) || !is_array($values[0]))) + $values = array($values); + if($count > 1 && count($values[0]) !== $count) + { + throw new TDbException('dbtablegateway_pk_value_count_mismatch', + $this->getTableInfo()->getTableFullName()); + } + return $this->getIndexKeyCondition($this->getTableInfo(),$primary, $values); + } + + /** + * @param TDbConnection database connection. + * @param array values + * @return string quoted recursive tuple values, e.g. "('val1', 'val2')". + */ + protected function quoteTuple($array) + { + $conn = $this->getDbConnection(); + $data = array(); + foreach($array as $k=>$v) + $data[] = is_array($v) ? $this->quoteTuple($v) : $conn->quoteString($v); + return '('.implode(', ', $data).')'; + } + + /** + * Create the condition and parameters for find by primary. + * @param array primary key values + * @return array tuple($where, $parameters) + */ + protected function getPrimaryKeyCondition($values) + { + $primary = $this->getTableInfo()->getPrimaryKeys(); + if(count($primary)===0) + { + throw new TDbException('dbtablegateway_no_primary_key_found', + $this->getTableInfo()->getTableFullName()); + } + $criteria=array(); + $bindings=array(); + $i = 0; + foreach($primary as $key) + { + $column = $this->getTableInfo()->getColumn($key)->getColumnName(); + $criteria[] = $column.' = :'.$key; + $bindings[$key] = isset($values[$key])?$values[$key]:$values[$i++]; + } + return array(implode(' AND ', $criteria), $bindings); + } + + /** + * Find one matching records for arbituary SQL. + * @param TSqlCriteria $criteria + * @return TDbDataReader record reader. + */ + public function findBySql($criteria) + { + $command = $this->getSqlCommand($criteria); + return $this->onExecuteCommand($command, $command->queryRow()); + } + + /** + * Find zero or more matching records for arbituary SQL. + * @param TSqlCriteria $criteria + * @return TDbDataReader record reader. + */ + public function findAllBySql($criteria) + { + $command = $this->getSqlCommand($criteria); + return $this->onExecuteCommand($command, $command->query()); + } + + /** + * Build sql command from the criteria. Limit, Offset and Ordering are applied if applicable. + * @param TSqlCriteria $criteria + * @return TDbCommand command corresponding to the criteria. + */ + protected function getSqlCommand($criteria) + { + $sql = $criteria->getCondition(); + $ordering = $criteria->getOrdersBy(); + $limit = $criteria->getLimit(); + $offset = $criteria->getOffset(); + if(count($ordering) > 0) + $sql = $this->getBuilder()->applyOrdering($sql, $ordering); + if($limit>=0 || $offset>=0) + $sql = $this->getBuilder()->applyLimitOffset($sql, $limit, $offset); + $command = $this->getBuilder()->createCommand($sql); + $this->getBuilder()->bindArrayValues($command, $criteria->getParameters()->toArray()); + $this->onCreateCommand($command, $criteria); + return $command; + } + + /** + * @param TSqlCriteria $criteria + * @return integer number of records. + */ + public function count($criteria) + { + if($criteria===null) + return (int)$this->getBuilder()->createCountCommand()->queryScalar(); + $where = $criteria->getCondition(); + $parameters = $criteria->getParameters()->toArray(); + $ordering = $criteria->getOrdersBy(); + $limit = $criteria->getLimit(); + $offset = $criteria->getOffset(); + $command = $this->getBuilder()->createCountCommand($where,$parameters,$ordering,$limit,$offset); + $this->onCreateCommand($command, $criteria); + return $this->onExecuteCommand($command, (int)$command->queryScalar()); + } + + /** + * Inserts a new record into the table. Each array key must + * correspond to a column name in the table unless a null value is permitted. + * @param array new record data. + * @return mixed last insert id if one column contains a serial or sequence, + * otherwise true if command executes successfully and affected 1 or more rows. + */ + public function insert($data) + { + $command=$this->getBuilder()->createInsertCommand($data); + $this->onCreateCommand($command, new TSqlCriteria(null,$data)); + $command->prepare(); + if($this->onExecuteCommand($command, $command->execute()) > 0) + { + $value = $this->getLastInsertId(); + return $value !== null ? $value : true; + } + return false; + } + + /** + * 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() + { + return $this->getBuilder()->getLastInsertID(); + } + + /** + * @param string __call method name + * @param string criteria conditions + * @param array method arguments + * @return TActiveRecordCriteria criteria created from the method name and its arguments. + */ + public 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 TDbException('dbtablegateway_mismatch_args_exception', + $method,count($fields),count($args)); + } + return new TSqlCriteria(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 + */ + protected function extractMatchingConditions($method, $condition) + { + $table = $this->getTableInfo(); + $columns = $table->getLowerCaseColumnNames(); + $regexp = '/('.implode('|', array_keys($columns)).')(and|_and_|or|_or_)?/i'; + $matches = array(); + if(!preg_match_all($regexp, strtolower($condition), $matches,PREG_SET_ORDER)) + { + throw new TDbException('dbtablegateway_mismatch_column_name', + $method, implode(', ', $columns), $table->getTableFullName()); + } + + $fields = array(); + foreach($matches as $match) + { + $key = $columns[$match[1]]; + $column = $table->getColumn($key)->getColumnName(); + $sql = $column . ' = ? '; + if(count($match) > 2) + $sql .= strtoupper(str_replace('_', '', $match[2])); + $fields[] = $sql; + } + return $fields; + } + + /** + * Raised when a command is prepared and parameter binding is completed. + * The parameter object is TDataGatewayEventParameter of which the + * {@link TDataGatewayEventParameter::getCommand Command} property can be + * inspected to obtain the sql query to be executed. + * @param TDataGatewayCommand originator $sender + * @param TDataGatewayEventParameter + */ + public function onCreateCommand($command, $criteria) + { + $this->raiseEvent('OnCreateCommand', $this, new TDataGatewayEventParameter($command,$criteria)); + } + + /** + * Raised when a command is executed and the result from the database was returned. + * The parameter object is TDataGatewayResultEventParameter of which the + * {@link TDataGatewayEventParameter::getResult Result} property contains + * the data return from the database. The data returned can be changed + * by setting the {@link TDataGatewayEventParameter::setResult Result} property. + * @param TDataGatewayCommand originator $sender + * @param TDataGatewayResultEventParameter + */ + public function onExecuteCommand($command, $result) + { + $parameter = new TDataGatewayResultEventParameter($command, $result); + $this->raiseEvent('OnExecuteCommand', $this, $parameter); + return $parameter->getResult(); + } +} + +/** + * TDataGatewayEventParameter class contains the TDbCommand to be executed as + * well as the criteria object. + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.DataGateway + * @since 3.1 + */ +class TDataGatewayEventParameter extends TEventParameter +{ + private $_command; + private $_criteria; + + public function __construct($command,$criteria) + { + $this->_command=$command; + $this->_criteria=$criteria; + } + + /** + * The database command to be executed. Do not rebind the parameters or change + * the sql query string. + * @return TDbCommand command to be executed. + */ + public function getCommand() + { + return $this->_command; + } + + /** + * @return TSqlCriteria criteria used to bind the sql query parameters. + */ + public function getCriteria() + { + return $this->_criteria; + } +} + +/** + * TDataGatewayResultEventParameter contains the TDbCommand executed and the resulting + * data returned from the database. The data can be changed by changing the + * {@link setResult Result} property. + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.DataGateway + * @since 3.1 + */ +class TDataGatewayResultEventParameter extends TEventParameter +{ + private $_command; + private $_result; + + public function __construct($command,$result) + { + $this->_command=$command; + $this->_result=$result; + } + + /** + * @return TDbCommand database command executed. + */ + public function getCommand() + { + return $this->_command; + } + + /** + * @return mixed result returned from executing the command. + */ + public function getResult() + { + return $this->_result; + } + + /** + * @param mixed change the result returned by the gateway. + */ + public function setResult($value) + { + $this->_result=$value; + } +} + +?> diff --git a/framework/Data/DataGateway/TSqlCriteria.php b/framework/Data/DataGateway/TSqlCriteria.php index a7da3adf..3a54f4c4 100644 --- a/framework/Data/DataGateway/TSqlCriteria.php +++ b/framework/Data/DataGateway/TSqlCriteria.php @@ -1,284 +1,284 @@ -<?php
-/**
- * TDbSqlCriteria class file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id: TDbSqlCriteria.php 1835 2007-04-03 01:38:15Z wei $
- * @package System.Data.DataGateway
- */
-
-/**
- * Search criteria for TDbDataGateway.
- *
- * Criteria object for data gateway finder methods. Usage:
- * <code>
- * $criteria = new TSqlCriteria();
- * $criteria->Parameters[':name'] = 'admin';
- * $criteria->Parameters[':pass'] = 'prado';
- * $criteria->OrdersBy['level'] = 'desc';
- * $criteria->OrdersBy['name'] = 'asc';
- * $criteria->Limit = 10;
- * $criteria->Offset = 20;
- * </code>
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id: TDbSqlCriteria.php 1835 2007-04-03 01:38:15Z wei $
- * @package System.Data.DataGateway
- * @since 3.1
- */
-class TSqlCriteria extends TComponent
-{
- /**
- * @var mixed
- * @since 3.1.7
- */
- private $_select='*';
- private $_condition;
- private $_parameters;
- private $_ordersBy;
- private $_limit;
- private $_offset;
-
- /**
- * Creates a new criteria with given condition;
- * @param string sql string after the WHERE stanza
- * @param mixed named or indexed parameters, accepts as multiple arguments.
- */
- public function __construct($condition=null, $parameters=array())
- {
- if(!is_array($parameters) && func_num_args() > 1)
- $parameters = array_slice(func_get_args(),1);
- $this->_parameters=new TAttributeCollection;
- $this->_parameters->setCaseSensitive(true);
- $this->_parameters->copyFrom((array)$parameters);
- $this->_ordersBy=new TAttributeCollection;
- $this->_ordersBy->setCaseSensitive(true);
-
- $this->setCondition($condition);
- }
-
- /**
- * Gets the field list to be placed after the SELECT in the SQL. Default to '*'
- * @return mixed
- * @since 3.1.7
- */
- public function getSelect()
- {
- return $this->_select;
- }
-
- /**
- * Sets the field list to be placed after the SELECT in the SQL.
- *
- * Different behavior depends on type of assigned value
- * string
- * usage without modification
- *
- * null
- * will be expanded to full list of quoted table column names (quoting depends on database)
- *
- * array
- * - Column names will be quoted if used as key or value of array
- * <code>
- * array('col1', 'col2', 'col2')
- * // SELECT `col1`, `col2`, `col3` FROM...
- * </code>
- *
- * - Column aliasing
- * <code>
- * array('mycol1' => 'col1', 'mycol2' => 'COUNT(*)')
- * // SELECT `col1` AS mycol1, COUNT(*) AS mycol2 FROM...
- * </code>
- *
- * - NULL and scalar values (strings will be quoted depending on database)
- * <code>
- * array('col1' => 'my custom string', 'col2' => 1.0, 'col3' => 'NULL')
- * // SELECT "my custom string" AS `col1`, 1.0 AS `col2`, NULL AS `col3` FROM...
- * </code>
- *
- * - If the *-wildcard char is used as key or value, add the full list of quoted table column names
- * <code>
- * array('col1' => 'NULL', '*')
- * // SELECT `col1`, `col2`, `col3`, NULL AS `col1` FROM...
- * </code>
- *
- * @param mixed
- * @since 3.1.7
- * @see TDbCommandBuilder::getSelectFieldList()
- */
- public function setSelect($value)
- {
- $this->_select = $value;
- }
-
- /**
- * @return string search conditions.
- */
- public function getCondition()
- {
- return $this->_condition;
- }
-
- /**
- * Sets the search conditions to be placed after the WHERE clause in the SQL.
- * @param string search conditions.
- */
- public function setCondition($value)
- {
- if(empty($value)) {
- return;
- }
-
- // supporting the following SELECT-syntax:
- // [ORDER BY {col_name | expr | position}
- // [ASC | DESC], ...]
- // [LIMIT {[offset,] row_count | row_count OFFSET offset}]
- // See: http://dev.mysql.com/doc/refman/5.0/en/select.html
-
- if(preg_match('/ORDER\s+BY\s+(.*?)(?=LIMIT)|ORDER\s+BY\s+(.*?)$/i', $value, $matches) > 0) {
- // condition contains ORDER BY
- $value = str_replace($matches[0], '', $value);
- if(strlen($matches[1]) > 0) {
- $this->setOrdersBy($matches[1]);
- } else if(strlen($matches[2]) > 0) {
- $this->setOrdersBy($matches[2]);
- }
- }
-
- if(preg_match('/LIMIT\s+([\d\s,]+)/i', $value, $matches) > 0) {
- // condition contains limit
- $value = str_replace($matches[0], '', $value); // remove limit from query
- if(strpos($matches[1], ',')) { // both offset and limit given
- list($offset, $limit) = explode(',', $matches[1]);
- $this->_limit = (int)$limit;
- $this->_offset = (int)$offset;
- } else { // only limit given
- $this->_limit = (int)$matches[1];
- }
- }
-
- if(preg_match('/OFFSET\s+(\d+)/i', $value, $matches) > 0) {
- // condition contains offset
- $value = str_replace($matches[0], '', $value); // remove offset from query
- $this->_offset = (int)$matches[1]; // set offset in criteria
- }
-
- $this->_condition = trim($value);
- }
-
- /**
- * @return TAttributeCollection list of named parameters and values.
- */
- public function getParameters()
- {
- return $this->_parameters;
- }
-
- /**
- * @param ArrayAccess named parameters.
- */
- public function setParameters($value)
- {
- if(!(is_array($value) || $value instanceof ArrayAccess))
- throw new TException('value must be array or ArrayAccess');
- $this->_parameters->copyFrom($value);
- }
-
- /**
- * @return boolean true if the parameter index are string base, false otherwise.
- */
- public function getIsNamedParameters()
- {
- foreach($this->getParameters() as $k=>$v)
- return is_string($k);
- }
-
- /**
- * @return TAttributeCollection ordering clause.
- */
- public function getOrdersBy()
- {
- return $this->_ordersBy;
- }
-
- /**
- * @param mixed ordering clause.
- */
- public function setOrdersBy($value)
- {
- if(is_array($value) || $value instanceof Traversable)
- $this->_ordersBy->copyFrom($value);
- else
- {
- $value=trim(preg_replace('/\s+/',' ',(string)$value));
- $orderBys=array();
- foreach(explode(',',$value) as $orderBy)
- {
- $vs=explode(' ',trim($orderBy));
- $orderBys[$vs[0]]=isset($vs[1])?$vs[1]:'asc';
- }
- $this->_ordersBy->copyFrom($orderBys);
- }
- }
-
- /**
- * @return int maximum number of records to return.
- */
- public function getLimit()
- {
- return $this->_limit;
- }
-
- /**
- * @param int maximum number of records to return.
- */
- public function setLimit($value)
- {
- $this->_limit=$value;
- }
-
- /**
- * @return int record offset.
- */
- public function getOffset()
- {
- return $this->_offset;
- }
-
- /**
- * @param int record offset.
- */
- public function setOffset($value)
- {
- $this->_offset=$value;
- }
-
- /**
- * @return string string representation of the parameters. Useful for debugging.
- */
- public function __toString()
- {
- $str = '';
- if(strlen((string)$this->getCondition()) > 0)
- $str .= '"'.(string)$this->getCondition().'"';
- $params = array();
- foreach($this->getParameters() as $k=>$v)
- $params[] = "{$k} => ${v}";
- if(count($params) > 0)
- $str .= ', "'.implode(', ',$params).'"';
- $orders = array();
- foreach($this->getOrdersBy() as $k=>$v)
- $orders[] = "{$k} => ${v}";
- if(count($orders) > 0)
- $str .= ', "'.implode(', ',$orders).'"';
- if($this->_limit !==null)
- $str .= ', '.$this->_limit;
- if($this->_offset !== null)
- $str .= ', '.$this->_offset;
- return $str;
- }
-}
+<?php +/** + * TDbSqlCriteria class file. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TDbSqlCriteria.php 1835 2007-04-03 01:38:15Z wei $ + * @package System.Data.DataGateway + */ + +/** + * Search criteria for TDbDataGateway. + * + * Criteria object for data gateway finder methods. Usage: + * <code> + * $criteria = new TSqlCriteria(); + * $criteria->Parameters[':name'] = 'admin'; + * $criteria->Parameters[':pass'] = 'prado'; + * $criteria->OrdersBy['level'] = 'desc'; + * $criteria->OrdersBy['name'] = 'asc'; + * $criteria->Limit = 10; + * $criteria->Offset = 20; + * </code> + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id: TDbSqlCriteria.php 1835 2007-04-03 01:38:15Z wei $ + * @package System.Data.DataGateway + * @since 3.1 + */ +class TSqlCriteria extends TComponent +{ + /** + * @var mixed + * @since 3.1.7 + */ + private $_select='*'; + private $_condition; + private $_parameters; + private $_ordersBy; + private $_limit; + private $_offset; + + /** + * Creates a new criteria with given condition; + * @param string sql string after the WHERE stanza + * @param mixed named or indexed parameters, accepts as multiple arguments. + */ + public function __construct($condition=null, $parameters=array()) + { + if(!is_array($parameters) && func_num_args() > 1) + $parameters = array_slice(func_get_args(),1); + $this->_parameters=new TAttributeCollection; + $this->_parameters->setCaseSensitive(true); + $this->_parameters->copyFrom((array)$parameters); + $this->_ordersBy=new TAttributeCollection; + $this->_ordersBy->setCaseSensitive(true); + + $this->setCondition($condition); + } + + /** + * Gets the field list to be placed after the SELECT in the SQL. Default to '*' + * @return mixed + * @since 3.1.7 + */ + public function getSelect() + { + return $this->_select; + } + + /** + * Sets the field list to be placed after the SELECT in the SQL. + * + * Different behavior depends on type of assigned value + * string + * usage without modification + * + * null + * will be expanded to full list of quoted table column names (quoting depends on database) + * + * array + * - Column names will be quoted if used as key or value of array + * <code> + * array('col1', 'col2', 'col2') + * // SELECT `col1`, `col2`, `col3` FROM... + * </code> + * + * - Column aliasing + * <code> + * array('mycol1' => 'col1', 'mycol2' => 'COUNT(*)') + * // SELECT `col1` AS mycol1, COUNT(*) AS mycol2 FROM... + * </code> + * + * - NULL and scalar values (strings will be quoted depending on database) + * <code> + * array('col1' => 'my custom string', 'col2' => 1.0, 'col3' => 'NULL') + * // SELECT "my custom string" AS `col1`, 1.0 AS `col2`, NULL AS `col3` FROM... + * </code> + * + * - If the *-wildcard char is used as key or value, add the full list of quoted table column names + * <code> + * array('col1' => 'NULL', '*') + * // SELECT `col1`, `col2`, `col3`, NULL AS `col1` FROM... + * </code> + * + * @param mixed + * @since 3.1.7 + * @see TDbCommandBuilder::getSelectFieldList() + */ + public function setSelect($value) + { + $this->_select = $value; + } + + /** + * @return string search conditions. + */ + public function getCondition() + { + return $this->_condition; + } + + /** + * Sets the search conditions to be placed after the WHERE clause in the SQL. + * @param string search conditions. + */ + public function setCondition($value) + { + if(empty($value)) { + return; + } + + // supporting the following SELECT-syntax: + // [ORDER BY {col_name | expr | position} + // [ASC | DESC], ...] + // [LIMIT {[offset,] row_count | row_count OFFSET offset}] + // See: http://dev.mysql.com/doc/refman/5.0/en/select.html + + if(preg_match('/ORDER\s+BY\s+(.*?)(?=LIMIT)|ORDER\s+BY\s+(.*?)$/i', $value, $matches) > 0) { + // condition contains ORDER BY + $value = str_replace($matches[0], '', $value); + if(strlen($matches[1]) > 0) { + $this->setOrdersBy($matches[1]); + } else if(strlen($matches[2]) > 0) { + $this->setOrdersBy($matches[2]); + } + } + + if(preg_match('/LIMIT\s+([\d\s,]+)/i', $value, $matches) > 0) { + // condition contains limit + $value = str_replace($matches[0], '', $value); // remove limit from query + if(strpos($matches[1], ',')) { // both offset and limit given + list($offset, $limit) = explode(',', $matches[1]); + $this->_limit = (int)$limit; + $this->_offset = (int)$offset; + } else { // only limit given + $this->_limit = (int)$matches[1]; + } + } + + if(preg_match('/OFFSET\s+(\d+)/i', $value, $matches) > 0) { + // condition contains offset + $value = str_replace($matches[0], '', $value); // remove offset from query + $this->_offset = (int)$matches[1]; // set offset in criteria + } + + $this->_condition = trim($value); + } + + /** + * @return TAttributeCollection list of named parameters and values. + */ + public function getParameters() + { + return $this->_parameters; + } + + /** + * @param ArrayAccess named parameters. + */ + public function setParameters($value) + { + if(!(is_array($value) || $value instanceof ArrayAccess)) + throw new TException('value must be array or ArrayAccess'); + $this->_parameters->copyFrom($value); + } + + /** + * @return boolean true if the parameter index are string base, false otherwise. + */ + public function getIsNamedParameters() + { + foreach($this->getParameters() as $k=>$v) + return is_string($k); + } + + /** + * @return TAttributeCollection ordering clause. + */ + public function getOrdersBy() + { + return $this->_ordersBy; + } + + /** + * @param mixed ordering clause. + */ + public function setOrdersBy($value) + { + if(is_array($value) || $value instanceof Traversable) + $this->_ordersBy->copyFrom($value); + else + { + $value=trim(preg_replace('/\s+/',' ',(string)$value)); + $orderBys=array(); + foreach(explode(',',$value) as $orderBy) + { + $vs=explode(' ',trim($orderBy)); + $orderBys[$vs[0]]=isset($vs[1])?$vs[1]:'asc'; + } + $this->_ordersBy->copyFrom($orderBys); + } + } + + /** + * @return int maximum number of records to return. + */ + public function getLimit() + { + return $this->_limit; + } + + /** + * @param int maximum number of records to return. + */ + public function setLimit($value) + { + $this->_limit=$value; + } + + /** + * @return int record offset. + */ + public function getOffset() + { + return $this->_offset; + } + + /** + * @param int record offset. + */ + public function setOffset($value) + { + $this->_offset=$value; + } + + /** + * @return string string representation of the parameters. Useful for debugging. + */ + public function __toString() + { + $str = ''; + if(strlen((string)$this->getCondition()) > 0) + $str .= '"'.(string)$this->getCondition().'"'; + $params = array(); + foreach($this->getParameters() as $k=>$v) + $params[] = "{$k} => ${v}"; + if(count($params) > 0) + $str .= ', "'.implode(', ',$params).'"'; + $orders = array(); + foreach($this->getOrdersBy() as $k=>$v) + $orders[] = "{$k} => ${v}"; + if(count($orders) > 0) + $str .= ', "'.implode(', ',$orders).'"'; + if($this->_limit !==null) + $str .= ', '.$this->_limit; + if($this->_offset !== null) + $str .= ', '.$this->_offset; + return $str; + } +} ?>
\ No newline at end of file diff --git a/framework/Data/DataGateway/TTableGateway.php b/framework/Data/DataGateway/TTableGateway.php index bdcb391e..c436e0b0 100644 --- a/framework/Data/DataGateway/TTableGateway.php +++ b/framework/Data/DataGateway/TTableGateway.php @@ -1,476 +1,476 @@ -<?php
-/**
- * TTableGateway class file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
+<?php +/** + * TTableGateway class file. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.DataGateway
- */
-
-/**
- * Loads the data gateway command builder and sql criteria.
- */
-Prado::using('System.Data.DataGateway.TSqlCriteria');
-Prado::using('System.Data.DataGateway.TDataGatewayCommand');
-
-/**
- * TTableGateway class provides several find methods to get data from the database
- * and update, insert, and delete methods.
- *
- * Each method maps the input parameters into a SQL call and executes the SQL
- * against a database connection. The TTableGateway is stateless
- * (with respect to the data and data objects), as its role is to push data back and forth.
- *
- * Example usage:
- * <code>
- * //create a connection
- * $dsn = 'pgsql:host=localhost;dbname=test';
- * $conn = new TDbConnection($dsn, 'dbuser','dbpass');
- *
- * //create a table gateway for table/view named 'address'
- * $table = new TTableGateway('address', $conn);
- *
- * //insert a new row, returns last insert id (if applicable)
- * $id = $table->insert(array('name'=>'wei', 'phone'=>'111111'));
- *
- * $record1 = $table->findByPk($id); //find inserted record
- *
- * //finds all records, returns an iterator
- * $records = $table->findAll();
- * print_r($records->readAll());
- *
- * //update the row
- * $table->updateByPk($record1, $id);
- * </code>
- *
- * All methods that may return more than one row of data will return an
- * TDbDataReader iterator.
- *
- * The OnCreateCommand event is raised when a command is prepared and parameter
- * binding is completed. The parameter object is a TDataGatewayEventParameter of which the
- * {@link TDataGatewayEventParameter::getCommand Command} property can be
- * inspected to obtain the sql query to be executed.
- *
- * The OnExecuteCommand event is raised when a command is executed and the result
- * from the database was returned. The parameter object is a
- * TDataGatewayResultEventParameter of which the
- * {@link TDataGatewayEventParameter::getResult Result} property contains
- * the data return from the database. The data returned can be changed
- * by setting the {@link TDataGatewayEventParameter::setResult Result} property.
- *
- * <code>
- * $table->OnCreateCommand[] = 'log_it'; //any valid PHP callback statement
- * $table->OnExecuteCommand[] = array($obj, 'method_name'); // calls 'method_name' on $obj
- *
- * function log_it($sender, $param)
- * {
- * var_dump($param); //TDataGatewayEventParameter object.
- * }
- * </code>
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.DataGateway
- * @since 3.1
- */
-class TTableGateway extends TComponent
-{
- private $_command;
- private $_connection;
-
- /**
- * Creates a new generic table gateway for a given table or view name
- * and a database connection.
- * @param string|TDbTableInfo table or view name or table information.
- * @param TDbConnection database connection.
- */
- public function __construct($table,$connection)
- {
- $this->_connection=$connection;
- if(is_string($table))
- $this->setTableName($table);
- else if($table instanceof TDbTableInfo)
- $this->setTableInfo($table);
- else
- throw new TDbException('dbtablegateway_invalid_table_info');
- }
-
- /**
- * @param TDbTableInfo table or view information.
- */
- protected function setTableInfo($tableInfo)
- {
- $builder = $tableInfo->createCommandBuilder($this->getDbConnection());
- $this->initCommandBuilder($builder);
- }
-
- /**
- * Sets up the command builder for the given table.
- * @param string table or view name.
- */
- protected function setTableName($tableName)
- {
- Prado::using('System.Data.Common.TDbMetaData');
- $meta = TDbMetaData::getInstance($this->getDbConnection());
- $this->initCommandBuilder($meta->createCommandBuilder($tableName));
- }
-
- public function getTableInfo()
- {
- return $this->getCommand()->getTableInfo();
- }
-
- public function getTableName()
- {
- return $this->getTableInfo()->getTableName();
- }
-
- /**
- * @param TDbCommandBuilder database specific command builder.
- */
- protected function initCommandBuilder($builder)
- {
- $this->_command = new TDataGatewayCommand($builder);
- $this->_command->OnCreateCommand[] = array($this, 'onCreateCommand');
- $this->_command->OnExecuteCommand[] = array($this, 'onExecuteCommand');
- }
-
- /**
- * Raised when a command is prepared and parameter binding is completed.
- * The parameter object is TDataGatewayEventParameter of which the
- * {@link TDataGatewayEventParameter::getCommand Command} property can be
- * inspected to obtain the sql query to be executed.
- * @param TDataGatewayCommand originator $sender
- * @param TDataGatewayEventParameter
- */
- public function onCreateCommand($sender, $param)
- {
- $this->raiseEvent('OnCreateCommand', $this, $param);
- }
-
- /**
- * Raised when a command is executed and the result from the database was returned.
- * The parameter object is TDataGatewayResultEventParameter of which the
- * {@link TDataGatewayEventParameter::getResult Result} property contains
- * the data return from the database. The data returned can be changed
- * by setting the {@link TDataGatewayEventParameter::setResult Result} property.
- * @param TDataGatewayCommand originator $sender
- * @param TDataGatewayResultEventParameter
- */
- public function onExecuteCommand($sender, $param)
- {
- $this->raiseEvent('OnExecuteCommand', $this, $param);
- }
-
- /**
- * @return TDataGatewayCommand command builder and executor.
- */
- protected function getCommand()
- {
- return $this->_command;
- }
-
- /**
- * @return TDbConnection database connection.
- */
- public function getDbConnection()
- {
- return $this->_connection;
- }
-
- /**
- * Execute arbituary sql command with binding parameters.
- * @param string SQL query string.
- * @param array binding parameters, positional or named.
- * @return array query results.
- */
- public function findBySql($sql, $parameters=array())
- {
- $args = func_num_args() > 1 ? array_slice(func_get_args(),1) : null;
- $criteria = $this->getCriteria($sql,$parameters, $args);
- return $this->getCommand()->findBySql($criteria);
- }
-
- /**
- * Execute arbituary sql command with binding parameters.
- * @param string SQL query string.
- * @param array binding parameters, positional or named.
- * @return TDbDataReader query results.
- */
- public function findAllBySql($sql, $parameters=array())
- {
- $args = func_num_args() > 1 ? array_slice(func_get_args(),1) : null;
- $criteria = $this->getCriteria($sql,$parameters, $args);
- return $this->getCommand()->findAllBySql($criteria);
- }
-
- /**
- * Find one single record that matches the criteria.
- *
- * Usage:
- * <code>
- * $table->find('username = :name AND password = :pass',
- * array(':name'=>$name, ':pass'=>$pass));
- * $table->find('username = ? AND password = ?', array($name, $pass));
- * $table->find('username = ? AND password = ?', $name, $pass);
- * //$criteria is of TSqlCriteria
- * $table->find($criteria); //the 2nd parameter for find() is ignored.
- * </code>
- *
- * @param string|TSqlCriteria SQL condition or criteria object.
- * @param mixed parameter values.
- * @return array matching record object.
- */
- public function find($criteria, $parameters=array())
- {
- $args = func_num_args() > 1 ? array_slice(func_get_args(),1) : null;
- $criteria = $this->getCriteria($criteria,$parameters, $args);
- return $this->getCommand()->find($criteria);
- }
-
- /**
- * Accepts same parameters as find(), but returns TDbDataReader instead.
- * @param string|TSqlCriteria SQL condition or criteria object.
- * @param mixed parameter values.
- * @return TDbDataReader matching records.
- */
- public function findAll($criteria=null, $parameters=array())
- {
- $args = func_num_args() > 1 ? array_slice(func_get_args(),1) : null;
- if($criteria!==null)
- $criteria = $this->getCriteria($criteria,$parameters, $args);
- return $this->getCommand()->findAll($criteria);
- }
-
- /**
- * Find one record using only the primary key or composite primary keys. Usage:
- *
- * <code>
- * $table->findByPk($primaryKey);
- * $table->findByPk($key1, $key2, ...);
- * $table->findByPk(array($key1,$key2,...));
- * </code>
- *
- * @param mixed primary keys
- * @return array matching record.
- */
- public function findByPk($keys)
- {
- if(func_num_args() > 1)
- $keys = func_get_args();
- return $this->getCommand()->findByPk($keys);
- }
-
- /**
- * Similar to findByPk(), but returns TDbDataReader instead.
- *
- * For scalar primary keys:
- * <code>
- * $table->findAllByPk($key1, $key2, ...);
- * $table->findAllByPk(array($key1, $key2, ...));
- * </code>
- *
- * For composite keys:
- * <code>
- * $table->findAllByPk(array($key1, $key2), array($key3, $key4), ...);
- * $table->findAllByPk(array(array($key1, $key2), array($key3, $key4), ...));
- * </code>
- * @param mixed primary keys
- * @return TDbDataReader data reader.
- */
- public function findAllByPks($keys)
- {
- if(func_num_args() > 1)
- $keys = func_get_args();
- return $this->getCommand()->findAllByPk($keys);
- }
-
- /**
- * Delete records from the table with condition given by $where and
- * binding values specified by $parameter argument.
- * This method uses additional arguments as $parameters. E.g.
- * <code>
- * $table->delete('age > ? AND location = ?', $age, $location);
- * </code>
- * @param string delete condition.
- * @param array condition parameters.
- * @return integer number of records deleted.
- */
- public function deleteAll($criteria, $parameters=array())
- {
- $args = func_num_args() > 1 ? array_slice(func_get_args(),1) : null;
- $criteria = $this->getCriteria($criteria,$parameters, $args);
- return $this->getCommand()->delete($criteria);
- }
-
- /**
- * Delete records by primary key. Usage:
- *
- * <code>
- * $table->deleteByPk($primaryKey); //delete 1 record
- * $table->deleteByPk($key1,$key2,...); //delete multiple records
- * $table->deleteByPk(array($key1,$key2,...)); //delete multiple records
- * </code>
- *
- * For composite primary keys (determined from the table definitions):
- * <code>
- * $table->deleteByPk(array($key1,$key2)); //delete 1 record
- *
- * //delete multiple records
- * $table->deleteByPk(array($key1,$key2), array($key3,$key4),...);
- *
- * //delete multiple records
- * $table->deleteByPk(array( array($key1,$key2), array($key3,$key4), .. ));
- * </code>
- *
- * @param mixed primary key values.
- * @return int number of records deleted.
- */
- public function deleteByPk($keys)
- {
- if(func_num_args() > 1)
- $keys = func_get_args();
- return $this->getCommand()->deleteByPk($keys);
- }
-
- /**
- * Alias for deleteByPk()
- */
- public function deleteAllByPks($keys)
- {
- if(func_num_args() > 1)
- $keys = func_get_args();
- return $this->deleteByPk($keys);
- }
-
- /**
- * Find the number of records.
- * @param string|TSqlCriteria SQL condition or criteria object.
- * @param mixed parameter values.
- * @return int number of records.
- */
- public function count($criteria=null,$parameters=array())
- {
- $args = func_num_args() > 1 ? array_slice(func_get_args(),1) : null;
- if($criteria!==null)
- $criteria = $this->getCriteria($criteria,$parameters, $args);
- return $this->getCommand()->count($criteria);
- }
-
- /**
- * Updates the table with new name-value pair $data. Each array key must
- * correspond to a column name in the table. The update condition is
- * specified by the $where argument and additional binding values can be
- * specified using the $parameter argument.
- * This method uses additional arguments as $parameters. E.g.
- * <code>
- * $gateway->update($data, 'age > ? AND location = ?', $age, $location);
- * </code>
- * @param array new record data.
- * @param string update condition
- * @param array additional binding name-value pairs.
- * @return integer number of records updated.
- */
- public function update($data, $criteria, $parameters=array())
- {
- $args = func_num_args() > 2 ? array_slice(func_get_args(),2) : null;
- $criteria = $this->getCriteria($criteria,$parameters, $args);
- return $this->getCommand()->update($data, $criteria);
- }
-
- /**
- * Inserts a new record into the table. Each array key must
- * correspond to a column name in the table unless a null value is permitted.
- * @param array new record data.
- * @return mixed last insert id if one column contains a serial or sequence,
- * otherwise true if command executes successfully and affected 1 or more rows.
- */
- public function insert($data)
- {
- return $this->getCommand()->insert($data);
- }
-
- /**
- * @return mixed last insert id, null if none is found.
- */
- public function getLastInsertId()
- {
- return $this->getCommand()->getLastInsertId();
- }
-
- /**
- * 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.
- */
- protected function getCriteria($criteria, $parameters, $args)
- {
- if(is_string($criteria))
- {
- $useArgs = !is_array($parameters) && is_array($args);
- return new TSqlCriteria($criteria,$useArgs ? $args : $parameters);
- }
- else if($criteria instanceof TSqlCriteria)
- return $criteria;
- else
- throw new TDbException('dbtablegateway_invalid_criteria');
- }
-
- /**
- * Dynamic find method using parts of method name as search criteria.
- * Method name starting with "findBy" only returns 1 record.
- * Method name starting with "findAllBy" returns 0 or more records.
- * Method name starting with "deleteBy" deletes records by the trail criteria.
- * The condition is taken as part of the method name after "findBy", "findAllBy"
- * or "deleteBy".
- *
- * The following are equivalent:
- * <code>
- * $table->findByName($name)
- * $table->find('Name = ?', $name);
- * </code>
- * <code>
- * $table->findByUsernameAndPassword($name,$pass); // OR may be used
- * $table->findBy_Username_And_Password($name,$pass); // _OR_ may be used
- * $table->find('Username = ? AND Password = ?', $name, $pass);
- * </code>
- * <code>
- * $table->findAllByAge($age);
- * $table->findAll('Age = ?', $age);
- * </code>
- * <code>
- * $table->deleteAll('Name = ?', $name);
- * $table->deleteByName($name);
- * </code>
- * @return mixed single record if method name starts with "findBy", 0 or more records
- * if method name starts with "findAllBy"
- */
- public function __call($method,$args)
- {
- $delete =false;
- if($findOne = substr(strtolower($method),0,6)==='findby')
- $condition = $method[6]==='_' ? substr($method,7) : substr($method,6);
- else if(substr(strtolower($method),0,9)==='findallby')
- $condition = $method[9]==='_' ? substr($method,10) : substr($method,9);
- else if($delete = substr(strtolower($method),0,8)==='deleteby')
- $condition = $method[8]==='_' ? substr($method,9) : substr($method,8);
- else if($delete = substr(strtolower($method),0,11)==='deleteallby')
- $condition = $method[11]==='_' ? substr($method,12) : substr($method,11);
- else
- return null;
-
- $criteria = $this->getCommand()->createCriteriaFromString($method, $condition, $args);
- if($delete)
- return $this->deleteAll($criteria);
- else
- return $findOne ? $this->find($criteria) : $this->findAll($criteria);
- }
-}
+ * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.DataGateway + */ + +/** + * Loads the data gateway command builder and sql criteria. + */ +Prado::using('System.Data.DataGateway.TSqlCriteria'); +Prado::using('System.Data.DataGateway.TDataGatewayCommand'); + +/** + * TTableGateway class provides several find methods to get data from the database + * and update, insert, and delete methods. + * + * Each method maps the input parameters into a SQL call and executes the SQL + * against a database connection. The TTableGateway is stateless + * (with respect to the data and data objects), as its role is to push data back and forth. + * + * Example usage: + * <code> + * //create a connection + * $dsn = 'pgsql:host=localhost;dbname=test'; + * $conn = new TDbConnection($dsn, 'dbuser','dbpass'); + * + * //create a table gateway for table/view named 'address' + * $table = new TTableGateway('address', $conn); + * + * //insert a new row, returns last insert id (if applicable) + * $id = $table->insert(array('name'=>'wei', 'phone'=>'111111')); + * + * $record1 = $table->findByPk($id); //find inserted record + * + * //finds all records, returns an iterator + * $records = $table->findAll(); + * print_r($records->readAll()); + * + * //update the row + * $table->updateByPk($record1, $id); + * </code> + * + * All methods that may return more than one row of data will return an + * TDbDataReader iterator. + * + * The OnCreateCommand event is raised when a command is prepared and parameter + * binding is completed. The parameter object is a TDataGatewayEventParameter of which the + * {@link TDataGatewayEventParameter::getCommand Command} property can be + * inspected to obtain the sql query to be executed. + * + * The OnExecuteCommand event is raised when a command is executed and the result + * from the database was returned. The parameter object is a + * TDataGatewayResultEventParameter of which the + * {@link TDataGatewayEventParameter::getResult Result} property contains + * the data return from the database. The data returned can be changed + * by setting the {@link TDataGatewayEventParameter::setResult Result} property. + * + * <code> + * $table->OnCreateCommand[] = 'log_it'; //any valid PHP callback statement + * $table->OnExecuteCommand[] = array($obj, 'method_name'); // calls 'method_name' on $obj + * + * function log_it($sender, $param) + * { + * var_dump($param); //TDataGatewayEventParameter object. + * } + * </code> + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.DataGateway + * @since 3.1 + */ +class TTableGateway extends TComponent +{ + private $_command; + private $_connection; + + /** + * Creates a new generic table gateway for a given table or view name + * and a database connection. + * @param string|TDbTableInfo table or view name or table information. + * @param TDbConnection database connection. + */ + public function __construct($table,$connection) + { + $this->_connection=$connection; + if(is_string($table)) + $this->setTableName($table); + else if($table instanceof TDbTableInfo) + $this->setTableInfo($table); + else + throw new TDbException('dbtablegateway_invalid_table_info'); + } + + /** + * @param TDbTableInfo table or view information. + */ + protected function setTableInfo($tableInfo) + { + $builder = $tableInfo->createCommandBuilder($this->getDbConnection()); + $this->initCommandBuilder($builder); + } + + /** + * Sets up the command builder for the given table. + * @param string table or view name. + */ + protected function setTableName($tableName) + { + Prado::using('System.Data.Common.TDbMetaData'); + $meta = TDbMetaData::getInstance($this->getDbConnection()); + $this->initCommandBuilder($meta->createCommandBuilder($tableName)); + } + + public function getTableInfo() + { + return $this->getCommand()->getTableInfo(); + } + + public function getTableName() + { + return $this->getTableInfo()->getTableName(); + } + + /** + * @param TDbCommandBuilder database specific command builder. + */ + protected function initCommandBuilder($builder) + { + $this->_command = new TDataGatewayCommand($builder); + $this->_command->OnCreateCommand[] = array($this, 'onCreateCommand'); + $this->_command->OnExecuteCommand[] = array($this, 'onExecuteCommand'); + } + + /** + * Raised when a command is prepared and parameter binding is completed. + * The parameter object is TDataGatewayEventParameter of which the + * {@link TDataGatewayEventParameter::getCommand Command} property can be + * inspected to obtain the sql query to be executed. + * @param TDataGatewayCommand originator $sender + * @param TDataGatewayEventParameter + */ + public function onCreateCommand($sender, $param) + { + $this->raiseEvent('OnCreateCommand', $this, $param); + } + + /** + * Raised when a command is executed and the result from the database was returned. + * The parameter object is TDataGatewayResultEventParameter of which the + * {@link TDataGatewayEventParameter::getResult Result} property contains + * the data return from the database. The data returned can be changed + * by setting the {@link TDataGatewayEventParameter::setResult Result} property. + * @param TDataGatewayCommand originator $sender + * @param TDataGatewayResultEventParameter + */ + public function onExecuteCommand($sender, $param) + { + $this->raiseEvent('OnExecuteCommand', $this, $param); + } + + /** + * @return TDataGatewayCommand command builder and executor. + */ + protected function getCommand() + { + return $this->_command; + } + + /** + * @return TDbConnection database connection. + */ + public function getDbConnection() + { + return $this->_connection; + } + + /** + * Execute arbituary sql command with binding parameters. + * @param string SQL query string. + * @param array binding parameters, positional or named. + * @return array query results. + */ + public function findBySql($sql, $parameters=array()) + { + $args = func_num_args() > 1 ? array_slice(func_get_args(),1) : null; + $criteria = $this->getCriteria($sql,$parameters, $args); + return $this->getCommand()->findBySql($criteria); + } + + /** + * Execute arbituary sql command with binding parameters. + * @param string SQL query string. + * @param array binding parameters, positional or named. + * @return TDbDataReader query results. + */ + public function findAllBySql($sql, $parameters=array()) + { + $args = func_num_args() > 1 ? array_slice(func_get_args(),1) : null; + $criteria = $this->getCriteria($sql,$parameters, $args); + return $this->getCommand()->findAllBySql($criteria); + } + + /** + * Find one single record that matches the criteria. + * + * Usage: + * <code> + * $table->find('username = :name AND password = :pass', + * array(':name'=>$name, ':pass'=>$pass)); + * $table->find('username = ? AND password = ?', array($name, $pass)); + * $table->find('username = ? AND password = ?', $name, $pass); + * //$criteria is of TSqlCriteria + * $table->find($criteria); //the 2nd parameter for find() is ignored. + * </code> + * + * @param string|TSqlCriteria SQL condition or criteria object. + * @param mixed parameter values. + * @return array matching record object. + */ + public function find($criteria, $parameters=array()) + { + $args = func_num_args() > 1 ? array_slice(func_get_args(),1) : null; + $criteria = $this->getCriteria($criteria,$parameters, $args); + return $this->getCommand()->find($criteria); + } + + /** + * Accepts same parameters as find(), but returns TDbDataReader instead. + * @param string|TSqlCriteria SQL condition or criteria object. + * @param mixed parameter values. + * @return TDbDataReader matching records. + */ + public function findAll($criteria=null, $parameters=array()) + { + $args = func_num_args() > 1 ? array_slice(func_get_args(),1) : null; + if($criteria!==null) + $criteria = $this->getCriteria($criteria,$parameters, $args); + return $this->getCommand()->findAll($criteria); + } + + /** + * Find one record using only the primary key or composite primary keys. Usage: + * + * <code> + * $table->findByPk($primaryKey); + * $table->findByPk($key1, $key2, ...); + * $table->findByPk(array($key1,$key2,...)); + * </code> + * + * @param mixed primary keys + * @return array matching record. + */ + public function findByPk($keys) + { + if(func_num_args() > 1) + $keys = func_get_args(); + return $this->getCommand()->findByPk($keys); + } + + /** + * Similar to findByPk(), but returns TDbDataReader instead. + * + * For scalar primary keys: + * <code> + * $table->findAllByPk($key1, $key2, ...); + * $table->findAllByPk(array($key1, $key2, ...)); + * </code> + * + * For composite keys: + * <code> + * $table->findAllByPk(array($key1, $key2), array($key3, $key4), ...); + * $table->findAllByPk(array(array($key1, $key2), array($key3, $key4), ...)); + * </code> + * @param mixed primary keys + * @return TDbDataReader data reader. + */ + public function findAllByPks($keys) + { + if(func_num_args() > 1) + $keys = func_get_args(); + return $this->getCommand()->findAllByPk($keys); + } + + /** + * Delete records from the table with condition given by $where and + * binding values specified by $parameter argument. + * This method uses additional arguments as $parameters. E.g. + * <code> + * $table->delete('age > ? AND location = ?', $age, $location); + * </code> + * @param string delete condition. + * @param array condition parameters. + * @return integer number of records deleted. + */ + public function deleteAll($criteria, $parameters=array()) + { + $args = func_num_args() > 1 ? array_slice(func_get_args(),1) : null; + $criteria = $this->getCriteria($criteria,$parameters, $args); + return $this->getCommand()->delete($criteria); + } + + /** + * Delete records by primary key. Usage: + * + * <code> + * $table->deleteByPk($primaryKey); //delete 1 record + * $table->deleteByPk($key1,$key2,...); //delete multiple records + * $table->deleteByPk(array($key1,$key2,...)); //delete multiple records + * </code> + * + * For composite primary keys (determined from the table definitions): + * <code> + * $table->deleteByPk(array($key1,$key2)); //delete 1 record + * + * //delete multiple records + * $table->deleteByPk(array($key1,$key2), array($key3,$key4),...); + * + * //delete multiple records + * $table->deleteByPk(array( array($key1,$key2), array($key3,$key4), .. )); + * </code> + * + * @param mixed primary key values. + * @return int number of records deleted. + */ + public function deleteByPk($keys) + { + if(func_num_args() > 1) + $keys = func_get_args(); + return $this->getCommand()->deleteByPk($keys); + } + + /** + * Alias for deleteByPk() + */ + public function deleteAllByPks($keys) + { + if(func_num_args() > 1) + $keys = func_get_args(); + return $this->deleteByPk($keys); + } + + /** + * Find the number of records. + * @param string|TSqlCriteria SQL condition or criteria object. + * @param mixed parameter values. + * @return int number of records. + */ + public function count($criteria=null,$parameters=array()) + { + $args = func_num_args() > 1 ? array_slice(func_get_args(),1) : null; + if($criteria!==null) + $criteria = $this->getCriteria($criteria,$parameters, $args); + return $this->getCommand()->count($criteria); + } + + /** + * Updates the table with new name-value pair $data. Each array key must + * correspond to a column name in the table. The update condition is + * specified by the $where argument and additional binding values can be + * specified using the $parameter argument. + * This method uses additional arguments as $parameters. E.g. + * <code> + * $gateway->update($data, 'age > ? AND location = ?', $age, $location); + * </code> + * @param array new record data. + * @param string update condition + * @param array additional binding name-value pairs. + * @return integer number of records updated. + */ + public function update($data, $criteria, $parameters=array()) + { + $args = func_num_args() > 2 ? array_slice(func_get_args(),2) : null; + $criteria = $this->getCriteria($criteria,$parameters, $args); + return $this->getCommand()->update($data, $criteria); + } + + /** + * Inserts a new record into the table. Each array key must + * correspond to a column name in the table unless a null value is permitted. + * @param array new record data. + * @return mixed last insert id if one column contains a serial or sequence, + * otherwise true if command executes successfully and affected 1 or more rows. + */ + public function insert($data) + { + return $this->getCommand()->insert($data); + } + + /** + * @return mixed last insert id, null if none is found. + */ + public function getLastInsertId() + { + return $this->getCommand()->getLastInsertId(); + } + + /** + * 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. + */ + protected function getCriteria($criteria, $parameters, $args) + { + if(is_string($criteria)) + { + $useArgs = !is_array($parameters) && is_array($args); + return new TSqlCriteria($criteria,$useArgs ? $args : $parameters); + } + else if($criteria instanceof TSqlCriteria) + return $criteria; + else + throw new TDbException('dbtablegateway_invalid_criteria'); + } + + /** + * Dynamic find method using parts of method name as search criteria. + * Method name starting with "findBy" only returns 1 record. + * Method name starting with "findAllBy" returns 0 or more records. + * Method name starting with "deleteBy" deletes records by the trail criteria. + * The condition is taken as part of the method name after "findBy", "findAllBy" + * or "deleteBy". + * + * The following are equivalent: + * <code> + * $table->findByName($name) + * $table->find('Name = ?', $name); + * </code> + * <code> + * $table->findByUsernameAndPassword($name,$pass); // OR may be used + * $table->findBy_Username_And_Password($name,$pass); // _OR_ may be used + * $table->find('Username = ? AND Password = ?', $name, $pass); + * </code> + * <code> + * $table->findAllByAge($age); + * $table->findAll('Age = ?', $age); + * </code> + * <code> + * $table->deleteAll('Name = ?', $name); + * $table->deleteByName($name); + * </code> + * @return mixed single record if method name starts with "findBy", 0 or more records + * if method name starts with "findAllBy" + */ + public function __call($method,$args) + { + $delete =false; + if($findOne = substr(strtolower($method),0,6)==='findby') + $condition = $method[6]==='_' ? substr($method,7) : substr($method,6); + else if(substr(strtolower($method),0,9)==='findallby') + $condition = $method[9]==='_' ? substr($method,10) : substr($method,9); + else if($delete = substr(strtolower($method),0,8)==='deleteby') + $condition = $method[8]==='_' ? substr($method,9) : substr($method,8); + else if($delete = substr(strtolower($method),0,11)==='deleteallby') + $condition = $method[11]==='_' ? substr($method,12) : substr($method,11); + else + return null; + + $criteria = $this->getCommand()->createCriteriaFromString($method, $condition, $args); + if($delete) + return $this->deleteAll($criteria); + else + return $findOne ? $this->find($criteria) : $this->findAll($criteria); + } +} diff --git a/framework/Data/SqlMap/Configuration/TDiscriminator.php b/framework/Data/SqlMap/Configuration/TDiscriminator.php index cbc05612..f0c9187b 100644 --- a/framework/Data/SqlMap/Configuration/TDiscriminator.php +++ b/framework/Data/SqlMap/Configuration/TDiscriminator.php @@ -1,232 +1,232 @@ -<?php
-/**
- * TDiscriminator and TSubMap classes file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
+<?php +/** + * TDiscriminator and TSubMap classes file. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.SqlMap.Configuration
- */
-
-/**
- * The TDiscriminator corresponds to the <discriminator> tag within a <resultMap>.
- *
- * TDiscriminator allows inheritance logic in SqlMap result mappings.
- * SqlMap compares the data found in the discriminator column to the different
- * <submap> values using the column value's string equivalence. When the string values
- * matches a particular <submap>, SqlMap will use the <resultMap> defined by
- * {@link resultMapping TSubMap::setResultMapping()} property for loading
- * the object data.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.SqlMap.Configuration
- * @since 3.1
- */
-class TDiscriminator extends TComponent
-{
- private $_column;
- private $_type;
- private $_typeHandler=null;
- private $_columnIndex;
- private $_nullValue;
- private $_mapping;
- private $_resultMaps=array();
- private $_subMaps=array();
-
- /**
- * @return string the name of the column in the result set from which the
- * value will be used to populate the property.
- */
- public function getColumn()
- {
- return $this->_column;
- }
-
- /**
- * @param string the name of the column in the result set from which the
- * value will be used to populate the property.
- */
- public function setColumn($value)
- {
- $this->_column = $value;
- }
-
- /**
- * @param string property type of the parameter to be set.
- */
- public function getType()
- {
- return $this->_type;
- }
-
- /**
- * The type attribute is used to explicitly specify the property type of the
- * parameter to be set. If the attribute type is not set and the framework
- * cannot otherwise determine the type, the type is assumed from the default
- * value of the property.
- * @return string property type of the parameter to be set.
- */
- public function setType($value)
- {
- $this->_type = $value;
- }
-
- /**
- * @return string custom type handler class name (may use namespace).
- */
- public function getTypeHandler()
- {
- return $this->_typeHandler;
- }
-
- /**
- * @param string custom type handler class name (may use namespace).
- */
- public function setTypeHandler($value)
- {
- $this->_typeHandler = $value;
- }
-
- /**
- * @return int index of the column in the ResultSet
- */
- public function getColumnIndex()
- {
- return $this->_columnIndex;
- }
-
- /**
- * The columnIndex attribute value is the index of the column in the
- * ResultSet from which the value will be used to populate the object property.
- * @param int index of the column in the ResultSet
- */
- public function setColumnIndex($value)
- {
- $this->_columnIndex = TPropertyValue::ensureInteger($value);
- }
-
- /**
- * @return mixed outgoing null value replacement.
- */
- public function getNullValue()
- {
- return $this->_nullValue;
- }
-
- /**
- * @param mixed outgoing null value replacement.
- */
- public function setNullValue($value)
- {
- $this->_nullValue = $value;
- }
-
- /**
- * @return TResultProperty result property for the discriminator column.
- */
- public function getMapping()
- {
- return $this->_mapping;
- }
-
- /**
- * @param TSubMap add new sub mapping.
- */
- public function addSubMap($subMap)
- {
- $this->_subMaps[] = $subMap;
- }
-
- /**
- * @param string database value
- * @return TResultMap result mapping.
- */
- public function getSubMap($value)
- {
- if(isset($this->_resultMaps[$value]))
- return $this->_resultMaps[$value];
- }
-
- /**
- * Copies the discriminator properties to a new TResultProperty.
- * @param TResultMap result map holding the discriminator.
- */
- public function initMapping($resultMap)
- {
- $this->_mapping = new TResultProperty($resultMap);
- $this->_mapping->setColumn($this->getColumn());
- $this->_mapping->setColumnIndex($this->getColumnIndex());
- $this->_mapping->setType($this->getType());
- $this->_mapping->setTypeHandler($this->getTypeHandler());
- $this->_mapping->setNullValue($this->getNullValue());
- }
-
- /**
- * Set the result maps for particular sub-mapping values.
- * @param TSqlMapManager sql map manager instance.
- */
- public function initialize($manager)
- {
- foreach($this->_subMaps as $subMap)
- {
- $this->_resultMaps[$subMap->getValue()] =
- $manager->getResultMap($subMap->getResultMapping());
- }
- }
-}
-
-/**
- * TSubMap class defines a submapping value and the corresponding <resultMap>
- *
- * The {@link Value setValue()} property is used for comparison with the
- * discriminator column value. When the {@link Value setValue()} matches
- * that of the discriminator column value, the corresponding {@link ResultMapping setResultMapping}
- * is used inplace of the current result map.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.SqlMap.Configuration
- * @since 3.1
- */
-class TSubMap extends TComponent
-{
- private $_value;
- private $_resultMapping;
-
- /**
- * @return string value for comparison with discriminator column value.
- */
- public function getValue()
- {
- return $this->_value;
- }
-
- /**
- * @param string value for comparison with discriminator column value.
- */
- public function setValue($value)
- {
- $this->_value = $value;
- }
-
- /**
- * The result map to use when the Value matches the discriminator column value.
- * @return string ID of a result map
- */
- public function getResultMapping()
- {
- return $this->_resultMapping;
- }
-
- /**
- * @param string ID of a result map
- */
- public function setResultMapping($value)
- {
- $this->_resultMapping = $value;
- }
-}
-
+ * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.SqlMap.Configuration + */ + +/** + * The TDiscriminator corresponds to the <discriminator> tag within a <resultMap>. + * + * TDiscriminator allows inheritance logic in SqlMap result mappings. + * SqlMap compares the data found in the discriminator column to the different + * <submap> values using the column value's string equivalence. When the string values + * matches a particular <submap>, SqlMap will use the <resultMap> defined by + * {@link resultMapping TSubMap::setResultMapping()} property for loading + * the object data. + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.SqlMap.Configuration + * @since 3.1 + */ +class TDiscriminator extends TComponent +{ + private $_column; + private $_type; + private $_typeHandler=null; + private $_columnIndex; + private $_nullValue; + private $_mapping; + private $_resultMaps=array(); + private $_subMaps=array(); + + /** + * @return string the name of the column in the result set from which the + * value will be used to populate the property. + */ + public function getColumn() + { + return $this->_column; + } + + /** + * @param string the name of the column in the result set from which the + * value will be used to populate the property. + */ + public function setColumn($value) + { + $this->_column = $value; + } + + /** + * @param string property type of the parameter to be set. + */ + public function getType() + { + return $this->_type; + } + + /** + * The type attribute is used to explicitly specify the property type of the + * parameter to be set. If the attribute type is not set and the framework + * cannot otherwise determine the type, the type is assumed from the default + * value of the property. + * @return string property type of the parameter to be set. + */ + public function setType($value) + { + $this->_type = $value; + } + + /** + * @return string custom type handler class name (may use namespace). + */ + public function getTypeHandler() + { + return $this->_typeHandler; + } + + /** + * @param string custom type handler class name (may use namespace). + */ + public function setTypeHandler($value) + { + $this->_typeHandler = $value; + } + + /** + * @return int index of the column in the ResultSet + */ + public function getColumnIndex() + { + return $this->_columnIndex; + } + + /** + * The columnIndex attribute value is the index of the column in the + * ResultSet from which the value will be used to populate the object property. + * @param int index of the column in the ResultSet + */ + public function setColumnIndex($value) + { + $this->_columnIndex = TPropertyValue::ensureInteger($value); + } + + /** + * @return mixed outgoing null value replacement. + */ + public function getNullValue() + { + return $this->_nullValue; + } + + /** + * @param mixed outgoing null value replacement. + */ + public function setNullValue($value) + { + $this->_nullValue = $value; + } + + /** + * @return TResultProperty result property for the discriminator column. + */ + public function getMapping() + { + return $this->_mapping; + } + + /** + * @param TSubMap add new sub mapping. + */ + public function addSubMap($subMap) + { + $this->_subMaps[] = $subMap; + } + + /** + * @param string database value + * @return TResultMap result mapping. + */ + public function getSubMap($value) + { + if(isset($this->_resultMaps[$value])) + return $this->_resultMaps[$value]; + } + + /** + * Copies the discriminator properties to a new TResultProperty. + * @param TResultMap result map holding the discriminator. + */ + public function initMapping($resultMap) + { + $this->_mapping = new TResultProperty($resultMap); + $this->_mapping->setColumn($this->getColumn()); + $this->_mapping->setColumnIndex($this->getColumnIndex()); + $this->_mapping->setType($this->getType()); + $this->_mapping->setTypeHandler($this->getTypeHandler()); + $this->_mapping->setNullValue($this->getNullValue()); + } + + /** + * Set the result maps for particular sub-mapping values. + * @param TSqlMapManager sql map manager instance. + */ + public function initialize($manager) + { + foreach($this->_subMaps as $subMap) + { + $this->_resultMaps[$subMap->getValue()] = + $manager->getResultMap($subMap->getResultMapping()); + } + } +} + +/** + * TSubMap class defines a submapping value and the corresponding <resultMap> + * + * The {@link Value setValue()} property is used for comparison with the + * discriminator column value. When the {@link Value setValue()} matches + * that of the discriminator column value, the corresponding {@link ResultMapping setResultMapping} + * is used inplace of the current result map. + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.SqlMap.Configuration + * @since 3.1 + */ +class TSubMap extends TComponent +{ + private $_value; + private $_resultMapping; + + /** + * @return string value for comparison with discriminator column value. + */ + public function getValue() + { + return $this->_value; + } + + /** + * @param string value for comparison with discriminator column value. + */ + public function setValue($value) + { + $this->_value = $value; + } + + /** + * The result map to use when the Value matches the discriminator column value. + * @return string ID of a result map + */ + public function getResultMapping() + { + return $this->_resultMapping; + } + + /** + * @param string ID of a result map + */ + public function setResultMapping($value) + { + $this->_resultMapping = $value; + } +} + diff --git a/framework/Data/SqlMap/Configuration/TInlineParameterMapParser.php b/framework/Data/SqlMap/Configuration/TInlineParameterMapParser.php index b78a235c..914d7eb7 100644 --- a/framework/Data/SqlMap/Configuration/TInlineParameterMapParser.php +++ b/framework/Data/SqlMap/Configuration/TInlineParameterMapParser.php @@ -1,79 +1,79 @@ -<?php
-/**
- * TInlineParameterMapParser class file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
+<?php +/** + * TInlineParameterMapParser class file. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.SqlMap.Configuration
- */
-
-/**
- * TInlineParameterMapParser class.
- *
- * The inline parameter map syntax lets you embed the property name,
- * the property type, the column type, and a null value replacement into a
- * parametrized SQL statement.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.SqlMap.Configuration
- * @since 3.1
- */
-class TInlineParameterMapParser
-{
- /**
- * Regular expression for parsing inline parameter maps.
- */
- const PARAMETER_TOKEN_REGEXP = '/#([^#]+)#/';
-
- /**
- * Parse the sql text for inline parameters.
- * @param string sql text
- * @param array file and node details for exception message.
- * @return array 'sql' and 'parameters' name value pairs.
- */
- public function parse($sqlText, $scope)
- {
- $matches = array();
- $mappings = array();
- preg_match_all(self::PARAMETER_TOKEN_REGEXP, $sqlText, $matches);
-
- for($i = 0, $k=count($matches[1]); $i<$k; $i++)
- {
- $mappings[] = $this->parseMapping($matches[1][$i], $scope);
- $sqlText = str_replace($matches[0][$i], '?', $sqlText);
- }
- return array('sql'=>$sqlText, 'parameters'=>$mappings);
- }
-
- /**
- * Parse inline parameter with syntax as
- * #propertyName,type=string,dbype=Varchar,nullValue=N/A,handler=string#
- * @param string parameter token
- * @param array file and node details for exception message.
- */
- protected function parseMapping($token, $scope)
- {
- $mapping = new TParameterProperty;
- $properties = explode(',', $token);
- $mapping->setProperty(trim(array_shift($properties)));
- foreach($properties as $property)
- {
- $prop = explode('=',$property);
- $name = trim($prop[0]); $value=trim($prop[1]);
- if($mapping->canSetProperty($name))
- $mapping->{'set'.$name}($value);
- else
- {
- throw new TSqlMapUndefinedException(
- 'sqlmap_undefined_property_inline_map',
- $name, $scope['file'], $scope['node'], $token);
- }
- }
- return $mapping;
- }
-}
-
+ * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.SqlMap.Configuration + */ + +/** + * TInlineParameterMapParser class. + * + * The inline parameter map syntax lets you embed the property name, + * the property type, the column type, and a null value replacement into a + * parametrized SQL statement. + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.SqlMap.Configuration + * @since 3.1 + */ +class TInlineParameterMapParser +{ + /** + * Regular expression for parsing inline parameter maps. + */ + const PARAMETER_TOKEN_REGEXP = '/#([^#]+)#/'; + + /** + * Parse the sql text for inline parameters. + * @param string sql text + * @param array file and node details for exception message. + * @return array 'sql' and 'parameters' name value pairs. + */ + public function parse($sqlText, $scope) + { + $matches = array(); + $mappings = array(); + preg_match_all(self::PARAMETER_TOKEN_REGEXP, $sqlText, $matches); + + for($i = 0, $k=count($matches[1]); $i<$k; $i++) + { + $mappings[] = $this->parseMapping($matches[1][$i], $scope); + $sqlText = str_replace($matches[0][$i], '?', $sqlText); + } + return array('sql'=>$sqlText, 'parameters'=>$mappings); + } + + /** + * Parse inline parameter with syntax as + * #propertyName,type=string,dbype=Varchar,nullValue=N/A,handler=string# + * @param string parameter token + * @param array file and node details for exception message. + */ + protected function parseMapping($token, $scope) + { + $mapping = new TParameterProperty; + $properties = explode(',', $token); + $mapping->setProperty(trim(array_shift($properties))); + foreach($properties as $property) + { + $prop = explode('=',$property); + $name = trim($prop[0]); $value=trim($prop[1]); + if($mapping->canSetProperty($name)) + $mapping->{'set'.$name}($value); + else + { + throw new TSqlMapUndefinedException( + 'sqlmap_undefined_property_inline_map', + $name, $scope['file'], $scope['node'], $token); + } + } + return $mapping; + } +} + diff --git a/framework/Data/SqlMap/Configuration/TParameterMap.php b/framework/Data/SqlMap/Configuration/TParameterMap.php index d7cc5eb6..ee740fdb 100644 --- a/framework/Data/SqlMap/Configuration/TParameterMap.php +++ b/framework/Data/SqlMap/Configuration/TParameterMap.php @@ -1,210 +1,210 @@ -<?php
-/**
- * TParameterMap class file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.SqlMap.Configuration
- */
-
-/**
- * TParameterMap corresponds to the <parameterMap> element.
- *
- * TParameterMap holds one or more parameter child elements that map object
- * properties to placeholders in a SQL statement.
- *
- * A TParameterMap defines an ordered list of values that match up with the
- * placeholders of a parameterized query statement. While the attributes
- * specified by the map still need to be in the correct order, each parameter
- * is named. You can populate the underlying class in any order, and the
- * TParameterMap ensures each value is passed in the correct order.
- *
- * Parameter Maps can be provided as an external element and inline.
- * The <parameterMap> element accepts two attributes: id (required) and extends (optional).
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.SqlMap.Configuration
- * @since 3.1
- */
-class TParameterMap extends TComponent
-{
- private $_extend;
- private $_properties;
- private $_propertyMap;
- private $_extendMap;
- private $_ID;
-
- /**
- * Initialize the properties and property map collections.
- */
- public function __construct()
- {
- $this->_properties = new TList;
- $this->_propertyMap = new TMap;
- }
-
- /**
- * @return string a unique identifier for the <parameterMap>.
- */
- public function getID()
- {
- return $this->_ID;
- }
-
- /**
- * @param string a unique identifier for the <parameterMap>.
- */
- public function setID($value)
- {
- $this->_ID=$value;
- }
-
- /**
- * @return TParameterProperty[] list of properties for the parameter map.
- */
- public function getProperties()
- {
- return $this->_properties;
- }
-
- /**
- * @return string name of another <parameterMap> upon which to base this TParameterMap.
- */
- public function getExtends()
- {
- return $this->_extend;
- }
-
- /**
- * @param string name of another <parameterMap> upon which to base this TParameterMap.
- */
- public function setExtends($value)
- {
- $this->_extend = $value;
- }
-
- /**
- * @param string name of a parameter property.
- * @return TParameterProperty parameter property.
- * @throws TSqlMapException if index is not string nor integer.
- */
- public function getProperty($index)
- {
- if(is_string($index))
- return $this->_propertyMap->itemAt($index);
- else if(is_int($index))
- return $this->_properties->itemAt($index);
- else
- throw new TSqlMapException('sqlmap_index_must_be_string_or_int', $index);
- }
-
- /**
- * @param TParameterProperty new parameter property
- */
- public function addProperty(TParameterProperty $property)
- {
- $this->_propertyMap->add($property->getProperty(), $property);
- $this->_properties->add($property);
- }
-
- /**
- * @param int parameter property index
- * @param TParameterProperty new parameter property.
- */
- public function insertProperty($index, TParameterProperty $property)
- {
- $this->_propertyMap->add($property->getProperty(), $property);
- $this->_properties->insertAt($index, $property);
- }
-
- /**
- * @return array list of property names.
- */
- public function getPropertyNames()
- {
- return $this->_propertyMap->getKeys();
- }
-
- /**
- * Get the value of a property from the the parameter object.
- * @param TSqlMapTypeHandlerRegistry type handler registry.
- * @param TParameterProperty parameter proproperty.
- * @param mixed parameter object to get the value from.
- * @return unknown
- */
- public function getPropertyValue($registry, $property, $parameterValue)
- {
- $value = $this->getObjectValue($parameterValue,$property);
-
- if(($handler=$this->createTypeHandler($property, $registry))!==null)
- $value = $handler->getParameter($value);
-
- $value = $this->nullifyDefaultValue($property,$value);
-
- if(($type = $property->getType())!==null)
- $value = $registry->convertToType($type, $value);
-
- return $value;
- }
-
-
- /**
- * Create type handler from {@link Type setType()} or {@link TypeHandler setTypeHandler}.
- * @param TParameterProperty parameter property
- * @param TSqlMapTypeHandlerRegistry type handler registry
- * @return TSqlMapTypeHandler type handler.
- */
- protected function createTypeHandler($property, $registry)
- {
- $type=$property->getTypeHandler() ? $property->getTypeHandler() : $property->getType();
- $handler=$registry->getTypeHandler($type);
- if($handler===null && $property->getTypeHandler())
- $handler = Prado::createComponent($type);
- return $handler;
- }
-
-
- /**
- * @param mixed object to obtain the property from.
- * @param TParameterProperty parameter property.
- * @return mixed property value.
- * @throws TSqlMapException if property access is invalid.
- */
- protected function getObjectValue($object,$property)
- {
- try
- {
- return TPropertyAccess::get($object, $property->getProperty());
- }
- catch (TInvalidPropertyException $e)
- {
- throw new TSqlMapException(
- 'sqlmap_unable_to_get_property_for_parameter',
- $this->getID(),
- $property->getProperty(),
- (is_object($object) ? get_class($object) : gettype($object))
- );
- }
- }
-
- /**
- * When the actual value matches the {@link NullValue TParameterProperty::setNullValue()},
- * set the current value to null.
- * @param TParameterProperty parameter property.
- * @param mixed current property value
- * @return mixed null if NullValue matches currrent value.
- */
- protected function nullifyDefaultValue($property,$value)
- {
- if(($nullValue = $property->getNullValue())!==null)
- {
- if($nullValue === $value)
- $value = null;
- }
- return $value;
- }
-}
+<?php +/** + * TParameterMap class file. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.SqlMap.Configuration + */ + +/** + * TParameterMap corresponds to the <parameterMap> element. + * + * TParameterMap holds one or more parameter child elements that map object + * properties to placeholders in a SQL statement. + * + * A TParameterMap defines an ordered list of values that match up with the + * placeholders of a parameterized query statement. While the attributes + * specified by the map still need to be in the correct order, each parameter + * is named. You can populate the underlying class in any order, and the + * TParameterMap ensures each value is passed in the correct order. + * + * Parameter Maps can be provided as an external element and inline. + * The <parameterMap> element accepts two attributes: id (required) and extends (optional). + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.SqlMap.Configuration + * @since 3.1 + */ +class TParameterMap extends TComponent +{ + private $_extend; + private $_properties; + private $_propertyMap; + private $_extendMap; + private $_ID; + + /** + * Initialize the properties and property map collections. + */ + public function __construct() + { + $this->_properties = new TList; + $this->_propertyMap = new TMap; + } + + /** + * @return string a unique identifier for the <parameterMap>. + */ + public function getID() + { + return $this->_ID; + } + + /** + * @param string a unique identifier for the <parameterMap>. + */ + public function setID($value) + { + $this->_ID=$value; + } + + /** + * @return TParameterProperty[] list of properties for the parameter map. + */ + public function getProperties() + { + return $this->_properties; + } + + /** + * @return string name of another <parameterMap> upon which to base this TParameterMap. + */ + public function getExtends() + { + return $this->_extend; + } + + /** + * @param string name of another <parameterMap> upon which to base this TParameterMap. + */ + public function setExtends($value) + { + $this->_extend = $value; + } + + /** + * @param string name of a parameter property. + * @return TParameterProperty parameter property. + * @throws TSqlMapException if index is not string nor integer. + */ + public function getProperty($index) + { + if(is_string($index)) + return $this->_propertyMap->itemAt($index); + else if(is_int($index)) + return $this->_properties->itemAt($index); + else + throw new TSqlMapException('sqlmap_index_must_be_string_or_int', $index); + } + + /** + * @param TParameterProperty new parameter property + */ + public function addProperty(TParameterProperty $property) + { + $this->_propertyMap->add($property->getProperty(), $property); + $this->_properties->add($property); + } + + /** + * @param int parameter property index + * @param TParameterProperty new parameter property. + */ + public function insertProperty($index, TParameterProperty $property) + { + $this->_propertyMap->add($property->getProperty(), $property); + $this->_properties->insertAt($index, $property); + } + + /** + * @return array list of property names. + */ + public function getPropertyNames() + { + return $this->_propertyMap->getKeys(); + } + + /** + * Get the value of a property from the the parameter object. + * @param TSqlMapTypeHandlerRegistry type handler registry. + * @param TParameterProperty parameter proproperty. + * @param mixed parameter object to get the value from. + * @return unknown + */ + public function getPropertyValue($registry, $property, $parameterValue) + { + $value = $this->getObjectValue($parameterValue,$property); + + if(($handler=$this->createTypeHandler($property, $registry))!==null) + $value = $handler->getParameter($value); + + $value = $this->nullifyDefaultValue($property,$value); + + if(($type = $property->getType())!==null) + $value = $registry->convertToType($type, $value); + + return $value; + } + + + /** + * Create type handler from {@link Type setType()} or {@link TypeHandler setTypeHandler}. + * @param TParameterProperty parameter property + * @param TSqlMapTypeHandlerRegistry type handler registry + * @return TSqlMapTypeHandler type handler. + */ + protected function createTypeHandler($property, $registry) + { + $type=$property->getTypeHandler() ? $property->getTypeHandler() : $property->getType(); + $handler=$registry->getTypeHandler($type); + if($handler===null && $property->getTypeHandler()) + $handler = Prado::createComponent($type); + return $handler; + } + + + /** + * @param mixed object to obtain the property from. + * @param TParameterProperty parameter property. + * @return mixed property value. + * @throws TSqlMapException if property access is invalid. + */ + protected function getObjectValue($object,$property) + { + try + { + return TPropertyAccess::get($object, $property->getProperty()); + } + catch (TInvalidPropertyException $e) + { + throw new TSqlMapException( + 'sqlmap_unable_to_get_property_for_parameter', + $this->getID(), + $property->getProperty(), + (is_object($object) ? get_class($object) : gettype($object)) + ); + } + } + + /** + * When the actual value matches the {@link NullValue TParameterProperty::setNullValue()}, + * set the current value to null. + * @param TParameterProperty parameter property. + * @param mixed current property value + * @return mixed null if NullValue matches currrent value. + */ + protected function nullifyDefaultValue($property,$value) + { + if(($nullValue = $property->getNullValue())!==null) + { + if($nullValue === $value) + $value = null; + } + return $value; + } +} diff --git a/framework/Data/SqlMap/Configuration/TParameterProperty.php b/framework/Data/SqlMap/Configuration/TParameterProperty.php index d941ca18..a79af7f2 100644 --- a/framework/Data/SqlMap/Configuration/TParameterProperty.php +++ b/framework/Data/SqlMap/Configuration/TParameterProperty.php @@ -1,150 +1,150 @@ -<?php
-/**
- * TParameterPropert class file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
+<?php +/** + * TParameterPropert class file. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.SqlMap.Configuration
- */
-
-/**
- * TParameterProperty corresponds to the <property> tag and defines
- * one object property for the <parameterMap>
- *
- * The {@link NullValue setNullValue()} attribute can be set to any valid
- * value (based on property type). The {@link NullValue setNullValue()} attribute
- * is used to specify an inbound null value replacement. What this means is
- * that when the value is detected in the object property, a NULL will be written
- * to the database (the opposite behavior of an inbound null value replacement).
- * This allows you to use a magic null number in your application for types that
- * do not support null values (such as int, double, float). When these types of
- * properties contain a matching null value (for example, say, -9999), a NULL
- * will be written to the database instead of the value.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.SqlMap.Configuration
- * @since 3.1
- */
-class TParameterProperty extends TComponent
-{
- private $_typeHandler;
- private $_type;
- private $_column;
- private $_dbType;
- private $_property;
- private $_nullValue;
-
- /**
- * @return string class name of a custom type handler.
- */
- public function getTypeHandler()
- {
- return $this->_typeHandler;
- }
-
- /**
- * @param string class name of a custom type handler.
- */
- public function setTypeHandler($value)
- {
- $this->_typeHandler = $value;
- }
-
- /**
- * @return string type of the parameter's property
- */
- public function getType()
- {
- return $this->_type;
- }
-
- /**
- * @param string type of the parameter's property
- */
- public function setType($value)
- {
- $this->_type = $value;
- }
-
- /**
- * @return string name of a parameter to be used in the SQL statement.
- */
- public function getColumn()
- {
- return $this->_column;
- }
-
- /**
- * @param string name of a parameter to be used in the SQL statement.
- */
- public function setColumn($value)
- {
- $this->_column = $value;
- }
-
- /**
- * @return string the database column type of the parameter to be set by this property.
- */
- public function getDbType()
- {
- return $this->_dbType;
- }
-
- /**
- * @param string the database column type of the parameter to be set by this property.
- */
- public function setDbType($value)
- {
- $this->_dbType = $value;
- }
-
- /**
- * @return string name of a property of the parameter object.
- */
- public function getProperty()
- {
- return $this->_property;
- }
-
- /**
- * @param string name of a property of the parameter object.
- */
- public function setProperty($value)
- {
- $this->_property = $value;
- }
-
- /**
- * @return mixed null value replacement
- */
- public function getNullValue()
- {
- return $this->_nullValue;
- }
-
- /**
- * The nullValue attribute is used to specify an outgoing null value replacement.
- * @param mixed null value replacement.
- */
- public function setNullValue($value)
- {
- $this->_nullValue = $value;
- }
-
- public function __sleep()
- {
- $exprops = array(); $cn = 'TParameterProperty';
- if ($this->_typeHandler===null) $exprops[] = "\0$cn\0_typeHandler";
- if ($this->_type===null) $exprops[] = "\0$cn\0_type";
- if ($this->_column===null) $exprops[] = "\0$cn\0_column";
- if ($this->_dbType===null) $exprops[] = "\0$cn\0_dbType";
- if ($this->_property===null) $exprops[] = "\0$cn\0_property";
- if ($this->_nullValue===null) $exprops[] = "\0$cn\0_nullValue";
- return array_diff(parent::__sleep(),$exprops);
- }
-}
-
+ * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.SqlMap.Configuration + */ + +/** + * TParameterProperty corresponds to the <property> tag and defines + * one object property for the <parameterMap> + * + * The {@link NullValue setNullValue()} attribute can be set to any valid + * value (based on property type). The {@link NullValue setNullValue()} attribute + * is used to specify an inbound null value replacement. What this means is + * that when the value is detected in the object property, a NULL will be written + * to the database (the opposite behavior of an inbound null value replacement). + * This allows you to use a magic null number in your application for types that + * do not support null values (such as int, double, float). When these types of + * properties contain a matching null value (for example, say, -9999), a NULL + * will be written to the database instead of the value. + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.SqlMap.Configuration + * @since 3.1 + */ +class TParameterProperty extends TComponent +{ + private $_typeHandler; + private $_type; + private $_column; + private $_dbType; + private $_property; + private $_nullValue; + + /** + * @return string class name of a custom type handler. + */ + public function getTypeHandler() + { + return $this->_typeHandler; + } + + /** + * @param string class name of a custom type handler. + */ + public function setTypeHandler($value) + { + $this->_typeHandler = $value; + } + + /** + * @return string type of the parameter's property + */ + public function getType() + { + return $this->_type; + } + + /** + * @param string type of the parameter's property + */ + public function setType($value) + { + $this->_type = $value; + } + + /** + * @return string name of a parameter to be used in the SQL statement. + */ + public function getColumn() + { + return $this->_column; + } + + /** + * @param string name of a parameter to be used in the SQL statement. + */ + public function setColumn($value) + { + $this->_column = $value; + } + + /** + * @return string the database column type of the parameter to be set by this property. + */ + public function getDbType() + { + return $this->_dbType; + } + + /** + * @param string the database column type of the parameter to be set by this property. + */ + public function setDbType($value) + { + $this->_dbType = $value; + } + + /** + * @return string name of a property of the parameter object. + */ + public function getProperty() + { + return $this->_property; + } + + /** + * @param string name of a property of the parameter object. + */ + public function setProperty($value) + { + $this->_property = $value; + } + + /** + * @return mixed null value replacement + */ + public function getNullValue() + { + return $this->_nullValue; + } + + /** + * The nullValue attribute is used to specify an outgoing null value replacement. + * @param mixed null value replacement. + */ + public function setNullValue($value) + { + $this->_nullValue = $value; + } + + public function __sleep() + { + $exprops = array(); $cn = 'TParameterProperty'; + if ($this->_typeHandler===null) $exprops[] = "\0$cn\0_typeHandler"; + if ($this->_type===null) $exprops[] = "\0$cn\0_type"; + if ($this->_column===null) $exprops[] = "\0$cn\0_column"; + if ($this->_dbType===null) $exprops[] = "\0$cn\0_dbType"; + if ($this->_property===null) $exprops[] = "\0$cn\0_property"; + if ($this->_nullValue===null) $exprops[] = "\0$cn\0_nullValue"; + return array_diff(parent::__sleep(),$exprops); + } +} + diff --git a/framework/Data/SqlMap/Configuration/TResultMap.php b/framework/Data/SqlMap/Configuration/TResultMap.php index e05c4c47..77b5f0b2 100644 --- a/framework/Data/SqlMap/Configuration/TResultMap.php +++ b/framework/Data/SqlMap/Configuration/TResultMap.php @@ -1,200 +1,200 @@ -<?php
-/**
- * TResultMap class file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.SqlMap.Configuration
- */
-
-/**
- * TResultMap corresponds to <resultMap> mapping tag.
- *
- * A TResultMap lets you control how data is extracted from the result of a
- * query, and how the columns are mapped to object properties. A TResultMap
- * can describe the column type, a null value replacement, and complex property
- * mappings including Collections.
- *
- * The <resultMap> can contain any number of property mappings that map object
- * properties to the columns of a result element. The property mappings are
- * applied, and the columns are read, in the order that they are defined.
- * Maintaining the element order ensures consistent results between different
- * drivers and providers.
- *
- * The {@link Class setClass()} property must be a PHP class object or array instance.
- *
- * The optional {@link Extends setExtends()} attribute can be set to the ID of
- * another <resultMap> upon which to base this <resultMap>. All properties of the
- * "parent" <resultMap> will be included as part of this <resultMap>, and values
- * from the "parent" <resultMap> are set before any values specified by this <resultMap>.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.SqlMap.Configuration
- * @since 3.1
- */
-class TResultMap extends TComponent
-{
- private $_columns;
- private $_class;
- private $_extends;
- private $_groupBy;
- private $_discriminator;
- private $_typeHandlers;
- private $_ID;
-
- /**
- * Initialize the columns collection.
- */
- public function __construct()
- {
- $this->_columns=new TMap;
- }
-
- /**
- * @return string a unique identifier for the <resultMap>.
- */
- public function getID()
- {
- return $this->_ID;
- }
-
- /**
- * @param string a unique identifier for the <resultMap>.
- */
- public function setID($value)
- {
- $this->_ID=$value;
- }
-
- /**
- * @return string result class name.
- */
- public function getClass()
- {
- return $this->_class;
- }
-
- /**
- * @param string result class name.
- */
- public function setClass($value)
- {
- $this->_class = $value;
- }
-
- /**
- * @return TMap result columns.
- */
- public function getColumns()
- {
- return $this->_columns;
- }
-
- /**
- * @return string result map extends another result map.
- */
- public function getExtends()
- {
- return $this->_extends;
- }
-
- /**
- * @param string result map extends another result map.
- */
- public function setExtends($value)
- {
- $this->_extends = $value;
- }
-
- /**
- * @return string result map groups by.
- */
- public function getGroupBy()
- {
- return $this->_groupBy;
- }
-
- /**
- * @param string result map group by
- */
- public function setGroupBy($value)
- {
- $this->_groupBy = $value;
- }
-
- /**
- * @return TDiscriminator result class discriminator.
- */
- public function getDiscriminator()
- {
- return $this->_discriminator;
- }
-
- /**
- * @param TDiscriminator result class discriminator.
- */
- public function setDiscriminator(TDiscriminator $value)
- {
- $this->_discriminator = $value;
- }
-
- /**
- * Add a TResultProperty to result mapping.
- * @param TResultProperty result property.
- */
- public function addResultProperty(TResultProperty $property)
- {
- $this->_columns[$property->getProperty()] = $property;
- }
-
- /**
- * Create a new instance of the class of this result map.
- * @param TSqlMapTypeHandlerRegistry type handler registry.
- * @return mixed new result object.
- * @throws TSqlMapException
- */
- public function createInstanceOfResult($registry)
- {
- $handler = $registry->getTypeHandler($this->getClass());
- try
- {
- if($handler!==null)
- return $handler->createNewInstance();
- else
- return $registry->createInstanceOf($this->getClass());
- }
- catch (TSqlMapException $e)
- {
- throw new TSqlMapException(
- 'sqlmap_unable_to_create_new_instance',
- $this->getClass(), get_class($handler), $this->getID());
- }
- }
-
- /**
- * Result sub-mappings using the discriminiator column.
- * @param TSqlMapTypeHandlerRegistry type handler registry
- * @param array row data.
- * @return TResultMap result sub-map.
- */
- public function resolveSubMap($registry,$row)
- {
- $subMap = $this;
- if(($disc = $this->getDiscriminator())!==null)
- {
- $value = $disc->getMapping()->getPropertyValue($registry,$row);
- $subMap = $disc->getSubMap((string)$value);
-
- if($subMap===null)
- $subMap = $this;
- else if($subMap !== $this)
- $subMap = $subMap->resolveSubMap($registry,$row);
- }
- return $subMap;
- }
-}
-
+<?php +/** + * TResultMap class file. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.SqlMap.Configuration + */ + +/** + * TResultMap corresponds to <resultMap> mapping tag. + * + * A TResultMap lets you control how data is extracted from the result of a + * query, and how the columns are mapped to object properties. A TResultMap + * can describe the column type, a null value replacement, and complex property + * mappings including Collections. + * + * The <resultMap> can contain any number of property mappings that map object + * properties to the columns of a result element. The property mappings are + * applied, and the columns are read, in the order that they are defined. + * Maintaining the element order ensures consistent results between different + * drivers and providers. + * + * The {@link Class setClass()} property must be a PHP class object or array instance. + * + * The optional {@link Extends setExtends()} attribute can be set to the ID of + * another <resultMap> upon which to base this <resultMap>. All properties of the + * "parent" <resultMap> will be included as part of this <resultMap>, and values + * from the "parent" <resultMap> are set before any values specified by this <resultMap>. + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.SqlMap.Configuration + * @since 3.1 + */ +class TResultMap extends TComponent +{ + private $_columns; + private $_class; + private $_extends; + private $_groupBy; + private $_discriminator; + private $_typeHandlers; + private $_ID; + + /** + * Initialize the columns collection. + */ + public function __construct() + { + $this->_columns=new TMap; + } + + /** + * @return string a unique identifier for the <resultMap>. + */ + public function getID() + { + return $this->_ID; + } + + /** + * @param string a unique identifier for the <resultMap>. + */ + public function setID($value) + { + $this->_ID=$value; + } + + /** + * @return string result class name. + */ + public function getClass() + { + return $this->_class; + } + + /** + * @param string result class name. + */ + public function setClass($value) + { + $this->_class = $value; + } + + /** + * @return TMap result columns. + */ + public function getColumns() + { + return $this->_columns; + } + + /** + * @return string result map extends another result map. + */ + public function getExtends() + { + return $this->_extends; + } + + /** + * @param string result map extends another result map. + */ + public function setExtends($value) + { + $this->_extends = $value; + } + + /** + * @return string result map groups by. + */ + public function getGroupBy() + { + return $this->_groupBy; + } + + /** + * @param string result map group by + */ + public function setGroupBy($value) + { + $this->_groupBy = $value; + } + + /** + * @return TDiscriminator result class discriminator. + */ + public function getDiscriminator() + { + return $this->_discriminator; + } + + /** + * @param TDiscriminator result class discriminator. + */ + public function setDiscriminator(TDiscriminator $value) + { + $this->_discriminator = $value; + } + + /** + * Add a TResultProperty to result mapping. + * @param TResultProperty result property. + */ + public function addResultProperty(TResultProperty $property) + { + $this->_columns[$property->getProperty()] = $property; + } + + /** + * Create a new instance of the class of this result map. + * @param TSqlMapTypeHandlerRegistry type handler registry. + * @return mixed new result object. + * @throws TSqlMapException + */ + public function createInstanceOfResult($registry) + { + $handler = $registry->getTypeHandler($this->getClass()); + try + { + if($handler!==null) + return $handler->createNewInstance(); + else + return $registry->createInstanceOf($this->getClass()); + } + catch (TSqlMapException $e) + { + throw new TSqlMapException( + 'sqlmap_unable_to_create_new_instance', + $this->getClass(), get_class($handler), $this->getID()); + } + } + + /** + * Result sub-mappings using the discriminiator column. + * @param TSqlMapTypeHandlerRegistry type handler registry + * @param array row data. + * @return TResultMap result sub-map. + */ + public function resolveSubMap($registry,$row) + { + $subMap = $this; + if(($disc = $this->getDiscriminator())!==null) + { + $value = $disc->getMapping()->getPropertyValue($registry,$row); + $subMap = $disc->getSubMap((string)$value); + + if($subMap===null) + $subMap = $this; + else if($subMap !== $this) + $subMap = $subMap->resolveSubMap($registry,$row); + } + return $subMap; + } +} + diff --git a/framework/Data/SqlMap/Configuration/TResultProperty.php b/framework/Data/SqlMap/Configuration/TResultProperty.php index 0a016350..8447d400 100644 --- a/framework/Data/SqlMap/Configuration/TResultProperty.php +++ b/framework/Data/SqlMap/Configuration/TResultProperty.php @@ -1,344 +1,344 @@ -<?php
-/**
- * TResultProperty class file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.SqlMap.Configuration
- */
-
-/**
- * TResultProperty corresponds a <property> tags inside a <resultMap> tag.
- *
- * The {@link NullValue setNullValue()} attribute can be set to any valid
- * value (based on property type). The {@link NullValue setNullValue()} attribute
- * is used to specify an outgoing null value replacement. What this means is
- * that when a null value is detected in the result, the corresponding value of
- * the {@link NullValue getNullValue()} will be used instead.
- *
- * The {@link Select setSelect()} property is used to describe a relationship
- * between objects and to automatically load complex (i.e. user defined)
- * property types. The value of the {@link Select setSelect()} property must be
- * the name of another mapped statement. The value of the database
- * {@link Column setColumn()} that is defined in the same property element as
- * this statement attribute will be passed to the related mapped statement as
- * the parameter. The {@link LazyLoad setLayLoad()} attribute can be specified
- * with the {@link Select setSelect()} .
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.SqlMap.Configuration
- * @since 3.1
- */
-class TResultProperty extends TComponent
-{
- private $_nullValue;
- private $_propertyName;
- private $_columnName;
- private $_columnIndex=-1;
- private $_nestedResultMapName;
- private $_nestedResultMap;
- private $_valueType;
- private $_typeHandler;
- private $_isLazyLoad=false;
- private $_select;
-
- private $_hostResultMapID='inplicit internal mapping';
-
- const LIST_TYPE = 0;
- const ARRAY_TYPE = 1;
-
- /**
- * Gets the containing result map ID.
- * @param TResultMap containing result map.
- */
- public function __construct($resultMap=null)
- {
- if($resultMap instanceof TResultMap)
- $this->_hostResultMapID = $resultMap->getID();
- }
-
- /**
- * @return mixed null value replacement.
- */
- public function getNullValue()
- {
- return $this->_nullValue;
- }
-
- /**
- * @param mixed null value replacement.
- */
- public function setNullValue($value)
- {
- $this->_nullValue = $value;
- }
-
- /**
- * @return string name of a property of the result object that will be set to.
- */
- public function getProperty()
- {
- return $this->_propertyName;
- }
-
- /**
- * @param string name of a property of the result object that will be set to.
- */
- public function setProperty($value)
- {
- $this->_propertyName = $value;
- }
-
- /**
- * @return string name of the column in the result set from which the value
- * will be used to populate the property.
- */
- public function getColumn()
- {
- return $this->_columnName;
- }
-
- /**
- * @param string name of the column in the result set from which the value
- * will be used to populate the property.
- */
- public function setColumn($value)
- {
- $this->_columnName = $value;
- }
-
- /**
- * @return int index of the column in the ResultSet from which the value will
- * be used to populate the object property
- */
- public function getColumnIndex()
- {
- return $this->_columnIndex;
- }
-
- /**
- * @param int index of the column in the ResultSet from which the value will
- * be used to populate the object property
- */
- public function setColumnIndex($value)
- {
- $this->_columnIndex = TPropertyValue::ensureInteger($value);
- }
-
- /**
- * @return string ID of another <resultMap> used to fill the property.
- */
- public function getResultMapping()
- {
- return $this->_nestedResultMapName;
- }
-
- /**
- * @param string ID of another <resultMap> used to fill the property.
- */
- public function setResultMapping($value)
- {
- $this->_nestedResultMapName = $value;
- }
-
- /**
- * @return TResultMap nested result map.
- */
- public function getNestedResultMap()
- {
- return $this->_nestedResultMap;
- }
-
- /**
- * @param TResult nested result map.
- */
- public function setNestedResultMap($value)
- {
- $this->_nestedResultMap = $value;
- }
-
- /**
- * @return string property type of the object property to be set.
- */
- public function getType()
- {
- return $this->_valueType;
- }
-
- /**
- * @param string property type of the object property to be set.
- */
- public function setType($value)
- {
- $this->_valueType = $value;
- }
-
- /**
- * @return string custom type handler class name (may use namespace).
- */
- public function getTypeHandler()
- {
- return $this->_typeHandler;
- }
-
- /**
- * @param string custom type handler class name (may use namespace).
- */
- public function setTypeHandler($value)
- {
- $this->_typeHandler = $value;
- }
-
- /**
- * @return string name of another mapped statement
- */
- public function getSelect()
- {
- return $this->_select;
- }
-
- /**
- * The select property is used to describe a relationship between objects
- * and to automatically load complex (i.e. user defined) property types.
- * @param string name of another mapped statement.
- */
- public function setSelect($value)
- {
- $this->_select = $value;
- }
-
- /**
- * @return boolean indicate whether or not the select statement's results should be lazy loaded
- */
- public function getLazyLoad()
- {
- return $this->_isLazyLoad;
- }
-
- /**
- * @param boolean indicate whether or not the select statement's results should be lazy loaded
- */
- public function setLazyLoad($value)
- {
- $this->_isLazyLoad = TPropertyValue::ensureBoolean($value,false);
- }
-
- /**
- * Gets the value for the current property, converts to applicable type if necessary.
- * @param TSqlMapTypeHandlerRegistry type handler registry
- * @param array result row
- * @return mixed property value.
- */
- public function getPropertyValue($registry,$row)
- {
- $value = null;
- $index = $this->getColumnIndex();
- $name = $this->getColumn();
- if($index > 0 && isset($row[$index]))
- $value = $this->getTypedValue($registry,$row[$index]);
- else if(isset($row[$name]))
- $value = $this->getTypedValue($registry,$row[$name]);
- if(($value===null) && ($this->getNullValue()!==null))
- $value = $this->getTypedValue($registry,$this->getNullValue());
- return $value;
- }
-
- /**
- * @param TSqlMapTypeHandlerRegistry type handler registry
- * @param mixed raw property value
- * @return mixed property value casted to specific type.
- */
- protected function getTypedValue($registry,$value)
- {
- if(($handler = $this->createTypeHandler($registry))!==null)
- return $handler->getResult($value);
- else
- return $registry->convertToType($this->getType(), $value);
- }
-
- /**
- * Create type handler from {@link Type setType()} or {@link TypeHandler setTypeHandler}.
- * @param TSqlMapTypeHandlerRegistry type handler registry
- * @return TSqlMapTypeHandler type handler.
- */
- protected function createTypeHandler($registry)
- {
- $type=$this->getTypeHandler() ? $this->getTypeHandler() : $this->getType();
- $handler=$registry->getTypeHandler($type);
- if($handler===null && $this->getTypeHandler())
- $handler = Prado::createComponent($type);
- return $handler;
- }
-
- /**
- * Determines if the type is an instance of ArrayAccess, TList or an array.
- * @return int TResultProperty::LIST_TYPE or TResultProperty::ARRAY_TYPE
- */
- protected function getPropertyValueType()
- {
- if(class_exists($type = $this->getType(), false)) //NO force autoloading
- {
- if($type==='TList')
- return self::LIST_TYPE;
- $class = new ReflectionClass($type);
- if($class->isSubclassOf('TList'))
- return self::LIST_TYPE;
- if($class->implementsInterface('ArrayAccess'))
- return self::ARRAY_TYPE;
- }
- if(strtolower($type) == 'array')
- return self::ARRAY_TYPE;
- }
-
- /**
- * Returns true if the result property {@link Type getType()} is of TList type
- * or that the actual result object is an instance of TList.
- * @param object result object
- * @return boolean true if the result object is an instance of TList
- */
- public function instanceOfListType($target)
- {
- if($this->getType()===null)
- return TPropertyAccess::get($target,$this->getProperty()) instanceof TList;
- return $this->getPropertyValueType() == self::LIST_TYPE;
- }
-
- /**
- * Returns true if the result property {@link Type getType()} is of ArrayAccess
- * or that the actual result object is an array or implements ArrayAccess
- * @param object result object
- * @return boolean true if the result object is an instance of ArrayAccess or is an array.
- */
- public function instanceOfArrayType($target)
- {
- if($this->getType()===null)
- {
- $prop = TPropertyAccess::get($target,$this->getProperty());
- if(is_object($prop))
- return $prop instanceof ArrayAccess;
- return is_array($prop);
- }
- return $this->getPropertyValueType() == self::ARRAY_TYPE;
- }
-
- public function __sleep()
- {
- $exprops = array(); $cn = 'TResultProperty';
- if ($this->_nullValue===null) $exprops[] = "\0$cn\0_nullValue";
- if ($this->_propertyName===null) $exprops[] = "\0$cn\0_propertyNama";
- if ($this->_columnName===null) $exprops[] = "\0$cn\0_columnName";
- if ($this->_columnIndex==-1) $exprops[] = "\0$cn\0_columnIndex";
- if ($this->_nestedResultMapName===null) $exprops[] = "\0$cn\0_nestedResultMapName";
- if ($this->_nestedResultMap===null) $exprops[] = "\0$cn\0_nestedResultMap";
- if ($this->_valueType===null) $exprops[] = "\0$cn\0_valueType";
- if ($this->_typeHandler===null) $exprops[] = "\0$cn\0_typeHandler";
- if ($this->_isLazyLoad===false) $exprops[] = "\0$cn\0_isLazyLoad";
- if ($this->_select===null) $exprops[] = "\0$cn\0_select";
- return array_diff(parent::__sleep(),$exprops);
- }
-}
-
+<?php +/** + * TResultProperty class file. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.SqlMap.Configuration + */ + +/** + * TResultProperty corresponds a <property> tags inside a <resultMap> tag. + * + * The {@link NullValue setNullValue()} attribute can be set to any valid + * value (based on property type). The {@link NullValue setNullValue()} attribute + * is used to specify an outgoing null value replacement. What this means is + * that when a null value is detected in the result, the corresponding value of + * the {@link NullValue getNullValue()} will be used instead. + * + * The {@link Select setSelect()} property is used to describe a relationship + * between objects and to automatically load complex (i.e. user defined) + * property types. The value of the {@link Select setSelect()} property must be + * the name of another mapped statement. The value of the database + * {@link Column setColumn()} that is defined in the same property element as + * this statement attribute will be passed to the related mapped statement as + * the parameter. The {@link LazyLoad setLayLoad()} attribute can be specified + * with the {@link Select setSelect()} . + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.SqlMap.Configuration + * @since 3.1 + */ +class TResultProperty extends TComponent +{ + private $_nullValue; + private $_propertyName; + private $_columnName; + private $_columnIndex=-1; + private $_nestedResultMapName; + private $_nestedResultMap; + private $_valueType; + private $_typeHandler; + private $_isLazyLoad=false; + private $_select; + + private $_hostResultMapID='inplicit internal mapping'; + + const LIST_TYPE = 0; + const ARRAY_TYPE = 1; + + /** + * Gets the containing result map ID. + * @param TResultMap containing result map. + */ + public function __construct($resultMap=null) + { + if($resultMap instanceof TResultMap) + $this->_hostResultMapID = $resultMap->getID(); + } + + /** + * @return mixed null value replacement. + */ + public function getNullValue() + { + return $this->_nullValue; + } + + /** + * @param mixed null value replacement. + */ + public function setNullValue($value) + { + $this->_nullValue = $value; + } + + /** + * @return string name of a property of the result object that will be set to. + */ + public function getProperty() + { + return $this->_propertyName; + } + + /** + * @param string name of a property of the result object that will be set to. + */ + public function setProperty($value) + { + $this->_propertyName = $value; + } + + /** + * @return string name of the column in the result set from which the value + * will be used to populate the property. + */ + public function getColumn() + { + return $this->_columnName; + } + + /** + * @param string name of the column in the result set from which the value + * will be used to populate the property. + */ + public function setColumn($value) + { + $this->_columnName = $value; + } + + /** + * @return int index of the column in the ResultSet from which the value will + * be used to populate the object property + */ + public function getColumnIndex() + { + return $this->_columnIndex; + } + + /** + * @param int index of the column in the ResultSet from which the value will + * be used to populate the object property + */ + public function setColumnIndex($value) + { + $this->_columnIndex = TPropertyValue::ensureInteger($value); + } + + /** + * @return string ID of another <resultMap> used to fill the property. + */ + public function getResultMapping() + { + return $this->_nestedResultMapName; + } + + /** + * @param string ID of another <resultMap> used to fill the property. + */ + public function setResultMapping($value) + { + $this->_nestedResultMapName = $value; + } + + /** + * @return TResultMap nested result map. + */ + public function getNestedResultMap() + { + return $this->_nestedResultMap; + } + + /** + * @param TResult nested result map. + */ + public function setNestedResultMap($value) + { + $this->_nestedResultMap = $value; + } + + /** + * @return string property type of the object property to be set. + */ + public function getType() + { + return $this->_valueType; + } + + /** + * @param string property type of the object property to be set. + */ + public function setType($value) + { + $this->_valueType = $value; + } + + /** + * @return string custom type handler class name (may use namespace). + */ + public function getTypeHandler() + { + return $this->_typeHandler; + } + + /** + * @param string custom type handler class name (may use namespace). + */ + public function setTypeHandler($value) + { + $this->_typeHandler = $value; + } + + /** + * @return string name of another mapped statement + */ + public function getSelect() + { + return $this->_select; + } + + /** + * The select property is used to describe a relationship between objects + * and to automatically load complex (i.e. user defined) property types. + * @param string name of another mapped statement. + */ + public function setSelect($value) + { + $this->_select = $value; + } + + /** + * @return boolean indicate whether or not the select statement's results should be lazy loaded + */ + public function getLazyLoad() + { + return $this->_isLazyLoad; + } + + /** + * @param boolean indicate whether or not the select statement's results should be lazy loaded + */ + public function setLazyLoad($value) + { + $this->_isLazyLoad = TPropertyValue::ensureBoolean($value,false); + } + + /** + * Gets the value for the current property, converts to applicable type if necessary. + * @param TSqlMapTypeHandlerRegistry type handler registry + * @param array result row + * @return mixed property value. + */ + public function getPropertyValue($registry,$row) + { + $value = null; + $index = $this->getColumnIndex(); + $name = $this->getColumn(); + if($index > 0 && isset($row[$index])) + $value = $this->getTypedValue($registry,$row[$index]); + else if(isset($row[$name])) + $value = $this->getTypedValue($registry,$row[$name]); + if(($value===null) && ($this->getNullValue()!==null)) + $value = $this->getTypedValue($registry,$this->getNullValue()); + return $value; + } + + /** + * @param TSqlMapTypeHandlerRegistry type handler registry + * @param mixed raw property value + * @return mixed property value casted to specific type. + */ + protected function getTypedValue($registry,$value) + { + if(($handler = $this->createTypeHandler($registry))!==null) + return $handler->getResult($value); + else + return $registry->convertToType($this->getType(), $value); + } + + /** + * Create type handler from {@link Type setType()} or {@link TypeHandler setTypeHandler}. + * @param TSqlMapTypeHandlerRegistry type handler registry + * @return TSqlMapTypeHandler type handler. + */ + protected function createTypeHandler($registry) + { + $type=$this->getTypeHandler() ? $this->getTypeHandler() : $this->getType(); + $handler=$registry->getTypeHandler($type); + if($handler===null && $this->getTypeHandler()) + $handler = Prado::createComponent($type); + return $handler; + } + + /** + * Determines if the type is an instance of ArrayAccess, TList or an array. + * @return int TResultProperty::LIST_TYPE or TResultProperty::ARRAY_TYPE + */ + protected function getPropertyValueType() + { + if(class_exists($type = $this->getType(), false)) //NO force autoloading + { + if($type==='TList') + return self::LIST_TYPE; + $class = new ReflectionClass($type); + if($class->isSubclassOf('TList')) + return self::LIST_TYPE; + if($class->implementsInterface('ArrayAccess')) + return self::ARRAY_TYPE; + } + if(strtolower($type) == 'array') + return self::ARRAY_TYPE; + } + + /** + * Returns true if the result property {@link Type getType()} is of TList type + * or that the actual result object is an instance of TList. + * @param object result object + * @return boolean true if the result object is an instance of TList + */ + public function instanceOfListType($target) + { + if($this->getType()===null) + return TPropertyAccess::get($target,$this->getProperty()) instanceof TList; + return $this->getPropertyValueType() == self::LIST_TYPE; + } + + /** + * Returns true if the result property {@link Type getType()} is of ArrayAccess + * or that the actual result object is an array or implements ArrayAccess + * @param object result object + * @return boolean true if the result object is an instance of ArrayAccess or is an array. + */ + public function instanceOfArrayType($target) + { + if($this->getType()===null) + { + $prop = TPropertyAccess::get($target,$this->getProperty()); + if(is_object($prop)) + return $prop instanceof ArrayAccess; + return is_array($prop); + } + return $this->getPropertyValueType() == self::ARRAY_TYPE; + } + + public function __sleep() + { + $exprops = array(); $cn = 'TResultProperty'; + if ($this->_nullValue===null) $exprops[] = "\0$cn\0_nullValue"; + if ($this->_propertyName===null) $exprops[] = "\0$cn\0_propertyNama"; + if ($this->_columnName===null) $exprops[] = "\0$cn\0_columnName"; + if ($this->_columnIndex==-1) $exprops[] = "\0$cn\0_columnIndex"; + if ($this->_nestedResultMapName===null) $exprops[] = "\0$cn\0_nestedResultMapName"; + if ($this->_nestedResultMap===null) $exprops[] = "\0$cn\0_nestedResultMap"; + if ($this->_valueType===null) $exprops[] = "\0$cn\0_valueType"; + if ($this->_typeHandler===null) $exprops[] = "\0$cn\0_typeHandler"; + if ($this->_isLazyLoad===false) $exprops[] = "\0$cn\0_isLazyLoad"; + if ($this->_select===null) $exprops[] = "\0$cn\0_select"; + return array_diff(parent::__sleep(),$exprops); + } +} + diff --git a/framework/Data/SqlMap/Configuration/TSimpleDynamicParser.php b/framework/Data/SqlMap/Configuration/TSimpleDynamicParser.php index 1ceba9eb..a70e6b3f 100644 --- a/framework/Data/SqlMap/Configuration/TSimpleDynamicParser.php +++ b/framework/Data/SqlMap/Configuration/TSimpleDynamicParser.php @@ -1,45 +1,45 @@ -<?php
-/**
- * TSimpleDynamicParser class file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
+<?php +/** + * TSimpleDynamicParser class file. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.SqlMap.Configuration
- */
-
-/**
- * TSimpleDynamicParser finds place holders $name$ in the sql text and replaces
- * it with a TSimpleDynamicParser::DYNAMIC_TOKEN.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.SqlMap.Configuration
- * @since 3.1
- */
-class TSimpleDynamicParser
-{
- const PARAMETER_TOKEN_REGEXP = '/\$([^\$]+)\$/';
- const DYNAMIC_TOKEN = '`!`';
-
- /**
- * Parse the sql text for dynamic place holders of the form $name$.
- * @param string Sql text.
- * @return array name value pairs 'sql' and 'parameters'.
- */
- public function parse($sqlText)
- {
- $matches = array();
- $mappings = array();
- preg_match_all(self::PARAMETER_TOKEN_REGEXP, $sqlText, $matches);
- for($i = 0, $k=count($matches[1]); $i<$k; $i++)
- {
- $mappings[] = $matches[1][$i];
- $sqlText = str_replace($matches[0][$i], self::DYNAMIC_TOKEN, $sqlText);
- }
- return array('sql'=>$sqlText, 'parameters'=>$mappings);
- }
-}
-
+ * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.SqlMap.Configuration + */ + +/** + * TSimpleDynamicParser finds place holders $name$ in the sql text and replaces + * it with a TSimpleDynamicParser::DYNAMIC_TOKEN. + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.SqlMap.Configuration + * @since 3.1 + */ +class TSimpleDynamicParser +{ + const PARAMETER_TOKEN_REGEXP = '/\$([^\$]+)\$/'; + const DYNAMIC_TOKEN = '`!`'; + + /** + * Parse the sql text for dynamic place holders of the form $name$. + * @param string Sql text. + * @return array name value pairs 'sql' and 'parameters'. + */ + public function parse($sqlText) + { + $matches = array(); + $mappings = array(); + preg_match_all(self::PARAMETER_TOKEN_REGEXP, $sqlText, $matches); + for($i = 0, $k=count($matches[1]); $i<$k; $i++) + { + $mappings[] = $matches[1][$i]; + $sqlText = str_replace($matches[0][$i], self::DYNAMIC_TOKEN, $sqlText); + } + return array('sql'=>$sqlText, 'parameters'=>$mappings); + } +} + diff --git a/framework/Data/SqlMap/Configuration/TSqlMapCacheModel.php b/framework/Data/SqlMap/Configuration/TSqlMapCacheModel.php index 4b985e51..0dc5d821 100644 --- a/framework/Data/SqlMap/Configuration/TSqlMapCacheModel.php +++ b/framework/Data/SqlMap/Configuration/TSqlMapCacheModel.php @@ -1,246 +1,246 @@ -<?php
-/**
- * TSqlMapCacheModel, TSqlMapCacheTypes and TSqlMapCacheKey classes file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.SqlMap.Configuration
- */
-
-/**
- * TSqlMapCacheModel corresponds to the <cacheModel> sql mapping configuration tag.
- *
- * The results from a query Mapped Statement can be cached simply by specifying
- * the {@link CacheModel TSqlMapStatement::setCacheModel()} property in <statement> tag.
- * A cache model is a configured cache that is defined within the sql map
- * configuration file. Cache models are configured using the <cacheModel> element.
- *
- * The cache model uses a pluggable framework for supporting different types of
- * caches. The choice of cache is specified by the {@link Implementation setImplementation()}
- * property. The class name specified must be one of {@link TSqlMapCacheTypes}.
- *
- * The cache implementations, LRU and FIFO cache below do not persist across
- * requests. That is, once the request is complete, all cache data is lost.
- * These caches are useful queries that results in the same repeated data during
- * the current request.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.SqlMap.Configuration
- * @since 3.1
- */
-class TSqlMapCacheModel extends TComponent
-{
- private $_cache;
- private $_hits = 0;
- private $_requests = 0;
- private $_id;
- private $_implementation=TSqlMapCacheTypes::Basic;
- private $_properties = array();
- private $_flushInterval = 0;
-
- private static $_cacheTypes = array();
-
- public static function registerCacheType($type, $className)
- {
- self::$_cacheTypes[$type] = $className;
- }
-
- /**
- * @return string unique cache model identifier.
- */
- public function getID()
- {
- return $this->_id;
- }
-
- /**
- * @param string unique cache model identifier.
- */
- public function setID($value)
- {
- $this->_id = $value;
- }
-
- /**
- * @return string cache implements of TSqlMapCacheTypes, either 'Basic', 'LRU' or 'FIFO'.
- */
- public function getImplementation()
- {
- return $this->_implementation;
- }
-
- /**
- * @param string cache implements of TSqlMapCacheTypes, either 'Basic', 'LRU' or 'FIFO'.
- */
- public function setImplementation($value)
- {
- if (isset(self::$_cacheTypes[$value]))
- $this->_implementation = $value;
- else
- $this->_implementation = TPropertyValue::ensureEnum($value,'TSqlMapCacheTypes');
- }
-
- /**
- * @param integer the number of seconds in which the cached value will expire. 0 means never expire.
- */
- public function setFlushInterval($value)
- {
- $this->_flushInterval=TPropertyValue::ensureInteger($value);
- }
-
- /**
- * @return integer cache duration.
- */
- public function getFlushInterval()
- {
- return $this->_flushInterval;
- }
-
- /**
- * Initialize the cache implementation, sets the actual cache contain if supplied.
- * @param ISqLMapCache cache implementation instance.
- */
- public function initialize($cache=null)
- {
- if($cache===null)
- $this->_cache= Prado::createComponent($this->getImplementationClass(), $this);
- else
- $this->_cache=$cache;
- }
-
- /**
- * @return string cache implementation class name.
- */
- public function getImplementationClass()
- {
- $implementation = $this->_implementation;
- if (isset(self::$_cacheTypes[$implementation])) return self::$_cacheTypes[$implementation];
-
- switch(TPropertyValue::ensureEnum($implementation,'TSqlMapCacheTypes'))
- {
- case TSqlMapCacheTypes::FIFO: return 'TSqlMapFifoCache';
- case TSqlMapCacheTypes::LRU : return 'TSqlMapLruCache';
- case TSqlMapCacheTypes::Basic : return 'TSqlMapApplicationCache';
- }
- }
-
- /**
- * Register a mapped statement that will trigger a cache flush.
- * @param TMappedStatement mapped statement that may flush the cache.
- */
- public function registerTriggerStatement($mappedStatement)
- {
- $mappedStatement->attachEventHandler('OnExecuteQuery',array($this, 'flush'));
- }
-
- /**
- * Clears the cache.
- */
- public function flush()
- {
- $this->_cache->flush();
- }
-
- /**
- * @param TSqlMapCacheKey|string cache key
- * @return mixed cached value.
- */
- public function get($key)
- {
- if($key instanceof TSqlMapCacheKey)
- $key = $key->getHash();
-
- //if flush ?
- $value = $this->_cache->get($key);
- $this->_requests++;
- if($value!==null)
- $this->_hits++;
- return $value;
- }
-
- /**
- * @param TSqlMapCacheKey|string cache key
- * @param mixed value to be cached.
- */
- public function set($key, $value)
- {
- if($key instanceof TSqlMapCacheKey)
- $key = $key->getHash();
-
- if($value!==null)
- $this->_cache->set($key, $value, $this->_flushInterval);
- }
-
- /**
- * @return float cache hit ratio.
- */
- public function getHitRatio()
- {
- if($this->_requests != 0)
- return $this->_hits / $this->_requests;
- else
- return 0;
- }
-}
-
-/**
- * TSqlMapCacheTypes enumerable class.
- *
- * Implemented cache are 'Basic', 'FIFO' and 'LRU'.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.SqlMap.Configuration
- * @since 3.1
- */
-class TSqlMapCacheTypes extends TEnumerable
-{
- const Basic='Basic';
- const FIFO='FIFO';
- const LRU='LRU';
-}
-
-/**
- * TSqlMapCacheKey class.
- *
- * Provides a hash of the object to be cached.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.SqlMap.Configuration
- * @since 3.1
- */
-class TSqlMapCacheKey
-{
- private $_key;
-
- /**
- * @param mixed object to be cached.
- */
- public function __construct($object)
- {
- $this->_key = $this->generateKey(serialize($object));
- }
-
- /**
- * @param string serialized object
- * @return string crc32 hash of the serialized object.
- */
- protected function generateKey($string)
- {
- return sprintf('%x',crc32($string));
- }
-
- /**
- * @return string object hash.
- */
- public function getHash()
- {
- return $this->_key;
- }
-}
-
+<?php +/** + * TSqlMapCacheModel, TSqlMapCacheTypes and TSqlMapCacheKey classes file. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.SqlMap.Configuration + */ + +/** + * TSqlMapCacheModel corresponds to the <cacheModel> sql mapping configuration tag. + * + * The results from a query Mapped Statement can be cached simply by specifying + * the {@link CacheModel TSqlMapStatement::setCacheModel()} property in <statement> tag. + * A cache model is a configured cache that is defined within the sql map + * configuration file. Cache models are configured using the <cacheModel> element. + * + * The cache model uses a pluggable framework for supporting different types of + * caches. The choice of cache is specified by the {@link Implementation setImplementation()} + * property. The class name specified must be one of {@link TSqlMapCacheTypes}. + * + * The cache implementations, LRU and FIFO cache below do not persist across + * requests. That is, once the request is complete, all cache data is lost. + * These caches are useful queries that results in the same repeated data during + * the current request. + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.SqlMap.Configuration + * @since 3.1 + */ +class TSqlMapCacheModel extends TComponent +{ + private $_cache; + private $_hits = 0; + private $_requests = 0; + private $_id; + private $_implementation=TSqlMapCacheTypes::Basic; + private $_properties = array(); + private $_flushInterval = 0; + + private static $_cacheTypes = array(); + + public static function registerCacheType($type, $className) + { + self::$_cacheTypes[$type] = $className; + } + + /** + * @return string unique cache model identifier. + */ + public function getID() + { + return $this->_id; + } + + /** + * @param string unique cache model identifier. + */ + public function setID($value) + { + $this->_id = $value; + } + + /** + * @return string cache implements of TSqlMapCacheTypes, either 'Basic', 'LRU' or 'FIFO'. + */ + public function getImplementation() + { + return $this->_implementation; + } + + /** + * @param string cache implements of TSqlMapCacheTypes, either 'Basic', 'LRU' or 'FIFO'. + */ + public function setImplementation($value) + { + if (isset(self::$_cacheTypes[$value])) + $this->_implementation = $value; + else + $this->_implementation = TPropertyValue::ensureEnum($value,'TSqlMapCacheTypes'); + } + + /** + * @param integer the number of seconds in which the cached value will expire. 0 means never expire. + */ + public function setFlushInterval($value) + { + $this->_flushInterval=TPropertyValue::ensureInteger($value); + } + + /** + * @return integer cache duration. + */ + public function getFlushInterval() + { + return $this->_flushInterval; + } + + /** + * Initialize the cache implementation, sets the actual cache contain if supplied. + * @param ISqLMapCache cache implementation instance. + */ + public function initialize($cache=null) + { + if($cache===null) + $this->_cache= Prado::createComponent($this->getImplementationClass(), $this); + else + $this->_cache=$cache; + } + + /** + * @return string cache implementation class name. + */ + public function getImplementationClass() + { + $implementation = $this->_implementation; + if (isset(self::$_cacheTypes[$implementation])) return self::$_cacheTypes[$implementation]; + + switch(TPropertyValue::ensureEnum($implementation,'TSqlMapCacheTypes')) + { + case TSqlMapCacheTypes::FIFO: return 'TSqlMapFifoCache'; + case TSqlMapCacheTypes::LRU : return 'TSqlMapLruCache'; + case TSqlMapCacheTypes::Basic : return 'TSqlMapApplicationCache'; + } + } + + /** + * Register a mapped statement that will trigger a cache flush. + * @param TMappedStatement mapped statement that may flush the cache. + */ + public function registerTriggerStatement($mappedStatement) + { + $mappedStatement->attachEventHandler('OnExecuteQuery',array($this, 'flush')); + } + + /** + * Clears the cache. + */ + public function flush() + { + $this->_cache->flush(); + } + + /** + * @param TSqlMapCacheKey|string cache key + * @return mixed cached value. + */ + public function get($key) + { + if($key instanceof TSqlMapCacheKey) + $key = $key->getHash(); + + //if flush ? + $value = $this->_cache->get($key); + $this->_requests++; + if($value!==null) + $this->_hits++; + return $value; + } + + /** + * @param TSqlMapCacheKey|string cache key + * @param mixed value to be cached. + */ + public function set($key, $value) + { + if($key instanceof TSqlMapCacheKey) + $key = $key->getHash(); + + if($value!==null) + $this->_cache->set($key, $value, $this->_flushInterval); + } + + /** + * @return float cache hit ratio. + */ + public function getHitRatio() + { + if($this->_requests != 0) + return $this->_hits / $this->_requests; + else + return 0; + } +} + +/** + * TSqlMapCacheTypes enumerable class. + * + * Implemented cache are 'Basic', 'FIFO' and 'LRU'. + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.SqlMap.Configuration + * @since 3.1 + */ +class TSqlMapCacheTypes extends TEnumerable +{ + const Basic='Basic'; + const FIFO='FIFO'; + const LRU='LRU'; +} + +/** + * TSqlMapCacheKey class. + * + * Provides a hash of the object to be cached. + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.SqlMap.Configuration + * @since 3.1 + */ +class TSqlMapCacheKey +{ + private $_key; + + /** + * @param mixed object to be cached. + */ + public function __construct($object) + { + $this->_key = $this->generateKey(serialize($object)); + } + + /** + * @param string serialized object + * @return string crc32 hash of the serialized object. + */ + protected function generateKey($string) + { + return sprintf('%x',crc32($string)); + } + + /** + * @return string object hash. + */ + public function getHash() + { + return $this->_key; + } +} + diff --git a/framework/Data/SqlMap/Configuration/TSqlMapStatement.php b/framework/Data/SqlMap/Configuration/TSqlMapStatement.php index 1d90d4b1..8f2ca5cc 100644 --- a/framework/Data/SqlMap/Configuration/TSqlMapStatement.php +++ b/framework/Data/SqlMap/Configuration/TSqlMapStatement.php @@ -1,451 +1,451 @@ -<?php
-/**
- * TSqlMapStatement, TSqlMapInsert, TSqlMapUpdate, TSqlMapDelete,
- * TSqlMapSelect and TSqlMapSelectKey classes file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.SqlMap.Configuration
- */
-
-/**
- * TSqlMapStatement class corresponds to <statement> element.
- *
- * Mapped Statements can hold any SQL statement and can use Parameter Maps
- * and Result Maps for input and output.
- *
- * The <statement> element is a general "catch all" element that can be used
- * for any type of SQL statement. Generally it is a good idea to use one of the
- * more specific statement-type elements. The more specific elements provided
- * better error-checking and even more functionality. (For example, the insert
- * statement can return a database-generated key.)
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.SqlMap.Configuration
- * @since 3.1
- */
-class TSqlMapStatement extends TComponent
-{
- private $_parameterMapName;
- private $_parameterMap;
- private $_parameterClassName;
- private $_resultMapName;
- private $_resultMap;
- private $_resultClassName;
- private $_cacheModelName;
- private $_SQL;
- private $_listClass;
- private $_typeHandler;
- private $_extendStatement;
- private $_cache;
- private $_ID;
-
- /**
- * @return string name for this statement, unique to each sql map manager.
- */
- public function getID()
- {
- return $this->_ID;
- }
-
- /**
- * @param string name for this statement, which must be unique for each sql map manager.
- */
- public function setID($value)
- {
- $this->_ID=$value;
- }
-
- /**
- * @return string name of a parameter map.
- */
- public function getParameterMap()
- {
- return $this->_parameterMapName;
- }
-
- /**
- * A Parameter Map defines an ordered list of values that match up with
- * the "?" placeholders of a standard, parameterized query statement.
- * @param string parameter map name.
- */
- public function setParameterMap($value)
- {
- $this->_parameterMapName = $value;
- }
-
- /**
- * @return string parameter class name.
- */
- public function getParameterClass()
- {
- return $this->_parameterClassName;
- }
-
- /**
- * If a {@link ParameterMap setParameterMap()} property is not specified,
- * you may specify a ParameterClass instead and use inline parameters.
- * The value of the parameterClass attribute can be any existing PHP class name.
- * @param string parameter class name.
- */
- public function setParameterClass($value)
- {
- $this->_parameterClassName = $value;
- }
-
- /**
- * @return string result map name.
- */
- public function getResultMap()
- {
- return $this->_resultMapName;
- }
-
- /**
- * A Result Map lets you control how data is extracted from the result of a
- * query, and how the columns are mapped to object properties.
- * @param string result map name.
- */
- public function setResultMap($value)
- {
- $this->_resultMapName = $value;
- }
-
- /**
- * @return string result class name.
- */
- public function getResultClass()
- {
- return $this->_resultClassName;
- }
-
- /**
- * If a {@link ResultMap setResultMap()} is not specified, you may specify a
- * ResultClass instead. The value of the ResultClass property can be the
- * name of a PHP class or primitives like integer, string, or array. The
- * class specified will be automatically mapped to the columns in the
- * result, based on the result metadata.
- * @param string result class name.
- */
- public function setResultClass($value)
- {
- $this->_resultClassName = $value;
- }
-
- /**
- * @return string cache mode name.
- */
- public function getCacheModel()
- {
- return $this->_cacheModelName;
- }
-
- /**
- * @param string cache mode name.
- */
- public function setCacheModel($value)
- {
- $this->_cacheModelName = $value;
- }
-
- /**
- * @return TSqlMapCacheModel cache implementation instance for this statement.
- */
- public function getCache()
- {
- return $this->_cache;
- }
-
- /**
- * @param TSqlMapCacheModel cache implementation instance for this statement.
- */
- public function setCache($value)
- {
- $this->_cache = $value;
- }
-
- /**
- * @return TStaticSql sql text container.
- */
- public function getSqlText()
- {
- return $this->_SQL;
- }
-
- /**
- * @param TStaticSql sql text container.
- */
- public function setSqlText($value)
- {
- $this->_SQL = $value;
- }
-
- /**
- * @return string name of a PHP class that implements ArrayAccess.
- */
- public function getListClass()
- {
- return $this->_listClass;
- }
-
- /**
- * An ArrayAccess class can be specified to handle the type of objects in the collection.
- * @param string name of a PHP class that implements ArrayAccess.
- */
- public function setListClass($value)
- {
- $this->_listClass = $value;
- }
-
- /**
- * @return string another statement element name.
- */
- public function getExtends()
- {
- return $this->_extendStatement;
- }
-
- /**
- * @param string name of another statement element to extend.
- */
- public function setExtends($value)
- {
- $this->_extendStatement = $value;
- }
-
- /**
- * @return TResultMap the result map corresponding to the
- * {@link ResultMap getResultMap()} property.
- */
- public function resultMap()
- {
- return $this->_resultMap;
- }
-
- /**
- * @return TParameterMap the parameter map corresponding to the
- * {@link ParameterMap getParameterMap()} property.
- */
- public function parameterMap()
- {
- return $this->_parameterMap;
- }
-
- /**
- * @param TInlineParameterMap parameter extracted from the sql text.
- */
- public function setInlineParameterMap($map)
- {
- $this->_parameterMap = $map;
- }
-
- /**
- * @param TSqlMapManager initialize the statement, sets the result and parameter maps.
- */
- public function initialize($manager)
- {
- if(strlen($this->_resultMapName) > 0)
- $this->_resultMap = $manager->getResultMap($this->_resultMapName);
- if(strlen($this->_parameterMapName) > 0)
- $this->_parameterMap = $manager->getParameterMap($this->_parameterMapName);
- }
-
- /**
- * @param TSqlMapTypeHandlerRegistry type handler registry
- * @return ArrayAccess new instance of list class.
- */
- public function createInstanceOfListClass($registry)
- {
- if(strlen($type = $this->getListClass()) > 0)
- return $this->createInstanceOf($registry,$type);
- return array();
- }
-
- /**
- * Create a new instance of a given type.
- * @param TSqlMapTypeHandlerRegistry type handler registry
- * @param string result class name.
- * @param array result data.
- * @return mixed result object.
- */
- protected function createInstanceOf($registry,$type,$row=null)
- {
- $handler = $registry->getTypeHandler($type);
- if($handler!==null)
- return $handler->createNewInstance($row);
- else
- return $registry->createInstanceOf($type);
- }
-
- /**
- * Create a new instance of result class.
- * @param TSqlMapTypeHandlerRegistry type handler registry
- * @param array result data.
- * @return mixed result object.
- */
- public function createInstanceOfResultClass($registry,$row)
- {
- if(strlen($type= $this->getResultClass()) > 0)
- return $this->createInstanceOf($registry,$type,$row);
- }
-
- public function __sleep()
- {
- $cn = __CLASS__;
- $exprops = array("\0$cn\0_resultMap");
- if (!$this->_parameterMapName) $exprops[] = "\0$cn\0_parameterMapName";
- if (!$this->_parameterMap) $exprops[] = "\0$cn\0_parameterMap";
- if (!$this->_parameterClassName) $exprops[] = "\0$cn\0_parameterClassName";
- if (!$this->_resultMapName) $exprops[] = "\0$cn\0_resultMapName";
- if (!$this->_resultMap) $exprops[] = "\0$cn\0_resultMap";
- if (!$this->_resultClassName) $exprops[] = "\0$cn\0_resultClassName";
- if (!$this->_cacheModelName) $exprops[] = "\0$cn\0_cacheModelName";
- if (!$this->_SQL) $exprops[] = "\0$cn\0_SQL";
- if (!$this->_listClass) $exprops[] = "\0$cn\0_listClass";
- if (!$this->_typeHandler) $exprops[] = "\0$cn\0_typeHandler";
- if (!$this->_extendStatement) $exprops[] = "\0$cn\0_extendStatement";
- if (!$this->_cache) $exprops[] = "\0$cn\0_cache";
-
- return array_diff(parent::__sleep(),$exprops);
- }
-
-}
-
-/**
- * TSqlMapSelect class file.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.SqlMap.Statements
- * @since 3.1
- */
-class TSqlMapSelect extends TSqlMapStatement
-{
- private $_generate;
-
- public function getGenerate(){ return $this->_generate; }
- public function setGenerate($value){ $this->_generate = $value; }
-}
-
-/**
- * TSqlMapInsert class corresponds to the <insert> element.
- *
- * The <insert> element allows <selectKey> child elements that can be used
- * to generate a key to be used for the insert command.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.SqlMap.Configuration
- * @since 3.1
- */
-class TSqlMapInsert extends TSqlMapStatement
-{
- private $_selectKey=null;
-
- /**
- * @return TSqlMapSelectKey select key element.
- */
- public function getSelectKey()
- {
- return $this->_selectKey;
- }
-
- /**
- * @param TSqlMapSelectKey select key.
- */
- public function setSelectKey($value)
- {
- $this->_selectKey = $value;
- }
-}
-
-/**
- * TSqlMapUpdate class corresponds to <update> element.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.SqlMap.Configuration
- * @since 3.1
- */
-class TSqlMapUpdate extends TSqlMapStatement
-{
-}
-
-/**
- * TSqlMapDelete class corresponds to the <delete> element.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.SqlMap.Configuration
- * @since 3.1
- */
-class TSqlMapDelete extends TSqlMapUpdate
-{
-}
-
-/**
- * TSqlMapSelect corresponds to the <selectKey> element.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.SqlMap.Configuration
- * @since 3.1
- */
-class TSqlMapSelectKey extends TSqlMapStatement
-{
- private $_type = 'post';
- private $_property;
-
- /**
- * @return string select generated key type, 'post' or 'pre'.
- */
- public function getType()
- {
- return $this->_type;
- }
-
- /**
- * @param string select generated key type, 'post' or 'pre'.
- */
- public function setType($value)
- {
- $this->_type = strtolower($value) == 'post' ? 'post' : 'pre';
- }
-
- /**
- * @return string property name for the generated key.
- */
- public function getProperty()
- {
- return $this->_property;
- }
-
- /**
- * @param string property name for the generated key.
- */
- public function setProperty($value)
- {
- $this->_property = $value;
- }
-
- /**
- * @throws TSqlMapConfigurationException extends is unsupported.
- */
- public function setExtends($value)
- {
- throw new TSqlMapConfigurationException('sqlmap_can_not_extend_select_key');
- }
-
- /**
- * @return boolean true if key is generated after insert command, false otherwise.
- */
- public function getIsAfter()
- {
- return $this->_type == 'post';
- }
-}
-
+<?php +/** + * TSqlMapStatement, TSqlMapInsert, TSqlMapUpdate, TSqlMapDelete, + * TSqlMapSelect and TSqlMapSelectKey classes file. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.SqlMap.Configuration + */ + +/** + * TSqlMapStatement class corresponds to <statement> element. + * + * Mapped Statements can hold any SQL statement and can use Parameter Maps + * and Result Maps for input and output. + * + * The <statement> element is a general "catch all" element that can be used + * for any type of SQL statement. Generally it is a good idea to use one of the + * more specific statement-type elements. The more specific elements provided + * better error-checking and even more functionality. (For example, the insert + * statement can return a database-generated key.) + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.SqlMap.Configuration + * @since 3.1 + */ +class TSqlMapStatement extends TComponent +{ + private $_parameterMapName; + private $_parameterMap; + private $_parameterClassName; + private $_resultMapName; + private $_resultMap; + private $_resultClassName; + private $_cacheModelName; + private $_SQL; + private $_listClass; + private $_typeHandler; + private $_extendStatement; + private $_cache; + private $_ID; + + /** + * @return string name for this statement, unique to each sql map manager. + */ + public function getID() + { + return $this->_ID; + } + + /** + * @param string name for this statement, which must be unique for each sql map manager. + */ + public function setID($value) + { + $this->_ID=$value; + } + + /** + * @return string name of a parameter map. + */ + public function getParameterMap() + { + return $this->_parameterMapName; + } + + /** + * A Parameter Map defines an ordered list of values that match up with + * the "?" placeholders of a standard, parameterized query statement. + * @param string parameter map name. + */ + public function setParameterMap($value) + { + $this->_parameterMapName = $value; + } + + /** + * @return string parameter class name. + */ + public function getParameterClass() + { + return $this->_parameterClassName; + } + + /** + * If a {@link ParameterMap setParameterMap()} property is not specified, + * you may specify a ParameterClass instead and use inline parameters. + * The value of the parameterClass attribute can be any existing PHP class name. + * @param string parameter class name. + */ + public function setParameterClass($value) + { + $this->_parameterClassName = $value; + } + + /** + * @return string result map name. + */ + public function getResultMap() + { + return $this->_resultMapName; + } + + /** + * A Result Map lets you control how data is extracted from the result of a + * query, and how the columns are mapped to object properties. + * @param string result map name. + */ + public function setResultMap($value) + { + $this->_resultMapName = $value; + } + + /** + * @return string result class name. + */ + public function getResultClass() + { + return $this->_resultClassName; + } + + /** + * If a {@link ResultMap setResultMap()} is not specified, you may specify a + * ResultClass instead. The value of the ResultClass property can be the + * name of a PHP class or primitives like integer, string, or array. The + * class specified will be automatically mapped to the columns in the + * result, based on the result metadata. + * @param string result class name. + */ + public function setResultClass($value) + { + $this->_resultClassName = $value; + } + + /** + * @return string cache mode name. + */ + public function getCacheModel() + { + return $this->_cacheModelName; + } + + /** + * @param string cache mode name. + */ + public function setCacheModel($value) + { + $this->_cacheModelName = $value; + } + + /** + * @return TSqlMapCacheModel cache implementation instance for this statement. + */ + public function getCache() + { + return $this->_cache; + } + + /** + * @param TSqlMapCacheModel cache implementation instance for this statement. + */ + public function setCache($value) + { + $this->_cache = $value; + } + + /** + * @return TStaticSql sql text container. + */ + public function getSqlText() + { + return $this->_SQL; + } + + /** + * @param TStaticSql sql text container. + */ + public function setSqlText($value) + { + $this->_SQL = $value; + } + + /** + * @return string name of a PHP class that implements ArrayAccess. + */ + public function getListClass() + { + return $this->_listClass; + } + + /** + * An ArrayAccess class can be specified to handle the type of objects in the collection. + * @param string name of a PHP class that implements ArrayAccess. + */ + public function setListClass($value) + { + $this->_listClass = $value; + } + + /** + * @return string another statement element name. + */ + public function getExtends() + { + return $this->_extendStatement; + } + + /** + * @param string name of another statement element to extend. + */ + public function setExtends($value) + { + $this->_extendStatement = $value; + } + + /** + * @return TResultMap the result map corresponding to the + * {@link ResultMap getResultMap()} property. + */ + public function resultMap() + { + return $this->_resultMap; + } + + /** + * @return TParameterMap the parameter map corresponding to the + * {@link ParameterMap getParameterMap()} property. + */ + public function parameterMap() + { + return $this->_parameterMap; + } + + /** + * @param TInlineParameterMap parameter extracted from the sql text. + */ + public function setInlineParameterMap($map) + { + $this->_parameterMap = $map; + } + + /** + * @param TSqlMapManager initialize the statement, sets the result and parameter maps. + */ + public function initialize($manager) + { + if(strlen($this->_resultMapName) > 0) + $this->_resultMap = $manager->getResultMap($this->_resultMapName); + if(strlen($this->_parameterMapName) > 0) + $this->_parameterMap = $manager->getParameterMap($this->_parameterMapName); + } + + /** + * @param TSqlMapTypeHandlerRegistry type handler registry + * @return ArrayAccess new instance of list class. + */ + public function createInstanceOfListClass($registry) + { + if(strlen($type = $this->getListClass()) > 0) + return $this->createInstanceOf($registry,$type); + return array(); + } + + /** + * Create a new instance of a given type. + * @param TSqlMapTypeHandlerRegistry type handler registry + * @param string result class name. + * @param array result data. + * @return mixed result object. + */ + protected function createInstanceOf($registry,$type,$row=null) + { + $handler = $registry->getTypeHandler($type); + if($handler!==null) + return $handler->createNewInstance($row); + else + return $registry->createInstanceOf($type); + } + + /** + * Create a new instance of result class. + * @param TSqlMapTypeHandlerRegistry type handler registry + * @param array result data. + * @return mixed result object. + */ + public function createInstanceOfResultClass($registry,$row) + { + if(strlen($type= $this->getResultClass()) > 0) + return $this->createInstanceOf($registry,$type,$row); + } + + public function __sleep() + { + $cn = __CLASS__; + $exprops = array("\0$cn\0_resultMap"); + if (!$this->_parameterMapName) $exprops[] = "\0$cn\0_parameterMapName"; + if (!$this->_parameterMap) $exprops[] = "\0$cn\0_parameterMap"; + if (!$this->_parameterClassName) $exprops[] = "\0$cn\0_parameterClassName"; + if (!$this->_resultMapName) $exprops[] = "\0$cn\0_resultMapName"; + if (!$this->_resultMap) $exprops[] = "\0$cn\0_resultMap"; + if (!$this->_resultClassName) $exprops[] = "\0$cn\0_resultClassName"; + if (!$this->_cacheModelName) $exprops[] = "\0$cn\0_cacheModelName"; + if (!$this->_SQL) $exprops[] = "\0$cn\0_SQL"; + if (!$this->_listClass) $exprops[] = "\0$cn\0_listClass"; + if (!$this->_typeHandler) $exprops[] = "\0$cn\0_typeHandler"; + if (!$this->_extendStatement) $exprops[] = "\0$cn\0_extendStatement"; + if (!$this->_cache) $exprops[] = "\0$cn\0_cache"; + + return array_diff(parent::__sleep(),$exprops); + } + +} + +/** + * TSqlMapSelect class file. + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.SqlMap.Statements + * @since 3.1 + */ +class TSqlMapSelect extends TSqlMapStatement +{ + private $_generate; + + public function getGenerate(){ return $this->_generate; } + public function setGenerate($value){ $this->_generate = $value; } +} + +/** + * TSqlMapInsert class corresponds to the <insert> element. + * + * The <insert> element allows <selectKey> child elements that can be used + * to generate a key to be used for the insert command. + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.SqlMap.Configuration + * @since 3.1 + */ +class TSqlMapInsert extends TSqlMapStatement +{ + private $_selectKey=null; + + /** + * @return TSqlMapSelectKey select key element. + */ + public function getSelectKey() + { + return $this->_selectKey; + } + + /** + * @param TSqlMapSelectKey select key. + */ + public function setSelectKey($value) + { + $this->_selectKey = $value; + } +} + +/** + * TSqlMapUpdate class corresponds to <update> element. + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.SqlMap.Configuration + * @since 3.1 + */ +class TSqlMapUpdate extends TSqlMapStatement +{ +} + +/** + * TSqlMapDelete class corresponds to the <delete> element. + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.SqlMap.Configuration + * @since 3.1 + */ +class TSqlMapDelete extends TSqlMapUpdate +{ +} + +/** + * TSqlMapSelect corresponds to the <selectKey> element. + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.SqlMap.Configuration + * @since 3.1 + */ +class TSqlMapSelectKey extends TSqlMapStatement +{ + private $_type = 'post'; + private $_property; + + /** + * @return string select generated key type, 'post' or 'pre'. + */ + public function getType() + { + return $this->_type; + } + + /** + * @param string select generated key type, 'post' or 'pre'. + */ + public function setType($value) + { + $this->_type = strtolower($value) == 'post' ? 'post' : 'pre'; + } + + /** + * @return string property name for the generated key. + */ + public function getProperty() + { + return $this->_property; + } + + /** + * @param string property name for the generated key. + */ + public function setProperty($value) + { + $this->_property = $value; + } + + /** + * @throws TSqlMapConfigurationException extends is unsupported. + */ + public function setExtends($value) + { + throw new TSqlMapConfigurationException('sqlmap_can_not_extend_select_key'); + } + + /** + * @return boolean true if key is generated after insert command, false otherwise. + */ + public function getIsAfter() + { + return $this->_type == 'post'; + } +} + diff --git a/framework/Data/SqlMap/Configuration/TSqlMapXmlConfiguration.php b/framework/Data/SqlMap/Configuration/TSqlMapXmlConfiguration.php index a60827fe..988d00db 100644 --- a/framework/Data/SqlMap/Configuration/TSqlMapXmlConfiguration.php +++ b/framework/Data/SqlMap/Configuration/TSqlMapXmlConfiguration.php @@ -1,805 +1,805 @@ -<?php
-/**
- * TSqlMapXmlConfigBuilder, TSqlMapXmlConfiguration, TSqlMapXmlMappingConfiguration classes file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.SqlMap.Configuration
- */
-
-Prado::using('System.Data.SqlMap.Configuration.TSqlMapStatement');
-
-/**
- * TSqlMapXmlConfig class file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.SqlMap.Configuration
- */
-abstract class TSqlMapXmlConfigBuilder
-{
- /**
- * Create an instance of an object give by the attribute named 'class' in the
- * node and set the properties on the object given by attribute names and values.
- * @param SimpleXmlNode property node
- * @return Object new instance of class with class name given by 'class' attribute value.
- */
- protected function createObjectFromNode($node)
- {
- if(isset($node['class']))
- {
- $obj = Prado::createComponent((string)$node['class']);
- $this->setObjectPropFromNode($obj,$node,array('class'));
- return $obj;
- }
- throw new TSqlMapConfigurationException(
- 'sqlmap_node_class_undef', $node, $this->getConfigFile());
- }
-
- /**
- * For each attributes (excluding attribute named in $except) set the
- * property of the $obj given by the name of the attribute with the value
- * of the attribute.
- * @param Object object instance
- * @param SimpleXmlNode property node
- * @param array exception property name
- */
- protected function setObjectPropFromNode($obj,$node,$except=array())
- {
- foreach($node->attributes() as $name=>$value)
- {
- if(!in_array($name,$except))
- {
- if($obj->canSetProperty($name))
- $obj->{$name} = (string)$value;
- else
- throw new TSqlMapConfigurationException(
- 'sqlmap_invalid_property', $name, get_class($obj),
- $node, $this->getConfigFile());
- }
- }
- }
-
- /**
- * Gets the filename relative to the basefile.
- * @param string base filename
- * @param string relative filename
- * @return string absolute filename.
- */
- protected function getAbsoluteFilePath($basefile,$resource)
- {
- $basedir = dirname($basefile);
- $file = realpath($basedir.DIRECTORY_SEPARATOR.$resource);
- if(!is_string($file) || !is_file($file))
- $file = realpath($resource);
- if(is_string($file) && is_file($file))
- return $file;
- else
- throw new TSqlMapConfigurationException(
- 'sqlmap_unable_to_find_resource', $resource);
- }
-
- /**
- * Load document using simple xml.
- * @param string filename.
- * @return SimpleXmlElement xml document.
- */
- protected function loadXmlDocument($filename,TSqlMapXmlConfiguration $config)
- {
- if( strpos($filename, '${') !== false)
- $filename = $config->replaceProperties($filename);
-
- if(!is_file($filename))
- throw new TSqlMapConfigurationException(
- 'sqlmap_unable_to_find_config', $filename);
- return simplexml_load_string($config->replaceProperties(file_get_contents($filename)));
- }
-
- /**
- * Get element node by ID value (try for attribute name ID as case insensitive).
- * @param SimpleXmlDocument $document
- * @param string tag name.
- * @param string id value.
- * @return SimpleXmlElement node if found, null otherwise.
- */
- protected function getElementByIdValue($document, $tag, $value)
- {
- //hack to allow upper case and lower case attribute names.
- foreach(array('id','ID','Id', 'iD') as $id)
- {
- $xpath = "//{$tag}[@{$id}='{$value}']";
- foreach($document->xpath($xpath) as $node)
- return $node;
- }
- }
-
- /**
- * @return string configuration file.
- */
- protected abstract function getConfigFile();
-}
-
-/**
- * TSqlMapXmlConfig class.
- *
- * Configures the TSqlMapManager using xml configuration file.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.SqlMap.Configuration
- * @since 3.1
- */
-class TSqlMapXmlConfiguration extends TSqlMapXmlConfigBuilder
-{
- /**
- * @var TSqlMapManager manager
- */
- private $_manager;
- /**
- * @var string configuration file.
- */
- private $_configFile;
- /**
- * @var array global properties.
- */
- private $_properties=array();
-
- /**
- * @param TSqlMapManager manager instance.
- */
- public function __construct($manager)
- {
- $this->_manager=$manager;
- }
-
- public function getManager()
- {
- return $this->_manager;
- }
-
- protected function getConfigFile()
- {
- return $this->_configFile;
- }
-
- /**
- * Configure the TSqlMapManager using the given xml file.
- * @param string SqlMap configuration xml file.
- */
- public function configure($filename=null)
- {
- $this->_configFile=$filename;
- $document = $this->loadXmlDocument($filename,$this);
-
- foreach($document->xpath('//property') as $property)
- $this->loadGlobalProperty($property);
-
- foreach($document->xpath('//typeHandler') as $handler)
- $this->loadTypeHandler($handler);
-
- foreach($document->xpath('//connection[last()]') as $conn)
- $this->loadDatabaseConnection($conn);
-
- //try to load configuration in the current config file.
- $mapping = new TSqlMapXmlMappingConfiguration($this);
- $mapping->configure($filename);
-
- foreach($document->xpath('//sqlMap') as $sqlmap)
- $this->loadSqlMappingFiles($sqlmap);
-
- $this->resolveResultMapping();
- $this->attachCacheModels();
- }
-
- /**
- * Load global replacement property.
- * @param SimpleXmlElement property node.
- */
- protected function loadGlobalProperty($node)
- {
- $this->_properties[(string)$node['name']] = (string)$node['value'];
- }
-
- /**
- * Load the type handler configurations.
- * @param SimpleXmlElement type handler node
- */
- protected function loadTypeHandler($node)
- {
- $handler = $this->createObjectFromNode($node);
- $this->_manager->getTypeHandlers()->registerTypeHandler($handler);
- }
-
- /**
- * Load the database connection tag.
- * @param SimpleXmlElement connection node.
- */
- protected function loadDatabaseConnection($node)
- {
- $conn = $this->createObjectFromNode($node);
- $this->_manager->setDbConnection($conn);
- }
-
- /**
- * Load SqlMap mapping configuration.
- * @param unknown_type $node
- */
- protected function loadSqlMappingFiles($node)
- {
- if(strlen($resource = (string)$node['resource']) > 0)
- {
- if( strpos($resource, '${') !== false)
- $resource = $this->replaceProperties($resource);
-
- $mapping = new TSqlMapXmlMappingConfiguration($this);
- $filename = $this->getAbsoluteFilePath($this->_configFile, $resource);
- $mapping->configure($filename);
- }
- }
-
- /**
- * Resolve nest result mappings.
- */
- protected function resolveResultMapping()
- {
- $maps = $this->_manager->getResultMaps();
- foreach($maps as $entry)
- {
- foreach($entry->getColumns() as $item)
- {
- $resultMap = $item->getResultMapping();
- if(strlen($resultMap) > 0)
- {
- if($maps->contains($resultMap))
- $item->setNestedResultMap($maps[$resultMap]);
- else
- throw new TSqlMapConfigurationException(
- 'sqlmap_unable_to_find_result_mapping',
- $resultMap, $this->_configFile, $entry->getID());
- }
- }
- if($entry->getDiscriminator()!==null)
- $entry->getDiscriminator()->initialize($this->_manager);
- }
- }
-
- /**
- * Set the cache for each statement having a cache model property.
- */
- protected function attachCacheModels()
- {
- foreach($this->_manager->getMappedStatements() as $mappedStatement)
- {
- if(strlen($model = $mappedStatement->getStatement()->getCacheModel()) > 0)
- {
- $cache = $this->_manager->getCacheModel($model);
- $mappedStatement->getStatement()->setCache($cache);
- }
- }
- }
-
- /**
- * Replace the place holders ${name} in text with properties the
- * corresponding global property value.
- * @param string original string.
- * @return string string with global property replacement.
- */
- public function replaceProperties($string)
- {
- foreach($this->_properties as $find => $replace)
- $string = str_replace('${'.$find.'}', $replace, $string);
- return $string;
- }
-}
-
-/**
- * Loads the statements, result maps, parameters maps from xml configuration.
- *
- * description
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.SqlMap.Configuration
- * @since 3.1
- */
-class TSqlMapXmlMappingConfiguration extends TSqlMapXmlConfigBuilder
-{
- private $_xmlConfig;
- private $_configFile;
- private $_manager;
-
- private $_document;
-
- private $_FlushOnExecuteStatements=array();
-
- /**
- * Regular expressions for escaping simple/inline parameter symbols
- */
- const SIMPLE_MARK='$';
- const INLINE_SYMBOL='#';
- const ESCAPED_SIMPLE_MARK_REGEXP='/\$\$/';
- const ESCAPED_INLINE_SYMBOL_REGEXP='/\#\#/';
- const SIMPLE_PLACEHOLDER='`!!`';
- const INLINE_PLACEHOLDER='`!!!`';
-
- /**
- * @param TSqlMapXmlConfiguration parent xml configuration.
- */
- public function __construct(TSqlMapXmlConfiguration $xmlConfig)
- {
- $this->_xmlConfig=$xmlConfig;
- $this->_manager=$xmlConfig->getManager();
- }
-
- protected function getConfigFile()
- {
- return $this->_configFile;
- }
-
- /**
- * Configure an XML mapping.
- * @param string xml mapping filename.
- */
- public function configure($filename)
- {
- $this->_configFile=$filename;
- $document = $this->loadXmlDocument($filename,$this->_xmlConfig);
- $this->_document=$document;
-
- static $bCacheDependencies;
- if($bCacheDependencies === null)
- $bCacheDependencies = Prado::getApplication()->getMode() !== TApplicationMode::Performance;
-
- if($bCacheDependencies)
- $this->_manager->getCacheDependencies()
- ->getDependencies()
- ->add(new TFileCacheDependency($filename));
-
- foreach($document->xpath('//resultMap') as $node)
- $this->loadResultMap($node);
-
- foreach($document->xpath('//parameterMap') as $node)
- $this->loadParameterMap($node);
-
- foreach($document->xpath('//statement') as $node)
- $this->loadStatementTag($node);
-
- foreach($document->xpath('//select') as $node)
- $this->loadSelectTag($node);
-
- foreach($document->xpath('//insert') as $node)
- $this->loadInsertTag($node);
-
- foreach($document->xpath('//update') as $node)
- $this->loadUpdateTag($node);
-
- foreach($document->xpath('//delete') as $node)
- $this->loadDeleteTag($node);
-
- foreach($document->xpath('//procedure') as $node)
- $this->loadProcedureTag($node);
-
- foreach($document->xpath('//cacheModel') as $node)
- $this->loadCacheModel($node);
-
- $this->registerCacheTriggers();
- }
-
- /**
- * Load the result maps.
- * @param SimpleXmlElement result map node.
- */
- protected function loadResultMap($node)
- {
- $resultMap = $this->createResultMap($node);
-
- //find extended result map.
- if(strlen($extendMap = $resultMap->getExtends()) > 0)
- {
- if(!$this->_manager->getResultMaps()->contains($extendMap))
- {
- $extendNode=$this->getElementByIdValue($this->_document,'resultMap',$extendMap);
- if($extendNode!==null)
- $this->loadResultMap($extendNode);
- }
-
- if(!$this->_manager->getResultMaps()->contains($extendMap))
- throw new TSqlMapConfigurationException(
- 'sqlmap_unable_to_find_parent_result_map', $node, $this->_configFile, $extendMap);
-
- $superMap = $this->_manager->getResultMap($extendMap);
- $resultMap->getColumns()->mergeWith($superMap->getColumns());
- }
-
- //add the result map
- if(!$this->_manager->getResultMaps()->contains($resultMap->getID()))
- $this->_manager->addResultMap($resultMap);
- }
-
- /**
- * Create a new result map and its associated result properties,
- * disciminiator and sub maps.
- * @param SimpleXmlElement result map node
- * @return TResultMap SqlMap result mapping.
- */
- protected function createResultMap($node)
- {
- $resultMap = new TResultMap();
- $this->setObjectPropFromNode($resultMap,$node);
-
- //result nodes
- foreach($node->result as $result)
- {
- $property = new TResultProperty($resultMap);
- $this->setObjectPropFromNode($property,$result);
- $resultMap->addResultProperty($property);
- }
-
- //create the discriminator
- $discriminator = null;
- if(isset($node->discriminator))
- {
- $discriminator = new TDiscriminator();
- $this->setObjectPropFromNode($discriminator, $node->discriminator);
- $discriminator->initMapping($resultMap);
- }
-
- foreach($node->xpath('subMap') as $subMapNode)
- {
- if($discriminator===null)
- throw new TSqlMapConfigurationException(
- 'sqlmap_undefined_discriminator', $node, $this->_configFile,$subMapNode);
- $subMap = new TSubMap;
- $this->setObjectPropFromNode($subMap,$subMapNode);
- $discriminator->addSubMap($subMap);
- }
-
- if($discriminator!==null)
- $resultMap->setDiscriminator($discriminator);
-
- return $resultMap;
- }
-
- /**
- * Load parameter map from xml.
- *
- * @param SimpleXmlElement parameter map node.
- */
- protected function loadParameterMap($node)
- {
- $parameterMap = $this->createParameterMap($node);
-
- if(strlen($extendMap = $parameterMap->getExtends()) > 0)
- {
- if(!$this->_manager->getParameterMaps()->contains($extendMap))
- {
- $extendNode=$this->getElementByIdValue($this->_document,'parameterMap',$extendMap);
- if($extendNode!==null)
- $this->loadParameterMap($extendNode);
- }
-
- if(!$this->_manager->getParameterMaps()->contains($extendMap))
- throw new TSqlMapConfigurationException(
- 'sqlmap_unable_to_find_parent_parameter_map', $node, $this->_configFile,$extendMap);
- $superMap = $this->_manager->getParameterMap($extendMap);
- $index = 0;
- foreach($superMap->getPropertyNames() as $propertyName)
- $parameterMap->insertProperty($index++,$superMap->getProperty($propertyName));
- }
- $this->_manager->addParameterMap($parameterMap);
- }
-
- /**
- * Create a new parameter map from xml node.
- * @param SimpleXmlElement parameter map node.
- * @return TParameterMap new parameter mapping.
- */
- protected function createParameterMap($node)
- {
- $parameterMap = new TParameterMap();
- $this->setObjectPropFromNode($parameterMap,$node);
- foreach($node->parameter as $parameter)
- {
- $property = new TParameterProperty();
- $this->setObjectPropFromNode($property,$parameter);
- $parameterMap->addProperty($property);
- }
- return $parameterMap;
- }
-
- /**
- * Load statement mapping from xml configuration file.
- * @param SimpleXmlElement statement node.
- */
- protected function loadStatementTag($node)
- {
- $statement = new TSqlMapStatement();
- $this->setObjectPropFromNode($statement,$node);
- $this->processSqlStatement($statement, $node);
- $mappedStatement = new TMappedStatement($this->_manager, $statement);
- $this->_manager->addMappedStatement($mappedStatement);
- }
-
- /**
- * Load extended SQL statements if application. Replaces global properties
- * in the sql text. Extracts inline parameter maps.
- * @param TSqlMapStatement mapped statement.
- * @param SimpleXmlElement statement node.
- */
- protected function processSqlStatement($statement, $node)
- {
- $commandText = (string)$node;
- if(strlen($extend = $statement->getExtends()) > 0)
- {
- $superNode = $this->getElementByIdValue($this->_document,'*',$extend);
- if($superNode!==null)
- $commandText = (string)$superNode . $commandText;
- else
- throw new TSqlMapConfigurationException(
- 'sqlmap_unable_to_find_parent_sql', $extend, $this->_configFile,$node);
- }
- //$commandText = $this->_xmlConfig->replaceProperties($commandText);
- $statement->initialize($this->_manager);
- $this->applyInlineParameterMap($statement, $commandText, $node);
- }
-
- /**
- * Extract inline parameter maps.
- * @param TSqlMapStatement statement object.
- * @param string sql text
- * @param SimpleXmlElement statement node.
- */
- protected function applyInlineParameterMap($statement, $sqlStatement, $node)
- {
- $scope['file'] = $this->_configFile;
- $scope['node'] = $node;
-
- $sqlStatement=preg_replace(self::ESCAPED_INLINE_SYMBOL_REGEXP,self::INLINE_PLACEHOLDER,$sqlStatement);
- if($statement->parameterMap() === null)
- {
- // Build a Parametermap with the inline parameters.
- // if they exist. Then delete inline infos from sqltext.
- $parameterParser = new TInlineParameterMapParser;
- $sqlText = $parameterParser->parse($sqlStatement, $scope);
- if(count($sqlText['parameters']) > 0)
- {
- $map = new TParameterMap();
- $map->setID($statement->getID().'-InLineParameterMap');
- $statement->setInlineParameterMap($map);
- foreach($sqlText['parameters'] as $property)
- $map->addProperty($property);
- }
- $sqlStatement = $sqlText['sql'];
- }
- $sqlStatement=preg_replace('/'.self::INLINE_PLACEHOLDER.'/',self::INLINE_SYMBOL,$sqlStatement);
-
- $this->prepareSql($statement, $sqlStatement, $node);
- }
-
- /**
- * Prepare the sql text (may extend to dynamic sql).
- * @param TSqlMapStatement mapped statement.
- * @param string sql text.
- * @param SimpleXmlElement statement node.
- * @todo Extend to dynamic sql.
- */
- protected function prepareSql($statement,$sqlStatement, $node)
- {
- $simpleDynamic = new TSimpleDynamicParser;
- $sqlStatement=preg_replace(self::ESCAPED_SIMPLE_MARK_REGEXP,self::SIMPLE_PLACEHOLDER,$sqlStatement);
- $dynamics = $simpleDynamic->parse($sqlStatement);
- if(count($dynamics['parameters']) > 0)
- {
- $sql = new TSimpleDynamicSql($dynamics['parameters']);
- $sqlStatement = $dynamics['sql'];
- }
- else
- $sql = new TStaticSql();
- $sqlStatement=preg_replace('/'.self::SIMPLE_PLACEHOLDER.'/',self::SIMPLE_MARK,$sqlStatement);
- $sql->buildPreparedStatement($statement, $sqlStatement);
- $statement->setSqlText($sql);
- }
-
- /**
- * Load select statement from xml mapping.
- * @param SimpleXmlElement select node.
- */
- protected function loadSelectTag($node)
- {
- $select = new TSqlMapSelect;
- $this->setObjectPropFromNode($select,$node);
- $this->processSqlStatement($select,$node);
- $mappedStatement = new TMappedStatement($this->_manager, $select);
- if(strlen($select->getCacheModel()) > 0)
- $mappedStatement = new TCachingStatement($mappedStatement);
-
- $this->_manager->addMappedStatement($mappedStatement);
- }
-
- /**
- * Load insert statement from xml mapping.
- * @param SimpleXmlElement insert node.
- */
- protected function loadInsertTag($node)
- {
- $insert = $this->createInsertStatement($node);
- $this->processSqlStatement($insert, $node);
- $mappedStatement = new TInsertMappedStatement($this->_manager, $insert);
- $this->_manager->addMappedStatement($mappedStatement);
- }
-
- /**
- * Create new insert statement from xml node.
- * @param SimpleXmlElement insert node.
- * @return TSqlMapInsert insert statement.
- */
- protected function createInsertStatement($node)
- {
- $insert = new TSqlMapInsert;
- $this->setObjectPropFromNode($insert,$node);
- if(isset($node->selectKey))
- $this->loadSelectKeyTag($insert,$node->selectKey);
- return $insert;
- }
-
- /**
- * Load the selectKey statement from xml mapping.
- * @param SimpleXmlElement selectkey node
- */
- protected function loadSelectKeyTag($insert, $node)
- {
- $selectKey = new TSqlMapSelectKey;
- $this->setObjectPropFromNode($selectKey,$node);
- $selectKey->setID($insert->getID());
- $selectKey->setID($insert->getID().'.SelectKey');
- $this->processSqlStatement($selectKey,$node);
- $insert->setSelectKey($selectKey);
- $mappedStatement = new TMappedStatement($this->_manager, $selectKey);
- $this->_manager->addMappedStatement($mappedStatement);
- }
-
- /**
- * Load update statement from xml mapping.
- * @param SimpleXmlElement update node.
- */
- protected function loadUpdateTag($node)
- {
- $update = new TSqlMapUpdate;
- $this->setObjectPropFromNode($update,$node);
- $this->processSqlStatement($update, $node);
- $mappedStatement = new TUpdateMappedStatement($this->_manager, $update);
- $this->_manager->addMappedStatement($mappedStatement);
- }
-
- /**
- * Load delete statement from xml mapping.
- * @param SimpleXmlElement delete node.
- */
- protected function loadDeleteTag($node)
- {
- $delete = new TSqlMapDelete;
- $this->setObjectPropFromNode($delete,$node);
- $this->processSqlStatement($delete, $node);
- $mappedStatement = new TDeleteMappedStatement($this->_manager, $delete);
- $this->_manager->addMappedStatement($mappedStatement);
- }
-
- /**
- * Load procedure statement from xml mapping.
- * @todo Implement loading procedure
- * @param SimpleXmlElement procedure node
- */
- protected function loadProcedureTag($node)
- {
- //var_dump('todo: add load procedure');
- }
-
- /**
- * Load cache models from xml mapping.
- * @param SimpleXmlElement cache node.
- */
- protected function loadCacheModel($node)
- {
- $cacheModel = new TSqlMapCacheModel;
- $properties = array('id','implementation');
- foreach($node->attributes() as $name=>$value)
- {
- if(in_array(strtolower($name), $properties))
- $cacheModel->{'set'.$name}((string)$value);
- }
- $cache = Prado::createComponent($cacheModel->getImplementationClass(), $cacheModel);
- $this->setObjectPropFromNode($cache,$node,$properties);
-
- foreach($node->xpath('property') as $propertyNode)
- {
- $name = $propertyNode->attributes()->name;
- if($name===null || $name==='') continue;
-
- $value = $propertyNode->attributes()->value;
- if($value===null || $value==='') continue;
-
- if( !TPropertyAccess::has($cache, $name) ) continue;
-
- TPropertyAccess::set($cache, $name, $value);
- }
-
- $this->loadFlushInterval($cacheModel,$node);
-
- $cacheModel->initialize($cache);
- $this->_manager->addCacheModel($cacheModel);
- foreach($node->xpath('flushOnExecute') as $flush)
- $this->loadFlushOnCache($cacheModel,$node,$flush);
- }
-
- /**
- * Load the flush interval
- * @param TSqlMapCacheModel cache model
- * @param SimpleXmlElement cache node
- */
- protected function loadFlushInterval($cacheModel, $node)
- {
- $flushInterval = $node->xpath('flushInterval');
- if($flushInterval === null || count($flushInterval) === 0) return;
- $duration = 0;
- foreach($flushInterval[0]->attributes() as $name=>$value)
- {
- switch(strToLower($name))
- {
- case 'seconds':
- $duration += (integer)$value;
- break;
- case 'minutes':
- $duration += 60 * (integer)$value;
- break;
- case 'hours':
- $duration += 3600 * (integer)$value;
- break;
- case 'days':
- $duration += 86400 * (integer)$value;
- break;
- case 'duration':
- $duration = (integer)$value;
- break 2; // switch, foreach
- }
- }
- $cacheModel->setFlushInterval($duration);
- }
-
- /**
- * Load the flush on cache properties.
- * @param TSqlMapCacheModel cache model
- * @param SimpleXmlElement parent node.
- * @param SimpleXmlElement flush node.
- */
- protected function loadFlushOnCache($cacheModel,$parent,$node)
- {
- $id = $cacheModel->getID();
- if(!isset($this->_FlushOnExecuteStatements[$id]))
- $this->_FlushOnExecuteStatements[$id] = array();
- foreach($node->attributes() as $name=>$value)
- {
- if(strtolower($name)==='statement')
- $this->_FlushOnExecuteStatements[$id][] = (string)$value;
- }
- }
-
- /**
- * Attach CacheModel to statement and register trigger statements for cache models
- */
- protected function registerCacheTriggers()
- {
- foreach($this->_FlushOnExecuteStatements as $cacheID => $statementIDs)
- {
- $cacheModel = $this->_manager->getCacheModel($cacheID);
- foreach($statementIDs as $statementID)
- {
- $statement = $this->_manager->getMappedStatement($statementID);
- $cacheModel->registerTriggerStatement($statement);
- }
- }
- }
-}
-
+<?php +/** + * TSqlMapXmlConfigBuilder, TSqlMapXmlConfiguration, TSqlMapXmlMappingConfiguration classes file. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.SqlMap.Configuration + */ + +Prado::using('System.Data.SqlMap.Configuration.TSqlMapStatement'); + +/** + * TSqlMapXmlConfig class file. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.SqlMap.Configuration + */ +abstract class TSqlMapXmlConfigBuilder +{ + /** + * Create an instance of an object give by the attribute named 'class' in the + * node and set the properties on the object given by attribute names and values. + * @param SimpleXmlNode property node + * @return Object new instance of class with class name given by 'class' attribute value. + */ + protected function createObjectFromNode($node) + { + if(isset($node['class'])) + { + $obj = Prado::createComponent((string)$node['class']); + $this->setObjectPropFromNode($obj,$node,array('class')); + return $obj; + } + throw new TSqlMapConfigurationException( + 'sqlmap_node_class_undef', $node, $this->getConfigFile()); + } + + /** + * For each attributes (excluding attribute named in $except) set the + * property of the $obj given by the name of the attribute with the value + * of the attribute. + * @param Object object instance + * @param SimpleXmlNode property node + * @param array exception property name + */ + protected function setObjectPropFromNode($obj,$node,$except=array()) + { + foreach($node->attributes() as $name=>$value) + { + if(!in_array($name,$except)) + { + if($obj->canSetProperty($name)) + $obj->{$name} = (string)$value; + else + throw new TSqlMapConfigurationException( + 'sqlmap_invalid_property', $name, get_class($obj), + $node, $this->getConfigFile()); + } + } + } + + /** + * Gets the filename relative to the basefile. + * @param string base filename + * @param string relative filename + * @return string absolute filename. + */ + protected function getAbsoluteFilePath($basefile,$resource) + { + $basedir = dirname($basefile); + $file = realpath($basedir.DIRECTORY_SEPARATOR.$resource); + if(!is_string($file) || !is_file($file)) + $file = realpath($resource); + if(is_string($file) && is_file($file)) + return $file; + else + throw new TSqlMapConfigurationException( + 'sqlmap_unable_to_find_resource', $resource); + } + + /** + * Load document using simple xml. + * @param string filename. + * @return SimpleXmlElement xml document. + */ + protected function loadXmlDocument($filename,TSqlMapXmlConfiguration $config) + { + if( strpos($filename, '${') !== false) + $filename = $config->replaceProperties($filename); + + if(!is_file($filename)) + throw new TSqlMapConfigurationException( + 'sqlmap_unable_to_find_config', $filename); + return simplexml_load_string($config->replaceProperties(file_get_contents($filename))); + } + + /** + * Get element node by ID value (try for attribute name ID as case insensitive). + * @param SimpleXmlDocument $document + * @param string tag name. + * @param string id value. + * @return SimpleXmlElement node if found, null otherwise. + */ + protected function getElementByIdValue($document, $tag, $value) + { + //hack to allow upper case and lower case attribute names. + foreach(array('id','ID','Id', 'iD') as $id) + { + $xpath = "//{$tag}[@{$id}='{$value}']"; + foreach($document->xpath($xpath) as $node) + return $node; + } + } + + /** + * @return string configuration file. + */ + protected abstract function getConfigFile(); +} + +/** + * TSqlMapXmlConfig class. + * + * Configures the TSqlMapManager using xml configuration file. + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.SqlMap.Configuration + * @since 3.1 + */ +class TSqlMapXmlConfiguration extends TSqlMapXmlConfigBuilder +{ + /** + * @var TSqlMapManager manager + */ + private $_manager; + /** + * @var string configuration file. + */ + private $_configFile; + /** + * @var array global properties. + */ + private $_properties=array(); + + /** + * @param TSqlMapManager manager instance. + */ + public function __construct($manager) + { + $this->_manager=$manager; + } + + public function getManager() + { + return $this->_manager; + } + + protected function getConfigFile() + { + return $this->_configFile; + } + + /** + * Configure the TSqlMapManager using the given xml file. + * @param string SqlMap configuration xml file. + */ + public function configure($filename=null) + { + $this->_configFile=$filename; + $document = $this->loadXmlDocument($filename,$this); + + foreach($document->xpath('//property') as $property) + $this->loadGlobalProperty($property); + + foreach($document->xpath('//typeHandler') as $handler) + $this->loadTypeHandler($handler); + + foreach($document->xpath('//connection[last()]') as $conn) + $this->loadDatabaseConnection($conn); + + //try to load configuration in the current config file. + $mapping = new TSqlMapXmlMappingConfiguration($this); + $mapping->configure($filename); + + foreach($document->xpath('//sqlMap') as $sqlmap) + $this->loadSqlMappingFiles($sqlmap); + + $this->resolveResultMapping(); + $this->attachCacheModels(); + } + + /** + * Load global replacement property. + * @param SimpleXmlElement property node. + */ + protected function loadGlobalProperty($node) + { + $this->_properties[(string)$node['name']] = (string)$node['value']; + } + + /** + * Load the type handler configurations. + * @param SimpleXmlElement type handler node + */ + protected function loadTypeHandler($node) + { + $handler = $this->createObjectFromNode($node); + $this->_manager->getTypeHandlers()->registerTypeHandler($handler); + } + + /** + * Load the database connection tag. + * @param SimpleXmlElement connection node. + */ + protected function loadDatabaseConnection($node) + { + $conn = $this->createObjectFromNode($node); + $this->_manager->setDbConnection($conn); + } + + /** + * Load SqlMap mapping configuration. + * @param unknown_type $node + */ + protected function loadSqlMappingFiles($node) + { + if(strlen($resource = (string)$node['resource']) > 0) + { + if( strpos($resource, '${') !== false) + $resource = $this->replaceProperties($resource); + + $mapping = new TSqlMapXmlMappingConfiguration($this); + $filename = $this->getAbsoluteFilePath($this->_configFile, $resource); + $mapping->configure($filename); + } + } + + /** + * Resolve nest result mappings. + */ + protected function resolveResultMapping() + { + $maps = $this->_manager->getResultMaps(); + foreach($maps as $entry) + { + foreach($entry->getColumns() as $item) + { + $resultMap = $item->getResultMapping(); + if(strlen($resultMap) > 0) + { + if($maps->contains($resultMap)) + $item->setNestedResultMap($maps[$resultMap]); + else + throw new TSqlMapConfigurationException( + 'sqlmap_unable_to_find_result_mapping', + $resultMap, $this->_configFile, $entry->getID()); + } + } + if($entry->getDiscriminator()!==null) + $entry->getDiscriminator()->initialize($this->_manager); + } + } + + /** + * Set the cache for each statement having a cache model property. + */ + protected function attachCacheModels() + { + foreach($this->_manager->getMappedStatements() as $mappedStatement) + { + if(strlen($model = $mappedStatement->getStatement()->getCacheModel()) > 0) + { + $cache = $this->_manager->getCacheModel($model); + $mappedStatement->getStatement()->setCache($cache); + } + } + } + + /** + * Replace the place holders ${name} in text with properties the + * corresponding global property value. + * @param string original string. + * @return string string with global property replacement. + */ + public function replaceProperties($string) + { + foreach($this->_properties as $find => $replace) + $string = str_replace('${'.$find.'}', $replace, $string); + return $string; + } +} + +/** + * Loads the statements, result maps, parameters maps from xml configuration. + * + * description + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.SqlMap.Configuration + * @since 3.1 + */ +class TSqlMapXmlMappingConfiguration extends TSqlMapXmlConfigBuilder +{ + private $_xmlConfig; + private $_configFile; + private $_manager; + + private $_document; + + private $_FlushOnExecuteStatements=array(); + + /** + * Regular expressions for escaping simple/inline parameter symbols + */ + const SIMPLE_MARK='$'; + const INLINE_SYMBOL='#'; + const ESCAPED_SIMPLE_MARK_REGEXP='/\$\$/'; + const ESCAPED_INLINE_SYMBOL_REGEXP='/\#\#/'; + const SIMPLE_PLACEHOLDER='`!!`'; + const INLINE_PLACEHOLDER='`!!!`'; + + /** + * @param TSqlMapXmlConfiguration parent xml configuration. + */ + public function __construct(TSqlMapXmlConfiguration $xmlConfig) + { + $this->_xmlConfig=$xmlConfig; + $this->_manager=$xmlConfig->getManager(); + } + + protected function getConfigFile() + { + return $this->_configFile; + } + + /** + * Configure an XML mapping. + * @param string xml mapping filename. + */ + public function configure($filename) + { + $this->_configFile=$filename; + $document = $this->loadXmlDocument($filename,$this->_xmlConfig); + $this->_document=$document; + + static $bCacheDependencies; + if($bCacheDependencies === null) + $bCacheDependencies = Prado::getApplication()->getMode() !== TApplicationMode::Performance; + + if($bCacheDependencies) + $this->_manager->getCacheDependencies() + ->getDependencies() + ->add(new TFileCacheDependency($filename)); + + foreach($document->xpath('//resultMap') as $node) + $this->loadResultMap($node); + + foreach($document->xpath('//parameterMap') as $node) + $this->loadParameterMap($node); + + foreach($document->xpath('//statement') as $node) + $this->loadStatementTag($node); + + foreach($document->xpath('//select') as $node) + $this->loadSelectTag($node); + + foreach($document->xpath('//insert') as $node) + $this->loadInsertTag($node); + + foreach($document->xpath('//update') as $node) + $this->loadUpdateTag($node); + + foreach($document->xpath('//delete') as $node) + $this->loadDeleteTag($node); + + foreach($document->xpath('//procedure') as $node) + $this->loadProcedureTag($node); + + foreach($document->xpath('//cacheModel') as $node) + $this->loadCacheModel($node); + + $this->registerCacheTriggers(); + } + + /** + * Load the result maps. + * @param SimpleXmlElement result map node. + */ + protected function loadResultMap($node) + { + $resultMap = $this->createResultMap($node); + + //find extended result map. + if(strlen($extendMap = $resultMap->getExtends()) > 0) + { + if(!$this->_manager->getResultMaps()->contains($extendMap)) + { + $extendNode=$this->getElementByIdValue($this->_document,'resultMap',$extendMap); + if($extendNode!==null) + $this->loadResultMap($extendNode); + } + + if(!$this->_manager->getResultMaps()->contains($extendMap)) + throw new TSqlMapConfigurationException( + 'sqlmap_unable_to_find_parent_result_map', $node, $this->_configFile, $extendMap); + + $superMap = $this->_manager->getResultMap($extendMap); + $resultMap->getColumns()->mergeWith($superMap->getColumns()); + } + + //add the result map + if(!$this->_manager->getResultMaps()->contains($resultMap->getID())) + $this->_manager->addResultMap($resultMap); + } + + /** + * Create a new result map and its associated result properties, + * disciminiator and sub maps. + * @param SimpleXmlElement result map node + * @return TResultMap SqlMap result mapping. + */ + protected function createResultMap($node) + { + $resultMap = new TResultMap(); + $this->setObjectPropFromNode($resultMap,$node); + + //result nodes + foreach($node->result as $result) + { + $property = new TResultProperty($resultMap); + $this->setObjectPropFromNode($property,$result); + $resultMap->addResultProperty($property); + } + + //create the discriminator + $discriminator = null; + if(isset($node->discriminator)) + { + $discriminator = new TDiscriminator(); + $this->setObjectPropFromNode($discriminator, $node->discriminator); + $discriminator->initMapping($resultMap); + } + + foreach($node->xpath('subMap') as $subMapNode) + { + if($discriminator===null) + throw new TSqlMapConfigurationException( + 'sqlmap_undefined_discriminator', $node, $this->_configFile,$subMapNode); + $subMap = new TSubMap; + $this->setObjectPropFromNode($subMap,$subMapNode); + $discriminator->addSubMap($subMap); + } + + if($discriminator!==null) + $resultMap->setDiscriminator($discriminator); + + return $resultMap; + } + + /** + * Load parameter map from xml. + * + * @param SimpleXmlElement parameter map node. + */ + protected function loadParameterMap($node) + { + $parameterMap = $this->createParameterMap($node); + + if(strlen($extendMap = $parameterMap->getExtends()) > 0) + { + if(!$this->_manager->getParameterMaps()->contains($extendMap)) + { + $extendNode=$this->getElementByIdValue($this->_document,'parameterMap',$extendMap); + if($extendNode!==null) + $this->loadParameterMap($extendNode); + } + + if(!$this->_manager->getParameterMaps()->contains($extendMap)) + throw new TSqlMapConfigurationException( + 'sqlmap_unable_to_find_parent_parameter_map', $node, $this->_configFile,$extendMap); + $superMap = $this->_manager->getParameterMap($extendMap); + $index = 0; + foreach($superMap->getPropertyNames() as $propertyName) + $parameterMap->insertProperty($index++,$superMap->getProperty($propertyName)); + } + $this->_manager->addParameterMap($parameterMap); + } + + /** + * Create a new parameter map from xml node. + * @param SimpleXmlElement parameter map node. + * @return TParameterMap new parameter mapping. + */ + protected function createParameterMap($node) + { + $parameterMap = new TParameterMap(); + $this->setObjectPropFromNode($parameterMap,$node); + foreach($node->parameter as $parameter) + { + $property = new TParameterProperty(); + $this->setObjectPropFromNode($property,$parameter); + $parameterMap->addProperty($property); + } + return $parameterMap; + } + + /** + * Load statement mapping from xml configuration file. + * @param SimpleXmlElement statement node. + */ + protected function loadStatementTag($node) + { + $statement = new TSqlMapStatement(); + $this->setObjectPropFromNode($statement,$node); + $this->processSqlStatement($statement, $node); + $mappedStatement = new TMappedStatement($this->_manager, $statement); + $this->_manager->addMappedStatement($mappedStatement); + } + + /** + * Load extended SQL statements if application. Replaces global properties + * in the sql text. Extracts inline parameter maps. + * @param TSqlMapStatement mapped statement. + * @param SimpleXmlElement statement node. + */ + protected function processSqlStatement($statement, $node) + { + $commandText = (string)$node; + if(strlen($extend = $statement->getExtends()) > 0) + { + $superNode = $this->getElementByIdValue($this->_document,'*',$extend); + if($superNode!==null) + $commandText = (string)$superNode . $commandText; + else + throw new TSqlMapConfigurationException( + 'sqlmap_unable_to_find_parent_sql', $extend, $this->_configFile,$node); + } + //$commandText = $this->_xmlConfig->replaceProperties($commandText); + $statement->initialize($this->_manager); + $this->applyInlineParameterMap($statement, $commandText, $node); + } + + /** + * Extract inline parameter maps. + * @param TSqlMapStatement statement object. + * @param string sql text + * @param SimpleXmlElement statement node. + */ + protected function applyInlineParameterMap($statement, $sqlStatement, $node) + { + $scope['file'] = $this->_configFile; + $scope['node'] = $node; + + $sqlStatement=preg_replace(self::ESCAPED_INLINE_SYMBOL_REGEXP,self::INLINE_PLACEHOLDER,$sqlStatement); + if($statement->parameterMap() === null) + { + // Build a Parametermap with the inline parameters. + // if they exist. Then delete inline infos from sqltext. + $parameterParser = new TInlineParameterMapParser; + $sqlText = $parameterParser->parse($sqlStatement, $scope); + if(count($sqlText['parameters']) > 0) + { + $map = new TParameterMap(); + $map->setID($statement->getID().'-InLineParameterMap'); + $statement->setInlineParameterMap($map); + foreach($sqlText['parameters'] as $property) + $map->addProperty($property); + } + $sqlStatement = $sqlText['sql']; + } + $sqlStatement=preg_replace('/'.self::INLINE_PLACEHOLDER.'/',self::INLINE_SYMBOL,$sqlStatement); + + $this->prepareSql($statement, $sqlStatement, $node); + } + + /** + * Prepare the sql text (may extend to dynamic sql). + * @param TSqlMapStatement mapped statement. + * @param string sql text. + * @param SimpleXmlElement statement node. + * @todo Extend to dynamic sql. + */ + protected function prepareSql($statement,$sqlStatement, $node) + { + $simpleDynamic = new TSimpleDynamicParser; + $sqlStatement=preg_replace(self::ESCAPED_SIMPLE_MARK_REGEXP,self::SIMPLE_PLACEHOLDER,$sqlStatement); + $dynamics = $simpleDynamic->parse($sqlStatement); + if(count($dynamics['parameters']) > 0) + { + $sql = new TSimpleDynamicSql($dynamics['parameters']); + $sqlStatement = $dynamics['sql']; + } + else + $sql = new TStaticSql(); + $sqlStatement=preg_replace('/'.self::SIMPLE_PLACEHOLDER.'/',self::SIMPLE_MARK,$sqlStatement); + $sql->buildPreparedStatement($statement, $sqlStatement); + $statement->setSqlText($sql); + } + + /** + * Load select statement from xml mapping. + * @param SimpleXmlElement select node. + */ + protected function loadSelectTag($node) + { + $select = new TSqlMapSelect; + $this->setObjectPropFromNode($select,$node); + $this->processSqlStatement($select,$node); + $mappedStatement = new TMappedStatement($this->_manager, $select); + if(strlen($select->getCacheModel()) > 0) + $mappedStatement = new TCachingStatement($mappedStatement); + + $this->_manager->addMappedStatement($mappedStatement); + } + + /** + * Load insert statement from xml mapping. + * @param SimpleXmlElement insert node. + */ + protected function loadInsertTag($node) + { + $insert = $this->createInsertStatement($node); + $this->processSqlStatement($insert, $node); + $mappedStatement = new TInsertMappedStatement($this->_manager, $insert); + $this->_manager->addMappedStatement($mappedStatement); + } + + /** + * Create new insert statement from xml node. + * @param SimpleXmlElement insert node. + * @return TSqlMapInsert insert statement. + */ + protected function createInsertStatement($node) + { + $insert = new TSqlMapInsert; + $this->setObjectPropFromNode($insert,$node); + if(isset($node->selectKey)) + $this->loadSelectKeyTag($insert,$node->selectKey); + return $insert; + } + + /** + * Load the selectKey statement from xml mapping. + * @param SimpleXmlElement selectkey node + */ + protected function loadSelectKeyTag($insert, $node) + { + $selectKey = new TSqlMapSelectKey; + $this->setObjectPropFromNode($selectKey,$node); + $selectKey->setID($insert->getID()); + $selectKey->setID($insert->getID().'.SelectKey'); + $this->processSqlStatement($selectKey,$node); + $insert->setSelectKey($selectKey); + $mappedStatement = new TMappedStatement($this->_manager, $selectKey); + $this->_manager->addMappedStatement($mappedStatement); + } + + /** + * Load update statement from xml mapping. + * @param SimpleXmlElement update node. + */ + protected function loadUpdateTag($node) + { + $update = new TSqlMapUpdate; + $this->setObjectPropFromNode($update,$node); + $this->processSqlStatement($update, $node); + $mappedStatement = new TUpdateMappedStatement($this->_manager, $update); + $this->_manager->addMappedStatement($mappedStatement); + } + + /** + * Load delete statement from xml mapping. + * @param SimpleXmlElement delete node. + */ + protected function loadDeleteTag($node) + { + $delete = new TSqlMapDelete; + $this->setObjectPropFromNode($delete,$node); + $this->processSqlStatement($delete, $node); + $mappedStatement = new TDeleteMappedStatement($this->_manager, $delete); + $this->_manager->addMappedStatement($mappedStatement); + } + + /** + * Load procedure statement from xml mapping. + * @todo Implement loading procedure + * @param SimpleXmlElement procedure node + */ + protected function loadProcedureTag($node) + { + //var_dump('todo: add load procedure'); + } + + /** + * Load cache models from xml mapping. + * @param SimpleXmlElement cache node. + */ + protected function loadCacheModel($node) + { + $cacheModel = new TSqlMapCacheModel; + $properties = array('id','implementation'); + foreach($node->attributes() as $name=>$value) + { + if(in_array(strtolower($name), $properties)) + $cacheModel->{'set'.$name}((string)$value); + } + $cache = Prado::createComponent($cacheModel->getImplementationClass(), $cacheModel); + $this->setObjectPropFromNode($cache,$node,$properties); + + foreach($node->xpath('property') as $propertyNode) + { + $name = $propertyNode->attributes()->name; + if($name===null || $name==='') continue; + + $value = $propertyNode->attributes()->value; + if($value===null || $value==='') continue; + + if( !TPropertyAccess::has($cache, $name) ) continue; + + TPropertyAccess::set($cache, $name, $value); + } + + $this->loadFlushInterval($cacheModel,$node); + + $cacheModel->initialize($cache); + $this->_manager->addCacheModel($cacheModel); + foreach($node->xpath('flushOnExecute') as $flush) + $this->loadFlushOnCache($cacheModel,$node,$flush); + } + + /** + * Load the flush interval + * @param TSqlMapCacheModel cache model + * @param SimpleXmlElement cache node + */ + protected function loadFlushInterval($cacheModel, $node) + { + $flushInterval = $node->xpath('flushInterval'); + if($flushInterval === null || count($flushInterval) === 0) return; + $duration = 0; + foreach($flushInterval[0]->attributes() as $name=>$value) + { + switch(strToLower($name)) + { + case 'seconds': + $duration += (integer)$value; + break; + case 'minutes': + $duration += 60 * (integer)$value; + break; + case 'hours': + $duration += 3600 * (integer)$value; + break; + case 'days': + $duration += 86400 * (integer)$value; + break; + case 'duration': + $duration = (integer)$value; + break 2; // switch, foreach + } + } + $cacheModel->setFlushInterval($duration); + } + + /** + * Load the flush on cache properties. + * @param TSqlMapCacheModel cache model + * @param SimpleXmlElement parent node. + * @param SimpleXmlElement flush node. + */ + protected function loadFlushOnCache($cacheModel,$parent,$node) + { + $id = $cacheModel->getID(); + if(!isset($this->_FlushOnExecuteStatements[$id])) + $this->_FlushOnExecuteStatements[$id] = array(); + foreach($node->attributes() as $name=>$value) + { + if(strtolower($name)==='statement') + $this->_FlushOnExecuteStatements[$id][] = (string)$value; + } + } + + /** + * Attach CacheModel to statement and register trigger statements for cache models + */ + protected function registerCacheTriggers() + { + foreach($this->_FlushOnExecuteStatements as $cacheID => $statementIDs) + { + $cacheModel = $this->_manager->getCacheModel($cacheID); + foreach($statementIDs as $statementID) + { + $statement = $this->_manager->getMappedStatement($statementID); + $cacheModel->registerTriggerStatement($statement); + } + } + } +} + diff --git a/framework/Data/SqlMap/DataMapper/TFastSqlMapApplicationCache.php b/framework/Data/SqlMap/DataMapper/TFastSqlMapApplicationCache.php index d780b413..e85c54cf 100644 --- a/framework/Data/SqlMap/DataMapper/TFastSqlMapApplicationCache.php +++ b/framework/Data/SqlMap/DataMapper/TFastSqlMapApplicationCache.php @@ -1,89 +1,89 @@ -<?php
-/**
- * TFastSqlMapApplicationCache class file contains Fast SqlMap cache implementation.
- *
- * @author Berczi Gabor <gabor.berczi@devworx.hu>
- * @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id: TFastSqlMapApplicationCache.php 2996 2011-06-20 15:24:57Z ctrlaltca@gmail.com $
- * @package System.Data.SqlMap
- */
-
-/**
- * TFastSqlMapApplicationCache class file
- *
- * Fast SqlMap result cache class with minimal-concurrency get/set and atomic flush operations
- *
- * @author Berczi Gabor <gabor.berczi@devworx.hu>
- * @version $Id: TFastSqlMapApplicationCache.php 2996 2011-06-20 15:24:57Z ctrlaltca@gmail.com $
- * @package System.Data.SqlMap
- * @since 3.2
- */
-
-class TFastSqlMapApplicationCache implements ICache
-{
- protected $_cacheModel=null;
- protected $_cache=null;
-
- public function __construct($cacheModel=null)
- {
- $this->_cacheModel = $cacheModel;
- }
-
- protected function getBaseKeyKeyName()
- {
- return 'SqlMapCacheBaseKey::'.$this->_cacheModel->getId();
- }
-
- protected function getBaseKey()
- {
- $cache = $this->getCache();
- $keyname = $this->getBaseKeyKeyName();
- $basekey = $cache->get($keyname);
- if (!$basekey)
- {
- $basekey = DxUtil::generateRandomHash(8);
- $cache->set($keyname,$basekey);
- }
- return $basekey;
- }
-
- protected function getCacheKey($key)
- {
- return $this->getBaseKey().'###'.$key;
- }
-
- public function delete($key)
- {
- $this->getCache()->delete($this->getCacheKey($key));
- }
-
- public function flush()
- {
- $this->getCache()->delete($this->getBaseKeyKeyName());
- }
-
- public function get($key)
- {
- $result = $this->getCache()->get($this->getCacheKey($key));
- return $result === false ? null : $result;
- }
-
- public function set($key, $value,$expire=0,$dependency=null)
- {
- $this->getCache()->set($this->getCacheKey($key), $value, $expire,$dependency);
- }
-
- protected function getCache()
- {
- if (!$this->_cache)
- $this->_cache = Prado::getApplication()->getCache();
- return $this->_cache;
- }
-
- public function add($id,$value,$expire=0,$dependency=null)
- {
- throw new TSqlMapException('sqlmap_use_set_to_store_cache');
- }
-}
+<?php +/** + * TFastSqlMapApplicationCache class file contains Fast SqlMap cache implementation. + * + * @author Berczi Gabor <gabor.berczi@devworx.hu> + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TFastSqlMapApplicationCache.php 2996 2011-06-20 15:24:57Z ctrlaltca@gmail.com $ + * @package System.Data.SqlMap + */ + +/** + * TFastSqlMapApplicationCache class file + * + * Fast SqlMap result cache class with minimal-concurrency get/set and atomic flush operations + * + * @author Berczi Gabor <gabor.berczi@devworx.hu> + * @version $Id: TFastSqlMapApplicationCache.php 2996 2011-06-20 15:24:57Z ctrlaltca@gmail.com $ + * @package System.Data.SqlMap + * @since 3.2 + */ + +class TFastSqlMapApplicationCache implements ICache +{ + protected $_cacheModel=null; + protected $_cache=null; + + public function __construct($cacheModel=null) + { + $this->_cacheModel = $cacheModel; + } + + protected function getBaseKeyKeyName() + { + return 'SqlMapCacheBaseKey::'.$this->_cacheModel->getId(); + } + + protected function getBaseKey() + { + $cache = $this->getCache(); + $keyname = $this->getBaseKeyKeyName(); + $basekey = $cache->get($keyname); + if (!$basekey) + { + $basekey = DxUtil::generateRandomHash(8); + $cache->set($keyname,$basekey); + } + return $basekey; + } + + protected function getCacheKey($key) + { + return $this->getBaseKey().'###'.$key; + } + + public function delete($key) + { + $this->getCache()->delete($this->getCacheKey($key)); + } + + public function flush() + { + $this->getCache()->delete($this->getBaseKeyKeyName()); + } + + public function get($key) + { + $result = $this->getCache()->get($this->getCacheKey($key)); + return $result === false ? null : $result; + } + + public function set($key, $value,$expire=0,$dependency=null) + { + $this->getCache()->set($this->getCacheKey($key), $value, $expire,$dependency); + } + + protected function getCache() + { + if (!$this->_cache) + $this->_cache = Prado::getApplication()->getCache(); + return $this->_cache; + } + + public function add($id,$value,$expire=0,$dependency=null) + { + throw new TSqlMapException('sqlmap_use_set_to_store_cache'); + } +} diff --git a/framework/Data/SqlMap/DataMapper/TLazyLoadList.php b/framework/Data/SqlMap/DataMapper/TLazyLoadList.php index 9b163960..8e5d8d85 100644 --- a/framework/Data/SqlMap/DataMapper/TLazyLoadList.php +++ b/framework/Data/SqlMap/DataMapper/TLazyLoadList.php @@ -1,144 +1,144 @@ -<?php
-/**
- * TLazyLoadList, TObjectProxy classes file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
+<?php +/** + * TLazyLoadList, TObjectProxy classes file. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.SqlMap
- */
-
-/**
- * TLazyLoadList executes mapped statements when the proxy collection is first accessed.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.SqlMap
- * @since 3.1
- */
-class TLazyLoadList
-{
- private $_param;
- private $_target;
- private $_propertyName='';
- private $_statement='';
- private $_loaded=false;
- private $_innerList;
- private $_connection;
-
- /**
- * Create a new proxy list that will execute the mapped statement when any
- * of the list's method are accessed for the first time.
- * @param TMappedStatement statement to be executed to load the data.
- * @param mixed parameter value for the statement.
- * @param object result object that contains the lazy collection.
- * @param string property of the result object to set the loaded collection.
- */
- protected function __construct($mappedStatement, $param, $target, $propertyName)
- {
- $this->_param = $param;
- $this->_target = $target;
- $this->_statement = $mappedStatement;
- $this->_connection=$mappedStatement->getManager()->getDbConnection();
- $this->_propertyName = $propertyName;
- }
-
- /**
- * Create a new instance of a lazy collection.
- * @param TMappedStatement statement to be executed to load the data.
- * @param mixed parameter value for the statement.
- * @param object result object that contains the lazy collection.
- * @param string property of the result object to set the loaded collection.
- * @return TObjectProxy proxied collection object.
- */
- public static function newInstance($mappedStatement, $param, $target, $propertyName)
- {
- $handler = new self($mappedStatement, $param, $target, $propertyName);
- $statement = $mappedStatement->getStatement();
- $registry=$mappedStatement->getManager()->getTypeHandlers();
- $list = $statement->createInstanceOfListClass($registry);
- if(!is_object($list))
- throw new TSqlMapExecutionException('sqlmap_invalid_lazyload_list',$statement->getID());
- return new TObjectProxy($handler, $list);
- }
-
- /**
- * Relay the method call to the underlying collection.
- * @param string method name.
- * @param array method parameters.
- */
- public function intercept($method, $arguments)
- {
- return call_user_func_array(array($this->_innerList, $method), $arguments);
- }
-
- /**
- * Load the data by executing the mapped statement.
- */
- protected function fetchListData()
- {
- if($this->_loaded == false)
- {
- $this->_innerList = $this->_statement->executeQueryForList($this->_connection,$this->_param);
- $this->_loaded = true;
- //replace the target property with real list
- TPropertyAccess::set($this->_target, $this->_propertyName, $this->_innerList);
- }
- }
-
- /**
- * Try to fetch the data when any of the proxy collection method is called.
- * @param string method name.
- * @return boolean true if the underlying collection has the corresponding method name.
- */
- public function hasMethod($method)
- {
- $this->fetchListData();
- if(is_object($this->_innerList))
- return in_array($method, get_class_methods($this->_innerList));
- return false;
- }
-}
-
-/**
- * TObjectProxy sets up a simple object that intercepts method calls to a
- * particular object and relays the call to handler object.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.SqlMap
- * @since 3.1
- */
-class TObjectProxy
-{
- private $_object;
- private $_handler;
-
- /**
- * @param object handler to method calls.
- * @param object the object to by proxied.
- */
- public function __construct($handler, $object)
- {
- $this->_handler = $handler;
- $this->_object = $object;
- }
-
- /**
- * Relay the method call to the handler object (if able to be handled), otherwise
- * it calls the proxied object's method.
- * @param string method name called
- * @param array method arguments
- * @return mixed method return value.
- */
- public function __call($method,$params)
- {
- if($this->_handler->hasMethod($method))
- return $this->_handler->intercept($method, $params);
- else
- return call_user_func_array(array($this->_object, $method), $params);
- }
-}
-
+ * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.SqlMap + */ + +/** + * TLazyLoadList executes mapped statements when the proxy collection is first accessed. + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.SqlMap + * @since 3.1 + */ +class TLazyLoadList +{ + private $_param; + private $_target; + private $_propertyName=''; + private $_statement=''; + private $_loaded=false; + private $_innerList; + private $_connection; + + /** + * Create a new proxy list that will execute the mapped statement when any + * of the list's method are accessed for the first time. + * @param TMappedStatement statement to be executed to load the data. + * @param mixed parameter value for the statement. + * @param object result object that contains the lazy collection. + * @param string property of the result object to set the loaded collection. + */ + protected function __construct($mappedStatement, $param, $target, $propertyName) + { + $this->_param = $param; + $this->_target = $target; + $this->_statement = $mappedStatement; + $this->_connection=$mappedStatement->getManager()->getDbConnection(); + $this->_propertyName = $propertyName; + } + + /** + * Create a new instance of a lazy collection. + * @param TMappedStatement statement to be executed to load the data. + * @param mixed parameter value for the statement. + * @param object result object that contains the lazy collection. + * @param string property of the result object to set the loaded collection. + * @return TObjectProxy proxied collection object. + */ + public static function newInstance($mappedStatement, $param, $target, $propertyName) + { + $handler = new self($mappedStatement, $param, $target, $propertyName); + $statement = $mappedStatement->getStatement(); + $registry=$mappedStatement->getManager()->getTypeHandlers(); + $list = $statement->createInstanceOfListClass($registry); + if(!is_object($list)) + throw new TSqlMapExecutionException('sqlmap_invalid_lazyload_list',$statement->getID()); + return new TObjectProxy($handler, $list); + } + + /** + * Relay the method call to the underlying collection. + * @param string method name. + * @param array method parameters. + */ + public function intercept($method, $arguments) + { + return call_user_func_array(array($this->_innerList, $method), $arguments); + } + + /** + * Load the data by executing the mapped statement. + */ + protected function fetchListData() + { + if($this->_loaded == false) + { + $this->_innerList = $this->_statement->executeQueryForList($this->_connection,$this->_param); + $this->_loaded = true; + //replace the target property with real list + TPropertyAccess::set($this->_target, $this->_propertyName, $this->_innerList); + } + } + + /** + * Try to fetch the data when any of the proxy collection method is called. + * @param string method name. + * @return boolean true if the underlying collection has the corresponding method name. + */ + public function hasMethod($method) + { + $this->fetchListData(); + if(is_object($this->_innerList)) + return in_array($method, get_class_methods($this->_innerList)); + return false; + } +} + +/** + * TObjectProxy sets up a simple object that intercepts method calls to a + * particular object and relays the call to handler object. + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.SqlMap + * @since 3.1 + */ +class TObjectProxy +{ + private $_object; + private $_handler; + + /** + * @param object handler to method calls. + * @param object the object to by proxied. + */ + public function __construct($handler, $object) + { + $this->_handler = $handler; + $this->_object = $object; + } + + /** + * Relay the method call to the handler object (if able to be handled), otherwise + * it calls the proxied object's method. + * @param string method name called + * @param array method arguments + * @return mixed method return value. + */ + public function __call($method,$params) + { + if($this->_handler->hasMethod($method)) + return $this->_handler->intercept($method, $params); + else + return call_user_func_array(array($this->_object, $method), $params); + } +} + diff --git a/framework/Data/SqlMap/DataMapper/TPropertyAccess.php b/framework/Data/SqlMap/DataMapper/TPropertyAccess.php index 7445e9d8..a6a2c451 100644 --- a/framework/Data/SqlMap/DataMapper/TPropertyAccess.php +++ b/framework/Data/SqlMap/DataMapper/TPropertyAccess.php @@ -1,156 +1,156 @@ -<?php
-/**
- * TPropertyAccess class file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.SqlMap
- */
-
-/**
- * TPropertyAccess class provides dot notation stype property access and setting.
- *
- * Access object's properties (and subproperties) using dot path notation.
- * The following are equivalent.
- * <code>
- * echo $obj->property1;
- * echo $obj->getProperty1();
- * echo $obj['property1']; //$obj may be an array or object
- * echo TPropertyAccess($obj, 'property1');
- * </code>
- *
- * Setting a property value.
- * <code>
- * $obj1->propert1 = 'hello';
- * $obj->setProperty('hello');
- * $obj['property1'] = 'hello'; //$obj may be an array or object
- * TPropertyAccess($obj, 'property1', 'hello');
- * </code>
- *
- * Subproperties are supported using the dot notation. E.g.
- * <code>
- * echo $obj->property1->property2->property3
- * echo TPropertyAccess::get($obj, 'property1.property2.property3');
- * </code>
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.SqlMap
- * @since 3.1
- */
-class TPropertyAccess
-{
- /**
- * Gets the property value.
- * @param mixed object or path.
- * @param string property path.
- * @return mixed property value.
- * @throws TInvalidDataValueException if property path is invalid.
- */
- public static function get($object,$path)
- {
- if(!is_array($object) && !is_object($object))
- return $object;
- $properties = explode('.', $path);
- foreach($properties as $prop)
- {
- if(is_array($object) || $object instanceof ArrayAccess)
- {
- if(array_key_exists($prop, $object))
- $object = $object[$prop];
- else
- throw new TInvalidPropertyException('sqlmap_invalid_property',$path);
- }
- else if(is_object($object))
- {
- $getter = 'get'.$prop;
- if(method_exists($object, $getter) && is_callable(array($object, $getter)))
- $object = $object->{$getter}();
- else if(in_array($prop, array_keys(get_object_vars($object))))
- $object = $object->{$prop};
- elseif(method_exists($object, '__get') && is_callable(array($object, '__get')))
- $object = $object->{$prop};
- else
- throw new TInvalidPropertyException('sqlmap_invalid_property',$path);
- }
- else
- throw new TInvalidPropertyException('sqlmap_invalid_property',$path);
- }
- return $object;
- }
-
- /**
- * @param mixed object or array
- * @param string property path.
- * @return boolean true if property path is valid
- */
- public static function has($object, $path)
- {
- if(!is_array($object) && !is_object($object))
- return false;
- $properties = explode('.', $path);
- foreach($properties as $prop)
- {
- if(is_array($object) || $object instanceof ArrayAccess)
- {
- if(array_key_exists($prop, $object))
- $object = $object[$prop];
- else
- return false;
- }
- else if(is_object($object))
- {
- $getter = 'get'.$prop;
- if(method_exists($object, $getter) && is_callable(array($object, $getter)))
- $object = $object->{$getter}();
- else if(in_array($prop, array_keys(get_object_vars($object))))
- $object = $object->{$prop};
- elseif(method_exists($object, '__get') && is_callable(array($object, '__get')))
- $object = $object->{$prop};
- else
- return false;
- }
- else
- return false;
- }
- return true;
- }
-
- /**
- * Sets the property value.
- * @param mixed object or array
- * @param string property path.
- * @param mixed new property value.
- * @throws TInvalidDataValueException if property path is invalid.
- */
- public static function set(&$originalObject, $path, $value)
- {
- $properties = explode('.', $path);
- $prop = array_pop($properties);
- if(count($properties) > 0)
- $object = self::get($originalObject, implode('.',$properties));
- else
- $object = &$originalObject;
-
- if(is_array($object) || $object instanceof ArrayAccess)
- {
- $object[$prop] = $value;
- }
- else if(is_object($object))
- {
- $setter = 'set'.$prop;
- if (method_exists($object, $setter) && is_callable(array($object, $setter)))
- $object->{$setter}($value);
- else
- $object->{$prop} = $value;
- }
- else
- throw new TInvalidPropertyException('sqlmap_invalid_property_type',$path);
- }
-
-}
-
-?>
+<?php +/** + * TPropertyAccess class file. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.SqlMap + */ + +/** + * TPropertyAccess class provides dot notation stype property access and setting. + * + * Access object's properties (and subproperties) using dot path notation. + * The following are equivalent. + * <code> + * echo $obj->property1; + * echo $obj->getProperty1(); + * echo $obj['property1']; //$obj may be an array or object + * echo TPropertyAccess($obj, 'property1'); + * </code> + * + * Setting a property value. + * <code> + * $obj1->propert1 = 'hello'; + * $obj->setProperty('hello'); + * $obj['property1'] = 'hello'; //$obj may be an array or object + * TPropertyAccess($obj, 'property1', 'hello'); + * </code> + * + * Subproperties are supported using the dot notation. E.g. + * <code> + * echo $obj->property1->property2->property3 + * echo TPropertyAccess::get($obj, 'property1.property2.property3'); + * </code> + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.SqlMap + * @since 3.1 + */ +class TPropertyAccess +{ + /** + * Gets the property value. + * @param mixed object or path. + * @param string property path. + * @return mixed property value. + * @throws TInvalidDataValueException if property path is invalid. + */ + public static function get($object,$path) + { + if(!is_array($object) && !is_object($object)) + return $object; + $properties = explode('.', $path); + foreach($properties as $prop) + { + if(is_array($object) || $object instanceof ArrayAccess) + { + if(array_key_exists($prop, $object)) + $object = $object[$prop]; + else + throw new TInvalidPropertyException('sqlmap_invalid_property',$path); + } + else if(is_object($object)) + { + $getter = 'get'.$prop; + if(method_exists($object, $getter) && is_callable(array($object, $getter))) + $object = $object->{$getter}(); + else if(in_array($prop, array_keys(get_object_vars($object)))) + $object = $object->{$prop}; + elseif(method_exists($object, '__get') && is_callable(array($object, '__get'))) + $object = $object->{$prop}; + else + throw new TInvalidPropertyException('sqlmap_invalid_property',$path); + } + else + throw new TInvalidPropertyException('sqlmap_invalid_property',$path); + } + return $object; + } + + /** + * @param mixed object or array + * @param string property path. + * @return boolean true if property path is valid + */ + public static function has($object, $path) + { + if(!is_array($object) && !is_object($object)) + return false; + $properties = explode('.', $path); + foreach($properties as $prop) + { + if(is_array($object) || $object instanceof ArrayAccess) + { + if(array_key_exists($prop, $object)) + $object = $object[$prop]; + else + return false; + } + else if(is_object($object)) + { + $getter = 'get'.$prop; + if(method_exists($object, $getter) && is_callable(array($object, $getter))) + $object = $object->{$getter}(); + else if(in_array($prop, array_keys(get_object_vars($object)))) + $object = $object->{$prop}; + elseif(method_exists($object, '__get') && is_callable(array($object, '__get'))) + $object = $object->{$prop}; + else + return false; + } + else + return false; + } + return true; + } + + /** + * Sets the property value. + * @param mixed object or array + * @param string property path. + * @param mixed new property value. + * @throws TInvalidDataValueException if property path is invalid. + */ + public static function set(&$originalObject, $path, $value) + { + $properties = explode('.', $path); + $prop = array_pop($properties); + if(count($properties) > 0) + $object = self::get($originalObject, implode('.',$properties)); + else + $object = &$originalObject; + + if(is_array($object) || $object instanceof ArrayAccess) + { + $object[$prop] = $value; + } + else if(is_object($object)) + { + $setter = 'set'.$prop; + if (method_exists($object, $setter) && is_callable(array($object, $setter))) + $object->{$setter}($value); + else + $object->{$prop} = $value; + } + else + throw new TInvalidPropertyException('sqlmap_invalid_property_type',$path); + } + +} + +?> diff --git a/framework/Data/SqlMap/DataMapper/TSqlMapCache.php b/framework/Data/SqlMap/DataMapper/TSqlMapCache.php index db027013..4e713fea 100644 --- a/framework/Data/SqlMap/DataMapper/TSqlMapCache.php +++ b/framework/Data/SqlMap/DataMapper/TSqlMapCache.php @@ -1,295 +1,295 @@ -<?php
-/**
- * TSqlMapCache class file contains FIFO, LRU, and GLOBAL cache implementations.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.SqlMap
- */
-
-/**
- * Allow different implementation of caching strategy. See <tt>TSqlMapFifoCache</tt>
- * for a first-in-first-out implementation. See <tt>TSqlMapLruCache</tt> for
- * a least-recently-used cache implementation.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.SqlMap
- * @since 3.1
- */
-abstract class TSqlMapCache implements ICache
-{
- protected $_keyList;
- protected $_cache;
- protected $_cacheSize = 100;
- protected $_cacheModel = null;
-
- /**
- * Create a new cache with limited cache size.
- * @param TSqlMapCacheModel $cacheModel.
- */
- public function __construct($cacheModel=null)
- {
- $this->_cache = new TMap;
- $this->_keyList = new TList;
- $this->_cacheModel=$cacheModel;
- }
-
- /**
- * Maximum number of items to cache. Default size is 100.
- * @param int cache size.
- */
- public function setCacheSize($value)
- {
- $this->_cacheSize=TPropertyValue::ensureInteger($value,100);
- }
-
- /**
- * @return int cache size.
- */
- public function getCacheSize()
- {
- return $this->_cacheSize;
- }
-
- /**
- * @return object the object removed if exists, null otherwise.
- */
- public function delete($key)
- {
- $object = $this->get($key);
- $this->_cache->remove($key);
- $this->_keyList->remove($key);
- return $object;
- }
-
- /**
- * Clears the cache.
- */
- public function flush()
- {
- $this->_keyList->clear();
- $this->_cache->clear();
- }
-
- /**
- * @throws TSqlMapException not implemented.
- */
- public function add($id,$value,$expire=0,$dependency=null)
- {
- throw new TSqlMapException('sqlmap_use_set_to_store_cache');
- }
-}
-
-/**
- * First-in-First-out cache implementation, removes
- * object that was first added when the cache is full.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.SqlMap
- * @since 3.1
- */
-class TSqlMapFifoCache extends TSqlMapCache
-{
- /**
- * @return mixed Gets a cached object with the specified key.
- */
- public function get($key)
- {
- return $this->_cache->itemAt($key);
- }
-
- /**
- * Stores a value identified by a key into cache.
- * The expire and dependency parameters are ignored.
- * @param string cache key
- * @param mixed value to cache.
- */
- public function set($key, $value,$expire=0,$dependency=null)
- {
- $this->_cache->add($key, $value);
- $this->_keyList->add($key);
- if($this->_keyList->getCount() > $this->_cacheSize)
- {
- $oldestKey = $this->_keyList->removeAt(0);
- $this->_cache->remove($oldestKey);
- }
- }
-}
-
-/**
- * Least recently used cache implementation, removes
- * object that was accessed last when the cache is full.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.SqlMap
- * @since 3.1
- */
-class TSqlMapLruCache extends TSqlMapCache
-{
- /**
- * @return mixed Gets a cached object with the specified key.
- */
- public function get($key)
- {
- if($this->_keyList->contains($key))
- {
- $this->_keyList->remove($key);
- $this->_keyList->add($key);
- return $this->_cache->itemAt($key);
- }
- }
-
- /**
- * Stores a value identified by a key into cache.
- * The expire and dependency parameters are ignored.
- * @param string the key identifying the value to be cached
- * @param mixed the value to be cached
- */
- public function set($key, $value,$expire=0,$dependency=null)
- {
- $this->_cache->add($key, $value);
- $this->_keyList->add($key);
- if($this->_keyList->getCount() > $this->_cacheSize)
- {
- $oldestKey = $this->_keyList->removeAt(0);
- $this->_cache->remove($oldestKey);
- }
- }
-}
-
-/**
- * TSqlMapApplicationCache uses the default Prado application cache for
- * caching SqlMap results.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.SqlMap
- * @since 3.1
- */
-class TSqlMapApplicationCache implements ICache
-{
- protected $_cacheModel=null;
-
- /**
- * Create a new cache with limited cache size.
- * @param TSqlMapCacheModel $cacheModel.
- */
- public function __construct($cacheModel=null)
- {
- $this->_cacheModel=$cacheModel;
- }
-
- /**
- *
- * @return string a KeyListID for the cache model.
- */
- protected function getKeyListId()
- {
- $id='keyList';
- if ($this->_cacheModel instanceof TSqlMapCacheModel)
- $id.='_'.$this->_cacheModel->getId();
- return $id;
- }
- /**
- * Retreive keylist from cache or create it if it doesn't exists
- * @return TList
- */
- protected function getKeyList()
- {
- if (($keyList=$this->getCache()->get($this->getKeyListId()))===false)
- {
- $keyList=new TList();
- $this->getCache()->set($this->getKeyListId(), $keyList);
- }
- return $keyList;
- }
-
- protected function setKeyList($keyList)
- {
- $this->getCache()->set($this->getKeyListId(), $keyList);
- }
-
- /**
- * @param string item to be deleted.
- */
- public function delete($key)
- {
- $keyList=$this->getKeyList();
- $keyList->remove($key);
- $this->getCache()->delete($key);
- $this->setKeyList($keyList);
- }
-
- /**
- * Deletes all items in the cache, only for data cached by sqlmap cachemodel
- */
- public function flush()
- {
- $keyList=$this->getKeyList();
- $cache=$this->getCache();
- foreach ($keyList as $key)
- {
- $cache->delete($key);
- }
- // Remove the old keylist
- $cache->delete($this->getKeyListId());
- }
-
- /**
- * @return mixed Gets a cached object with the specified key.
- */
- public function get($key)
- {
- $result = $this->getCache()->get($key);
- if ($result === false)
- {
- // if the key has not been found in cache (e.g expired), remove from keylist
- $keyList=$this->getKeyList();
- if ($keyList->contains($key))
- {
- $keyList->remove($key);
- $this->setKeyList($keyList);
- }
- }
- return $result === false ? null : $result;
- }
-
- /**
- * Stores a value identified by a key into cache.
- * @param string the key identifying the value to be cached
- * @param mixed the value to be cached
- */
- public function set($key, $value,$expire=0,$dependency=null)
- {
- $this->getCache()->set($key, $value, $expire,$dependency);
- $keyList=$this->getKeyList();
- if (!$keyList->contains($key))
- {
- $keyList->add($key);
- $this->setKeyList($keyList);
- }
- }
-
- /**
- * @return ICache Application cache instance.
- */
- protected function getCache()
- {
- return Prado::getApplication()->getCache();
- }
-
- /**
- * @throws TSqlMapException not implemented.
- */
- public function add($id,$value,$expire=0,$dependency=null)
- {
- throw new TSqlMapException('sqlmap_use_set_to_store_cache');
- }
-}
-
+<?php +/** + * TSqlMapCache class file contains FIFO, LRU, and GLOBAL cache implementations. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.SqlMap + */ + +/** + * Allow different implementation of caching strategy. See <tt>TSqlMapFifoCache</tt> + * for a first-in-first-out implementation. See <tt>TSqlMapLruCache</tt> for + * a least-recently-used cache implementation. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.SqlMap + * @since 3.1 + */ +abstract class TSqlMapCache implements ICache +{ + protected $_keyList; + protected $_cache; + protected $_cacheSize = 100; + protected $_cacheModel = null; + + /** + * Create a new cache with limited cache size. + * @param TSqlMapCacheModel $cacheModel. + */ + public function __construct($cacheModel=null) + { + $this->_cache = new TMap; + $this->_keyList = new TList; + $this->_cacheModel=$cacheModel; + } + + /** + * Maximum number of items to cache. Default size is 100. + * @param int cache size. + */ + public function setCacheSize($value) + { + $this->_cacheSize=TPropertyValue::ensureInteger($value,100); + } + + /** + * @return int cache size. + */ + public function getCacheSize() + { + return $this->_cacheSize; + } + + /** + * @return object the object removed if exists, null otherwise. + */ + public function delete($key) + { + $object = $this->get($key); + $this->_cache->remove($key); + $this->_keyList->remove($key); + return $object; + } + + /** + * Clears the cache. + */ + public function flush() + { + $this->_keyList->clear(); + $this->_cache->clear(); + } + + /** + * @throws TSqlMapException not implemented. + */ + public function add($id,$value,$expire=0,$dependency=null) + { + throw new TSqlMapException('sqlmap_use_set_to_store_cache'); + } +} + +/** + * First-in-First-out cache implementation, removes + * object that was first added when the cache is full. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.SqlMap + * @since 3.1 + */ +class TSqlMapFifoCache extends TSqlMapCache +{ + /** + * @return mixed Gets a cached object with the specified key. + */ + public function get($key) + { + return $this->_cache->itemAt($key); + } + + /** + * Stores a value identified by a key into cache. + * The expire and dependency parameters are ignored. + * @param string cache key + * @param mixed value to cache. + */ + public function set($key, $value,$expire=0,$dependency=null) + { + $this->_cache->add($key, $value); + $this->_keyList->add($key); + if($this->_keyList->getCount() > $this->_cacheSize) + { + $oldestKey = $this->_keyList->removeAt(0); + $this->_cache->remove($oldestKey); + } + } +} + +/** + * Least recently used cache implementation, removes + * object that was accessed last when the cache is full. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.SqlMap + * @since 3.1 + */ +class TSqlMapLruCache extends TSqlMapCache +{ + /** + * @return mixed Gets a cached object with the specified key. + */ + public function get($key) + { + if($this->_keyList->contains($key)) + { + $this->_keyList->remove($key); + $this->_keyList->add($key); + return $this->_cache->itemAt($key); + } + } + + /** + * Stores a value identified by a key into cache. + * The expire and dependency parameters are ignored. + * @param string the key identifying the value to be cached + * @param mixed the value to be cached + */ + public function set($key, $value,$expire=0,$dependency=null) + { + $this->_cache->add($key, $value); + $this->_keyList->add($key); + if($this->_keyList->getCount() > $this->_cacheSize) + { + $oldestKey = $this->_keyList->removeAt(0); + $this->_cache->remove($oldestKey); + } + } +} + +/** + * TSqlMapApplicationCache uses the default Prado application cache for + * caching SqlMap results. + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.SqlMap + * @since 3.1 + */ +class TSqlMapApplicationCache implements ICache +{ + protected $_cacheModel=null; + + /** + * Create a new cache with limited cache size. + * @param TSqlMapCacheModel $cacheModel. + */ + public function __construct($cacheModel=null) + { + $this->_cacheModel=$cacheModel; + } + + /** + * + * @return string a KeyListID for the cache model. + */ + protected function getKeyListId() + { + $id='keyList'; + if ($this->_cacheModel instanceof TSqlMapCacheModel) + $id.='_'.$this->_cacheModel->getId(); + return $id; + } + /** + * Retreive keylist from cache or create it if it doesn't exists + * @return TList + */ + protected function getKeyList() + { + if (($keyList=$this->getCache()->get($this->getKeyListId()))===false) + { + $keyList=new TList(); + $this->getCache()->set($this->getKeyListId(), $keyList); + } + return $keyList; + } + + protected function setKeyList($keyList) + { + $this->getCache()->set($this->getKeyListId(), $keyList); + } + + /** + * @param string item to be deleted. + */ + public function delete($key) + { + $keyList=$this->getKeyList(); + $keyList->remove($key); + $this->getCache()->delete($key); + $this->setKeyList($keyList); + } + + /** + * Deletes all items in the cache, only for data cached by sqlmap cachemodel + */ + public function flush() + { + $keyList=$this->getKeyList(); + $cache=$this->getCache(); + foreach ($keyList as $key) + { + $cache->delete($key); + } + // Remove the old keylist + $cache->delete($this->getKeyListId()); + } + + /** + * @return mixed Gets a cached object with the specified key. + */ + public function get($key) + { + $result = $this->getCache()->get($key); + if ($result === false) + { + // if the key has not been found in cache (e.g expired), remove from keylist + $keyList=$this->getKeyList(); + if ($keyList->contains($key)) + { + $keyList->remove($key); + $this->setKeyList($keyList); + } + } + return $result === false ? null : $result; + } + + /** + * Stores a value identified by a key into cache. + * @param string the key identifying the value to be cached + * @param mixed the value to be cached + */ + public function set($key, $value,$expire=0,$dependency=null) + { + $this->getCache()->set($key, $value, $expire,$dependency); + $keyList=$this->getKeyList(); + if (!$keyList->contains($key)) + { + $keyList->add($key); + $this->setKeyList($keyList); + } + } + + /** + * @return ICache Application cache instance. + */ + protected function getCache() + { + return Prado::getApplication()->getCache(); + } + + /** + * @throws TSqlMapException not implemented. + */ + public function add($id,$value,$expire=0,$dependency=null) + { + throw new TSqlMapException('sqlmap_use_set_to_store_cache'); + } +} + diff --git a/framework/Data/SqlMap/DataMapper/TSqlMapException.php b/framework/Data/SqlMap/DataMapper/TSqlMapException.php index 0bf0ac32..694774d8 100644 --- a/framework/Data/SqlMap/DataMapper/TSqlMapException.php +++ b/framework/Data/SqlMap/DataMapper/TSqlMapException.php @@ -1,115 +1,115 @@ -<?php
-
-/**
- * TSqlMapException is the base exception class for all SqlMap exceptions.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.SqlMap
- * @since 3.1
- */
-class TSqlMapException extends TException
-{
- /**
- * Constructor, similar to the parent constructor. For parameters that
- * are of SimpleXmlElement, the tag name and its attribute names and values
- * are expanded into a string.
- */
- public function __construct($errorMessage)
- {
- $this->setErrorCode($errorMessage);
- $errorMessage=$this->translateErrorMessage($errorMessage);
- $args=func_get_args();
- array_shift($args);
- $n=count($args);
- $tokens=array();
- for($i=0;$i<$n;++$i)
- {
- if($args[$i] instanceof SimpleXmlElement)
- $tokens['{'.$i.'}']=$this->implodeNode($args[$i]);
- else
- $tokens['{'.$i.'}']=TPropertyValue::ensureString($args[$i]);
- }
- parent::__construct(strtr($errorMessage,$tokens));
- }
-
- /**
- * @param SimpleXmlElement node
- * @return string tag name and attribute names and values.
- */
- protected function implodeNode($node)
- {
- $attributes=array();
- foreach($node->attributes() as $k=>$v)
- $attributes[]=$k.'="'.(string)$v.'"';
- return '<'.$node->getName().' '.implode(' ',$attributes).'>';
- }
-
- /**
- * @return string path to the error message file
- */
- protected function getErrorMessageFile()
- {
- $lang=Prado::getPreferredLanguage();
- $dir=dirname(__FILE__);
- $msgFile=$dir.'/messages-'.$lang.'.txt';
- if(!is_file($msgFile))
- $msgFile=$dir.'/messages.txt';
- return $msgFile;
- }
-}
-
-/**
- * TSqlMapConfigurationException, raised during configuration file parsing.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.SqlMap
- * @since 3.1
- */
-class TSqlMapConfigurationException extends TSqlMapException
-{
-
-}
-
-/**
- * TSqlMapUndefinedException, raised when mapped statemented are undefined.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.SqlMap
- * @since 3.1
- */
-class TSqlMapUndefinedException extends TSqlMapException
-{
-
-}
-
-/**
- * TSqlMapDuplicateException, raised when a duplicate mapped statement is found.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.SqlMap
- * @since 3.1
- */
-class TSqlMapDuplicateException extends TSqlMapException
-{
-}
-
-/**
- * TInvalidPropertyException, raised when setting or getting an invalid property.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.SqlMap
- * @since 3.1
- */
-class TInvalidPropertyException extends TSqlMapException
-{
-}
-
-class TSqlMapExecutionException extends TSqlMapException
-{
-}
-
+<?php + +/** + * TSqlMapException is the base exception class for all SqlMap exceptions. + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.SqlMap + * @since 3.1 + */ +class TSqlMapException extends TException +{ + /** + * Constructor, similar to the parent constructor. For parameters that + * are of SimpleXmlElement, the tag name and its attribute names and values + * are expanded into a string. + */ + public function __construct($errorMessage) + { + $this->setErrorCode($errorMessage); + $errorMessage=$this->translateErrorMessage($errorMessage); + $args=func_get_args(); + array_shift($args); + $n=count($args); + $tokens=array(); + for($i=0;$i<$n;++$i) + { + if($args[$i] instanceof SimpleXmlElement) + $tokens['{'.$i.'}']=$this->implodeNode($args[$i]); + else + $tokens['{'.$i.'}']=TPropertyValue::ensureString($args[$i]); + } + parent::__construct(strtr($errorMessage,$tokens)); + } + + /** + * @param SimpleXmlElement node + * @return string tag name and attribute names and values. + */ + protected function implodeNode($node) + { + $attributes=array(); + foreach($node->attributes() as $k=>$v) + $attributes[]=$k.'="'.(string)$v.'"'; + return '<'.$node->getName().' '.implode(' ',$attributes).'>'; + } + + /** + * @return string path to the error message file + */ + protected function getErrorMessageFile() + { + $lang=Prado::getPreferredLanguage(); + $dir=dirname(__FILE__); + $msgFile=$dir.'/messages-'.$lang.'.txt'; + if(!is_file($msgFile)) + $msgFile=$dir.'/messages.txt'; + return $msgFile; + } +} + +/** + * TSqlMapConfigurationException, raised during configuration file parsing. + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.SqlMap + * @since 3.1 + */ +class TSqlMapConfigurationException extends TSqlMapException +{ + +} + +/** + * TSqlMapUndefinedException, raised when mapped statemented are undefined. + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.SqlMap + * @since 3.1 + */ +class TSqlMapUndefinedException extends TSqlMapException +{ + +} + +/** + * TSqlMapDuplicateException, raised when a duplicate mapped statement is found. + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.SqlMap + * @since 3.1 + */ +class TSqlMapDuplicateException extends TSqlMapException +{ +} + +/** + * TInvalidPropertyException, raised when setting or getting an invalid property. + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.SqlMap + * @since 3.1 + */ +class TInvalidPropertyException extends TSqlMapException +{ +} + +class TSqlMapExecutionException extends TSqlMapException +{ +} + diff --git a/framework/Data/SqlMap/DataMapper/TSqlMapPagedList.php b/framework/Data/SqlMap/DataMapper/TSqlMapPagedList.php index 7c6a8e87..57949561 100644 --- a/framework/Data/SqlMap/DataMapper/TSqlMapPagedList.php +++ b/framework/Data/SqlMap/DataMapper/TSqlMapPagedList.php @@ -1,208 +1,208 @@ -<?php
-/**
- * TSqlMapPagedList class file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.SqlMap
- */
-
-Prado::using('System.Collections.TPagedList');
-
-/**
- * TSqlMapPagedList implements a list with paging functionality that retrieves
- * data from a SqlMap statement.
- *
- * The maximum number of records fetched is 3 times the page size. It fetches
- * the current, the previous and the next page at a time. This allows the paged
- * list to determine if the page is a the begin, the middle or the end of the list.
- *
- * The paged list does not need to know about the total number of records.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.SqlMap
- * @since 3.1
- */
-class TSqlMapPagedList extends TPagedList
-{
- private $_statement;
- private $_parameter;
- private $_prevPageList;
- private $_nextPageList;
- private $_delegate=null;
-
- /**
- * Create a new SqlMap paged list.
- * @param IMappedStatement SqlMap statement.
- * @param mixed query parameters
- * @param int page size
- * @param mixed delegate for each data row retrieved.
- * @param int number of page to fetch on initialization
- */
- public function __construct(IMappedStatement $statement,$parameter, $pageSize, $delegate=null, $page=0)
- {
- parent::__construct();
- parent::setCustomPaging(true);
- $this->initialize($statement,$parameter, $pageSize, $page);
- $this->_delegate=$delegate;
- }
-
- /**
- * Initialize the paged list.
- * @param IMappedStatement SqlMap statement.
- * @param mixed query parameters
- * @param int page size.
- * @param int number of page.
- */
- protected function initialize($statement, $parameter, $pageSize, $page)
- {
- $this->_statement = $statement;
- $this->_parameter = $parameter;
- $this->setPageSize($pageSize);
- $this->attachEventHandler('OnFetchData', array($this, 'fetchDataFromStatement'));
- $this->gotoPage($page);
- }
-
- /**
- * @throws TSqlMapException custom paging must be enabled.
- */
- public function setCustomPaging($value)
- {
- throw new TSqlMapException('sqlmap_must_enable_custom_paging');
- }
-
- /**
- * Fetch data by executing the SqlMap statement.
- * @param TPageList current object.
- * @param TPagedListFetchDataEventParameter fetch parameters
- */
- protected function fetchDataFromStatement($sender, $param)
- {
- $limit = $this->getOffsetAndLimit($param);
- $connection = $this->_statement->getManager()->getDbConnection();
- $data = $this->_statement->executeQueryForList($connection,
- $this->_parameter, null, $limit[0], $limit[1], $this->_delegate);
- $this->populateData($param, $data);
- }
-
- /**
- * Switches to the next page.
- * @return integer|boolean the new page index, false if next page is not availabe.
- */
- public function nextPage()
- {
- return $this->getIsNextPageAvailable() ? parent::nextPage() : false;
- }
-
- /**
- * Switches to the previous page.
- * @return integer|boolean the new page index, false if previous page is not availabe.
- */
- public function previousPage()
- {
- return $this->getIsPreviousPageAvailable() ? parent::previousPage() : false;
- }
-
- /**
- * Populate the list with the fetched data.
- * @param TPagedListFetchDataEventParameter fetch parameters
- * @param array fetched data.
- */
- protected function populateData($param, $data)
- {
- $total = $data instanceof TList ? $data->getCount() : count($data);
- $pageSize = $this->getPageSize();
- if($total < 1)
- {
- $param->setData($data);
- $this->_prevPageList = null;
- $this->_nextPageList = null;
- return;
- }
-
- if($param->getNewPageIndex() < 1)
- {
- $this->_prevPageList = null;
- if($total <= $pageSize)
- {
- $param->setData($data);
- $this->_nextPageList = null;
- }
- else
- {
- $param->setData(array_slice($data, 0, $pageSize));
- $this->_nextPageList = array_slice($data, $pageSize-1,$total);
- }
- }
- else
- {
- if($total <= $pageSize)
- {
- $this->_prevPageList = array_slice($data, 0, $total);
- $param->setData(array());
- $this->_nextPageList = null;
- }
- else if($total <= $pageSize*2)
- {
- $this->_prevPageList = array_slice($data, 0, $pageSize);
- $param->setData(array_slice($data, $pageSize, $total));
- $this->_nextPageList = null;
- }
- else
- {
- $this->_prevPageList = array_slice($data, 0, $pageSize);
- $param->setData(array_slice($data, $pageSize, $pageSize));
- $this->_nextPageList = array_slice($data, $pageSize*2, $total-$pageSize*2);
- }
- }
- }
-
- /**
- * Calculate the data fetch offsets and limits.
- * @param TPagedListFetchDataEventParameter fetch parameters
- * @return array 1st element is the offset, 2nd element is the limit.
- */
- protected function getOffsetAndLimit($param)
- {
- $index = $param->getNewPageIndex();
- $pageSize = $this->getPageSize();
- return $index < 1 ? array($index, $pageSize*2) : array(($index-1)*$pageSize, $pageSize*3);
- }
-
- /**
- * @return boolean true if the next page is available, false otherwise.
- */
- public function getIsNextPageAvailable()
- {
- return $this->_nextPageList!==null;
- }
-
- /**
- * @return boolean true if the previous page is available, false otherwise.
- */
- public function getIsPreviousPageAvailable()
- {
- return $this->_prevPageList!==null;
- }
-
- /**
- * @return boolean true if is the very last page, false otherwise.
- */
- public function getIsLastPage()
- {
- return ($this->_nextPageList===null) || $this->_nextPageList->getCount() < 1;
- }
-
- /**
- * @return boolean true if is not first nor last page, false otherwise.
- */
- public function getIsMiddlePage()
- {
- return !($this->getIsFirstPage() || $this->getIsLastPage());
- }
-}
-
+<?php +/** + * TSqlMapPagedList class file. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.SqlMap + */ + +Prado::using('System.Collections.TPagedList'); + +/** + * TSqlMapPagedList implements a list with paging functionality that retrieves + * data from a SqlMap statement. + * + * The maximum number of records fetched is 3 times the page size. It fetches + * the current, the previous and the next page at a time. This allows the paged + * list to determine if the page is a the begin, the middle or the end of the list. + * + * The paged list does not need to know about the total number of records. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.SqlMap + * @since 3.1 + */ +class TSqlMapPagedList extends TPagedList +{ + private $_statement; + private $_parameter; + private $_prevPageList; + private $_nextPageList; + private $_delegate=null; + + /** + * Create a new SqlMap paged list. + * @param IMappedStatement SqlMap statement. + * @param mixed query parameters + * @param int page size + * @param mixed delegate for each data row retrieved. + * @param int number of page to fetch on initialization + */ + public function __construct(IMappedStatement $statement,$parameter, $pageSize, $delegate=null, $page=0) + { + parent::__construct(); + parent::setCustomPaging(true); + $this->initialize($statement,$parameter, $pageSize, $page); + $this->_delegate=$delegate; + } + + /** + * Initialize the paged list. + * @param IMappedStatement SqlMap statement. + * @param mixed query parameters + * @param int page size. + * @param int number of page. + */ + protected function initialize($statement, $parameter, $pageSize, $page) + { + $this->_statement = $statement; + $this->_parameter = $parameter; + $this->setPageSize($pageSize); + $this->attachEventHandler('OnFetchData', array($this, 'fetchDataFromStatement')); + $this->gotoPage($page); + } + + /** + * @throws TSqlMapException custom paging must be enabled. + */ + public function setCustomPaging($value) + { + throw new TSqlMapException('sqlmap_must_enable_custom_paging'); + } + + /** + * Fetch data by executing the SqlMap statement. + * @param TPageList current object. + * @param TPagedListFetchDataEventParameter fetch parameters + */ + protected function fetchDataFromStatement($sender, $param) + { + $limit = $this->getOffsetAndLimit($param); + $connection = $this->_statement->getManager()->getDbConnection(); + $data = $this->_statement->executeQueryForList($connection, + $this->_parameter, null, $limit[0], $limit[1], $this->_delegate); + $this->populateData($param, $data); + } + + /** + * Switches to the next page. + * @return integer|boolean the new page index, false if next page is not availabe. + */ + public function nextPage() + { + return $this->getIsNextPageAvailable() ? parent::nextPage() : false; + } + + /** + * Switches to the previous page. + * @return integer|boolean the new page index, false if previous page is not availabe. + */ + public function previousPage() + { + return $this->getIsPreviousPageAvailable() ? parent::previousPage() : false; + } + + /** + * Populate the list with the fetched data. + * @param TPagedListFetchDataEventParameter fetch parameters + * @param array fetched data. + */ + protected function populateData($param, $data) + { + $total = $data instanceof TList ? $data->getCount() : count($data); + $pageSize = $this->getPageSize(); + if($total < 1) + { + $param->setData($data); + $this->_prevPageList = null; + $this->_nextPageList = null; + return; + } + + if($param->getNewPageIndex() < 1) + { + $this->_prevPageList = null; + if($total <= $pageSize) + { + $param->setData($data); + $this->_nextPageList = null; + } + else + { + $param->setData(array_slice($data, 0, $pageSize)); + $this->_nextPageList = array_slice($data, $pageSize-1,$total); + } + } + else + { + if($total <= $pageSize) + { + $this->_prevPageList = array_slice($data, 0, $total); + $param->setData(array()); + $this->_nextPageList = null; + } + else if($total <= $pageSize*2) + { + $this->_prevPageList = array_slice($data, 0, $pageSize); + $param->setData(array_slice($data, $pageSize, $total)); + $this->_nextPageList = null; + } + else + { + $this->_prevPageList = array_slice($data, 0, $pageSize); + $param->setData(array_slice($data, $pageSize, $pageSize)); + $this->_nextPageList = array_slice($data, $pageSize*2, $total-$pageSize*2); + } + } + } + + /** + * Calculate the data fetch offsets and limits. + * @param TPagedListFetchDataEventParameter fetch parameters + * @return array 1st element is the offset, 2nd element is the limit. + */ + protected function getOffsetAndLimit($param) + { + $index = $param->getNewPageIndex(); + $pageSize = $this->getPageSize(); + return $index < 1 ? array($index, $pageSize*2) : array(($index-1)*$pageSize, $pageSize*3); + } + + /** + * @return boolean true if the next page is available, false otherwise. + */ + public function getIsNextPageAvailable() + { + return $this->_nextPageList!==null; + } + + /** + * @return boolean true if the previous page is available, false otherwise. + */ + public function getIsPreviousPageAvailable() + { + return $this->_prevPageList!==null; + } + + /** + * @return boolean true if is the very last page, false otherwise. + */ + public function getIsLastPage() + { + return ($this->_nextPageList===null) || $this->_nextPageList->getCount() < 1; + } + + /** + * @return boolean true if is not first nor last page, false otherwise. + */ + public function getIsMiddlePage() + { + return !($this->getIsFirstPage() || $this->getIsLastPage()); + } +} + diff --git a/framework/Data/SqlMap/DataMapper/TSqlMapTypeHandlerRegistry.php b/framework/Data/SqlMap/DataMapper/TSqlMapTypeHandlerRegistry.php index 7a54e347..61c97245 100644 --- a/framework/Data/SqlMap/DataMapper/TSqlMapTypeHandlerRegistry.php +++ b/framework/Data/SqlMap/DataMapper/TSqlMapTypeHandlerRegistry.php @@ -1,192 +1,192 @@ -<?php
-/**
- * TSqlMapTypeHandlerRegistry, and abstract TSqlMapTypeHandler classes file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
+<?php +/** + * TSqlMapTypeHandlerRegistry, and abstract TSqlMapTypeHandler classes file. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.SqlMap
- */
-
-/**
- * TTypeHandlerFactory provides type handler classes to convert database field type
- * to PHP types and vice versa.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.SqlMap
- * @since 3.1
- */
-class TSqlMapTypeHandlerRegistry
-{
- private $_typeHandlers=array();
-
- /**
- * @param string database field type
- * @return TSqlMapTypeHandler type handler for give database field type.
- */
- public function getDbTypeHandler($dbType='NULL')
- {
- foreach($this->_typeHandlers as $handler)
- if($handler->getDbType()===$dbType)
- return $handler;
- }
-
- /**
- * @param string type handler class name
- * @return TSqlMapTypeHandler type handler
- */
- public function getTypeHandler($class)
- {
- if(isset($this->_typeHandlers[$class]))
- return $this->_typeHandlers[$class];
- }
-
- /**
- * @param TSqlMapTypeHandler registers a new type handler
- */
- public function registerTypeHandler(TSqlMapTypeHandler $handler)
- {
- $this->_typeHandlers[$handler->getType()] = $handler;
- }
-
- /**
- * Creates a new instance of a particular class (for PHP primative types,
- * their corresponding default value for given type is used).
- * @param string PHP type name
- * @return mixed default type value, if no type is specified null is returned.
- * @throws TSqlMapException if class name is not found.
- */
- public function createInstanceOf($type='')
- {
- if(strlen($type) > 0)
- {
- switch(strtolower($type))
- {
- case 'string': return '';
- case 'array': return array();
- case 'float': case 'double': case 'decimal': return 0.0;
- case 'integer': case 'int': return 0;
- case 'bool': case 'boolean': return false;
- }
-
- if(class_exists('Prado', false))
- return Prado::createComponent($type);
- else if(class_exists($type, false)) //NO auto loading
- return new $type;
- else
- throw new TSqlMapException('sqlmap_unable_to_find_class', $type);
- }
- }
-
- /**
- * Converts the value to given type using PHP's settype() function.
- * @param string PHP primative type.
- * @param mixed value to be casted
- * @return mixed type casted value.
- */
- public function convertToType($type, $value)
- {
- switch(strtolower($type))
- {
- case 'integer': case 'int':
- $type = 'integer'; break;
- case 'float': case 'double': case 'decimal':
- $type = 'float'; break;
- case 'boolean': case 'bool':
- $type = 'boolean'; break;
- case 'string' :
- $type = 'string'; break;
- default:
- return $value;
- }
- settype($value, $type);
- return $value;
- }
-}
-
-/**
- * A simple interface for implementing custom type handlers.
- *
- * Using this interface, you can implement a type handler that
- * will perform customized processing before parameters are set
- * on and after values are retrieved from the database.
- * Using a custom type handler you can extend
- * the framework to handle types that are not supported, or
- * handle supported types in a different way. For example,
- * you might use a custom type handler to implement proprietary
- * BLOB support (e.g. Oracle), or you might use it to handle
- * booleans using "Y" and "N" instead of the more typical 0/1.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.SqlMap
- * @since 3.1
- */
-abstract class TSqlMapTypeHandler extends TComponent
-{
- private $_dbType='NULL';
- private $_type;
- /**
- * @param string database field type.
- */
- public function setDbType($value)
- {
- $this->_dbType=$value;
- }
-
- /**
- * @return string database field type.
- */
- public function getDbType()
- {
- return $this->_dbType;
- }
-
- public function getType()
- {
- if($this->_type===null)
- return get_class($this);
- else
- return $this->_type;
- }
-
- public function setType($value)
- {
- $this->_type=$value;
- }
-
- /**
- * Performs processing on a value before it is used to set
- * the parameter of a IDbCommand.
- * @param object The interface for setting the value.
- * @param object The value to be set.
- */
- public abstract function getParameter($object);
-
-
- /**
- * Performs processing on a value before after it has been retrieved
- * from a database
- * @param object The interface for getting the value.
- * @return mixed The processed value.
- */
- public abstract function getResult($string);
-
-
- /**
- * Casts the string representation of a value into a type recognized by
- * this type handler. This method is used to translate nullValue values
- * into types that can be appropriately compared. If your custom type handler
- * cannot support nullValues, or if there is no reasonable string representation
- * for this type (e.g. File type), you can simply return the String representation
- * as it was passed in. It is not recommended to return null, unless null was passed
- * in.
- * @param array result row.
- * @return mixed
- */
- public abstract function createNewInstance($row=null);
-}
-
+ * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.SqlMap + */ + +/** + * TTypeHandlerFactory provides type handler classes to convert database field type + * to PHP types and vice versa. + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.SqlMap + * @since 3.1 + */ +class TSqlMapTypeHandlerRegistry +{ + private $_typeHandlers=array(); + + /** + * @param string database field type + * @return TSqlMapTypeHandler type handler for give database field type. + */ + public function getDbTypeHandler($dbType='NULL') + { + foreach($this->_typeHandlers as $handler) + if($handler->getDbType()===$dbType) + return $handler; + } + + /** + * @param string type handler class name + * @return TSqlMapTypeHandler type handler + */ + public function getTypeHandler($class) + { + if(isset($this->_typeHandlers[$class])) + return $this->_typeHandlers[$class]; + } + + /** + * @param TSqlMapTypeHandler registers a new type handler + */ + public function registerTypeHandler(TSqlMapTypeHandler $handler) + { + $this->_typeHandlers[$handler->getType()] = $handler; + } + + /** + * Creates a new instance of a particular class (for PHP primative types, + * their corresponding default value for given type is used). + * @param string PHP type name + * @return mixed default type value, if no type is specified null is returned. + * @throws TSqlMapException if class name is not found. + */ + public function createInstanceOf($type='') + { + if(strlen($type) > 0) + { + switch(strtolower($type)) + { + case 'string': return ''; + case 'array': return array(); + case 'float': case 'double': case 'decimal': return 0.0; + case 'integer': case 'int': return 0; + case 'bool': case 'boolean': return false; + } + + if(class_exists('Prado', false)) + return Prado::createComponent($type); + else if(class_exists($type, false)) //NO auto loading + return new $type; + else + throw new TSqlMapException('sqlmap_unable_to_find_class', $type); + } + } + + /** + * Converts the value to given type using PHP's settype() function. + * @param string PHP primative type. + * @param mixed value to be casted + * @return mixed type casted value. + */ + public function convertToType($type, $value) + { + switch(strtolower($type)) + { + case 'integer': case 'int': + $type = 'integer'; break; + case 'float': case 'double': case 'decimal': + $type = 'float'; break; + case 'boolean': case 'bool': + $type = 'boolean'; break; + case 'string' : + $type = 'string'; break; + default: + return $value; + } + settype($value, $type); + return $value; + } +} + +/** + * A simple interface for implementing custom type handlers. + * + * Using this interface, you can implement a type handler that + * will perform customized processing before parameters are set + * on and after values are retrieved from the database. + * Using a custom type handler you can extend + * the framework to handle types that are not supported, or + * handle supported types in a different way. For example, + * you might use a custom type handler to implement proprietary + * BLOB support (e.g. Oracle), or you might use it to handle + * booleans using "Y" and "N" instead of the more typical 0/1. + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.SqlMap + * @since 3.1 + */ +abstract class TSqlMapTypeHandler extends TComponent +{ + private $_dbType='NULL'; + private $_type; + /** + * @param string database field type. + */ + public function setDbType($value) + { + $this->_dbType=$value; + } + + /** + * @return string database field type. + */ + public function getDbType() + { + return $this->_dbType; + } + + public function getType() + { + if($this->_type===null) + return get_class($this); + else + return $this->_type; + } + + public function setType($value) + { + $this->_type=$value; + } + + /** + * Performs processing on a value before it is used to set + * the parameter of a IDbCommand. + * @param object The interface for setting the value. + * @param object The value to be set. + */ + public abstract function getParameter($object); + + + /** + * Performs processing on a value before after it has been retrieved + * from a database + * @param object The interface for getting the value. + * @return mixed The processed value. + */ + public abstract function getResult($string); + + + /** + * Casts the string representation of a value into a type recognized by + * this type handler. This method is used to translate nullValue values + * into types that can be appropriately compared. If your custom type handler + * cannot support nullValues, or if there is no reasonable string representation + * for this type (e.g. File type), you can simply return the String representation + * as it was passed in. It is not recommended to return null, unless null was passed + * in. + * @param array result row. + * @return mixed + */ + public abstract function createNewInstance($row=null); +} + diff --git a/framework/Data/SqlMap/Statements/IMappedStatement.php b/framework/Data/SqlMap/Statements/IMappedStatement.php index dc628c9e..15f61fad 100644 --- a/framework/Data/SqlMap/Statements/IMappedStatement.php +++ b/framework/Data/SqlMap/Statements/IMappedStatement.php @@ -1,82 +1,82 @@ -<?php
-/**
- * IMappedStatement interface file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.SqlMap.Statements
- */
-
-/**
- * Interface for all mapping statements.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.SqlMap.Statements
- * @since 3.1
- */
-interface IMappedStatement
-{
- /**
- * @return string Name used to identify the MappedStatement amongst the others.
- */
- public function getID();
-
- /**
- * @return TSqlMapStatement The SQL statment used by this TMappedStatement.
- */
- public function getStatement();
-
- /**
- * @return TSqlMap The TSqlMap used by this TMappedStatement
- */
- public function getManager();
-
- /**
- * Executes the SQL and retuns all rows selected in a map that is keyed on
- * the property named in the <tt>$keyProperty</tt> parameter. The value at
- * each key will be the value of the property specified in the
- * <tt>$valueProperty</tt> parameter. If <tt>$valueProperty</tt> is
- * <tt>null</tt>, the entire result object will be entered.
- * @param IDbConnection database connection to execute the query
- * @param mixed The object used to set the parameters in the SQL.
- * @param string The property of the result object to be used as the key.
- * @param string The property of the result object to be used as the value (or null)
- * @return TMap A map of object containing the rows keyed by <tt>$keyProperty</tt>.
- */
- public function executeQueryForMap($connection, $parameter, $keyProperty, $valueProperty=null);
-
-
- /**
- * Execute an update statement. Also used for delete statement. Return the
- * number of row effected.
- * @param IDbConnection database connection to execute the query
- * @param mixed The object used to set the parameters in the SQL.
- * @return integer The number of row effected.
- */
- public function executeUpdate($connection, $parameter);
-
-
- /**
- * Executes the SQL and retuns a subset of the rows selected.
- * @param IDbConnection database connection to execute the query
- * @param mixed The object used to set the parameters in the SQL.
- * @param TList A list to populate the result with.
- * @param integer The number of rows to skip over.
- * @param integer The maximum number of rows to return.
- * @return TList A TList of result objects.
- */
- public function executeQueryForList($connection, $parameter, $result=null, $skip=-1, $max=-1);
-
-
- /**
- * Executes an SQL statement that returns a single row as an object
- * of the type of the <tt>$result</tt> passed in as a parameter.
- * @param IDbConnection database connection to execute the query
- * @param mixed The object used to set the parameters in the SQL.
- * @param object The result object.
- * @return object result.
- */
- public function executeQueryForObject($connection,$parameter, $result=null);
-}
-
+<?php +/** + * IMappedStatement interface file. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.SqlMap.Statements + */ + +/** + * Interface for all mapping statements. + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.SqlMap.Statements + * @since 3.1 + */ +interface IMappedStatement +{ + /** + * @return string Name used to identify the MappedStatement amongst the others. + */ + public function getID(); + + /** + * @return TSqlMapStatement The SQL statment used by this TMappedStatement. + */ + public function getStatement(); + + /** + * @return TSqlMap The TSqlMap used by this TMappedStatement + */ + public function getManager(); + + /** + * Executes the SQL and retuns all rows selected in a map that is keyed on + * the property named in the <tt>$keyProperty</tt> parameter. The value at + * each key will be the value of the property specified in the + * <tt>$valueProperty</tt> parameter. If <tt>$valueProperty</tt> is + * <tt>null</tt>, the entire result object will be entered. + * @param IDbConnection database connection to execute the query + * @param mixed The object used to set the parameters in the SQL. + * @param string The property of the result object to be used as the key. + * @param string The property of the result object to be used as the value (or null) + * @return TMap A map of object containing the rows keyed by <tt>$keyProperty</tt>. + */ + public function executeQueryForMap($connection, $parameter, $keyProperty, $valueProperty=null); + + + /** + * Execute an update statement. Also used for delete statement. Return the + * number of row effected. + * @param IDbConnection database connection to execute the query + * @param mixed The object used to set the parameters in the SQL. + * @return integer The number of row effected. + */ + public function executeUpdate($connection, $parameter); + + + /** + * Executes the SQL and retuns a subset of the rows selected. + * @param IDbConnection database connection to execute the query + * @param mixed The object used to set the parameters in the SQL. + * @param TList A list to populate the result with. + * @param integer The number of rows to skip over. + * @param integer The maximum number of rows to return. + * @return TList A TList of result objects. + */ + public function executeQueryForList($connection, $parameter, $result=null, $skip=-1, $max=-1); + + + /** + * Executes an SQL statement that returns a single row as an object + * of the type of the <tt>$result</tt> passed in as a parameter. + * @param IDbConnection database connection to execute the query + * @param mixed The object used to set the parameters in the SQL. + * @param object The result object. + * @return object result. + */ + public function executeQueryForObject($connection,$parameter, $result=null); +} + diff --git a/framework/Data/SqlMap/Statements/TCachingStatement.php b/framework/Data/SqlMap/Statements/TCachingStatement.php index 54664e37..cac84458 100644 --- a/framework/Data/SqlMap/Statements/TCachingStatement.php +++ b/framework/Data/SqlMap/Statements/TCachingStatement.php @@ -1,108 +1,108 @@ -<?php
-/**
- * TCachingStatement class file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.SqlMap.Statements
- */
-
-/**
- * TCacheingStatement class.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.SqlMap.Statements
- * @since 3.1
- */
-class TCachingStatement extends TComponent implements IMappedStatement
-{
- private $_mappedStatement;
-
- public function __construct(TMappedStatement $statement)
- {
- $this->_mappedStatement = $statement;
- }
-
- public function getID()
- {
- return $this->_mappedStatement->getID();
- }
-
- public function getStatement()
- {
- return $this->_mappedStatement->getStatement();
- }
-
- public function getManager()
- {
- return $this->_mappedStatement->getManager();
- }
-
- public function executeQueryForMap($connection, $parameter,$keyProperty, $valueProperty=null, $skip=-1, $max=-1,$delegate=null)
- {
- $sql = $this->createCommand($connection, $parameter, $skip, $max);
- $key = $this->getCacheKey(array(clone($sql), $keyProperty, $valueProperty,$skip, $max));
- $map = $this->getStatement()->getCache()->get($key);
- if($map===null)
- {
- $map = $this->_mappedStatement->runQueryForMap(
- $connection, $parameter, $sql, $keyProperty, $valueProperty, $delegate);
- $this->getStatement()->getCache()->set($key, $map);
- }
- return $map;
- }
-
- public function executeUpdate($connection, $parameter)
- {
- return $this->_mappedStatement->executeUpdate($connection, $parameter);
- }
-
- public function executeInsert($connection, $parameter)
- {
- return $this->executeInsert($connection, $parameter);
- }
-
- public function executeQueryForList($connection, $parameter, $result=null, $skip=-1, $max=-1, $delegate=null)
- {
- $sql = $this->createCommand($connection, $parameter, $skip, $max);
- $key = $this->getCacheKey(array(clone($sql), $parameter, $skip, $max));
- $list = $this->getStatement()->getCache()->get($key);
- if($list===null)
- {
- $list = $this->_mappedStatement->runQueryForList(
- $connection, $parameter, $sql, $result, $delegate);
- $this->getStatement()->getCache()->set($key, $list);
- }
- return $list;
- }
-
- public function executeQueryForObject($connection, $parameter, $result=null)
- {
- $sql = $this->createCommand($connection, $parameter);
- $key = $this->getCacheKey(array(clone($sql), $parameter));
- $object = $this->getStatement()->getCache()->get($key);
- if($object===null)
- {
- $object = $this->_mappedStatement->runQueryForObject($connection, $sql, $result);
- $this->getStatement()->getCache()->set($key, $object);
- }
- return $object;
- }
-
- protected function getCacheKey($object)
- {
- $cacheKey = new TSqlMapCacheKey($object);
- return $cacheKey->getHash();
- }
-
- protected function createCommand($connection, $parameter, $skip=null, $max=null)
- {
- return $this->_mappedStatement->getCommand()->create($this->getManager(),
- $connection, $this->getStatement(), $parameter, $skip, $max);
- }
-}
-
+<?php +/** + * TCachingStatement class file. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.SqlMap.Statements + */ + +/** + * TCacheingStatement class. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.SqlMap.Statements + * @since 3.1 + */ +class TCachingStatement extends TComponent implements IMappedStatement +{ + private $_mappedStatement; + + public function __construct(TMappedStatement $statement) + { + $this->_mappedStatement = $statement; + } + + public function getID() + { + return $this->_mappedStatement->getID(); + } + + public function getStatement() + { + return $this->_mappedStatement->getStatement(); + } + + public function getManager() + { + return $this->_mappedStatement->getManager(); + } + + public function executeQueryForMap($connection, $parameter,$keyProperty, $valueProperty=null, $skip=-1, $max=-1,$delegate=null) + { + $sql = $this->createCommand($connection, $parameter, $skip, $max); + $key = $this->getCacheKey(array(clone($sql), $keyProperty, $valueProperty,$skip, $max)); + $map = $this->getStatement()->getCache()->get($key); + if($map===null) + { + $map = $this->_mappedStatement->runQueryForMap( + $connection, $parameter, $sql, $keyProperty, $valueProperty, $delegate); + $this->getStatement()->getCache()->set($key, $map); + } + return $map; + } + + public function executeUpdate($connection, $parameter) + { + return $this->_mappedStatement->executeUpdate($connection, $parameter); + } + + public function executeInsert($connection, $parameter) + { + return $this->executeInsert($connection, $parameter); + } + + public function executeQueryForList($connection, $parameter, $result=null, $skip=-1, $max=-1, $delegate=null) + { + $sql = $this->createCommand($connection, $parameter, $skip, $max); + $key = $this->getCacheKey(array(clone($sql), $parameter, $skip, $max)); + $list = $this->getStatement()->getCache()->get($key); + if($list===null) + { + $list = $this->_mappedStatement->runQueryForList( + $connection, $parameter, $sql, $result, $delegate); + $this->getStatement()->getCache()->set($key, $list); + } + return $list; + } + + public function executeQueryForObject($connection, $parameter, $result=null) + { + $sql = $this->createCommand($connection, $parameter); + $key = $this->getCacheKey(array(clone($sql), $parameter)); + $object = $this->getStatement()->getCache()->get($key); + if($object===null) + { + $object = $this->_mappedStatement->runQueryForObject($connection, $sql, $result); + $this->getStatement()->getCache()->set($key, $object); + } + return $object; + } + + protected function getCacheKey($object) + { + $cacheKey = new TSqlMapCacheKey($object); + return $cacheKey->getHash(); + } + + protected function createCommand($connection, $parameter, $skip=null, $max=null) + { + return $this->_mappedStatement->getCommand()->create($this->getManager(), + $connection, $this->getStatement(), $parameter, $skip, $max); + } +} + diff --git a/framework/Data/SqlMap/Statements/TDeleteMappedStatement.php b/framework/Data/SqlMap/Statements/TDeleteMappedStatement.php index a3cbaadb..562720ed 100644 --- a/framework/Data/SqlMap/Statements/TDeleteMappedStatement.php +++ b/framework/Data/SqlMap/Statements/TDeleteMappedStatement.php @@ -1,24 +1,24 @@ -<?php
-/**
- * TDeleteMappedStatement class file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
+<?php +/** + * TDeleteMappedStatement class file. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.SqlMap.Statements
- */
-
-/**
- * TDeleteMappedStatement class.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.SqlMap.Statements
- * @since 3.1
- */
-class TDeleteMappedStatement extends TUpdateMappedStatement
-{
-}
-
+ * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.SqlMap.Statements + */ + +/** + * TDeleteMappedStatement class. + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.SqlMap.Statements + * @since 3.1 + */ +class TDeleteMappedStatement extends TUpdateMappedStatement +{ +} + diff --git a/framework/Data/SqlMap/Statements/TInsertMappedStatement.php b/framework/Data/SqlMap/Statements/TInsertMappedStatement.php index c7cd6ff6..e91ca3aa 100644 --- a/framework/Data/SqlMap/Statements/TInsertMappedStatement.php +++ b/framework/Data/SqlMap/Statements/TInsertMappedStatement.php @@ -1,49 +1,49 @@ -<?php
-/**
- * TInsertMappedStatement class file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
+<?php +/** + * TInsertMappedStatement class file. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.SqlMap.Statements
- */
-
-/**
- * TInsertMappedStatement class.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.SqlMap.Statements
- * @since 3.1
- */
-class TInsertMappedStatement extends TMappedStatement
-{
- public function executeQueryForMap($connection, $parameter,
- $keyProperty, $valueProperty=null)
- {
- throw new TSqlMapExecutionException(
- 'sqlmap_cannot_execute_query_for_map', get_class($this), $this->getID());
- }
-
- public function executeUpdate($connection, $parameter)
- {
- throw new TSqlMapExecutionException(
- 'sqlmap_cannot_execute_update', get_class($this), $this->getID());
- }
-
- public function executeQueryForList($connection, $parameter, $result=null,
- $skip=-1, $max=-1)
- {
- throw new TSqlMapExecutionException(
- 'sqlmap_cannot_execute_query_for_list', get_class($this), $this->getID());
- }
-
- public function executeQueryForObject($connection, $parameter, $result=null)
- {
- throw new TSqlMapExecutionException(
- 'sqlmap_cannot_execute_query_for_object', get_class($this), $this->getID());
- }
-}
-
+ * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.SqlMap.Statements + */ + +/** + * TInsertMappedStatement class. + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.SqlMap.Statements + * @since 3.1 + */ +class TInsertMappedStatement extends TMappedStatement +{ + public function executeQueryForMap($connection, $parameter, + $keyProperty, $valueProperty=null) + { + throw new TSqlMapExecutionException( + 'sqlmap_cannot_execute_query_for_map', get_class($this), $this->getID()); + } + + public function executeUpdate($connection, $parameter) + { + throw new TSqlMapExecutionException( + 'sqlmap_cannot_execute_update', get_class($this), $this->getID()); + } + + public function executeQueryForList($connection, $parameter, $result=null, + $skip=-1, $max=-1) + { + throw new TSqlMapExecutionException( + 'sqlmap_cannot_execute_query_for_list', get_class($this), $this->getID()); + } + + public function executeQueryForObject($connection, $parameter, $result=null) + { + throw new TSqlMapExecutionException( + 'sqlmap_cannot_execute_query_for_object', get_class($this), $this->getID()); + } +} + diff --git a/framework/Data/SqlMap/Statements/TMappedStatement.php b/framework/Data/SqlMap/Statements/TMappedStatement.php index 0dbc8def..ee54df95 100644 --- a/framework/Data/SqlMap/Statements/TMappedStatement.php +++ b/framework/Data/SqlMap/Statements/TMappedStatement.php @@ -1,1242 +1,1242 @@ -<?php
-/**
- * TMappedStatement and related classes.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.SqlMap.Statements
- */
-
-/**
- * TMappedStatement class executes SQL mapped statements. Mapped Statements can
- * hold any SQL statement and use Parameter Maps and Result Maps for input and output.
- *
- * This class is usualy instantiated during SQLMap configuration by TSqlDomBuilder.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.SqlMap.Statements
- * @since 3.0
- */
-class TMappedStatement extends TComponent implements IMappedStatement
-{
- /**
- * @var TSqlMapStatement current SQL statement.
- */
- private $_statement;
-
- /**
- * @var TPreparedCommand SQL command prepareer
- */
- private $_command;
-
- /**
- * @var TSqlMapper sqlmap used by this mapper.
- */
- private $_manager;
-
- /**
- * @var TPostSelectBinding[] post select statement queue.
- */
- private $_selectQueue=array();
-
- /**
- * @var boolean true when data is mapped to a particular row.
- */
- private $_IsRowDataFound = false;
-
- /**
- * @var TSQLMapObjectCollectionTree group by object collection tree
- */
- private $_groupBy;
-
- /**
- * @var Post select is to query for list.
- */
- const QUERY_FOR_LIST = 0;
-
- /**
- * @var Post select is to query for list.
- */
- const QUERY_FOR_ARRAY = 1;
-
- /**
- * @var Post select is to query for object.
- */
- const QUERY_FOR_OBJECT = 2;
-
- /**
- * @return string Name used to identify the TMappedStatement amongst the others.
- * This the name of the SQL statement by default.
- */
- public function getID()
- {
- return $this->_statement->ID;
- }
-
- /**
- * @return TSqlMapStatement The SQL statment used by this MappedStatement
- */
- public function getStatement()
- {
- return $this->_statement;
- }
-
- /**
- * @return TSqlMapper The SqlMap used by this MappedStatement
- */
- public function getManager()
- {
- return $this->_manager;
- }
-
- /**
- * @return TPreparedCommand command to prepare SQL statements.
- */
- public function getCommand()
- {
- return $this->_command;
- }
-
- /**
- * Empty the group by results cache.
- */
- protected function initialGroupByResults()
- {
- $this->_groupBy = new TSqlMapObjectCollectionTree();
- }
-
- /**
- * Creates a new mapped statement.
- * @param TSqlMapper an sqlmap.
- * @param TSqlMapStatement An SQL statement.
- */
- public function __construct(TSqlMapManager $sqlMap, TSqlMapStatement $statement)
- {
- $this->_manager = $sqlMap;
- $this->_statement = $statement;
- $this->_command = new TPreparedCommand();
- $this->initialGroupByResults();
- }
-
- public function getSqlString()
- {
- return $this->getStatement()->getSqlText()->getPreparedStatement()->getPreparedSql();
- }
-
- /**
- * Execute SQL Query.
- * @param IDbConnection database connection
- * @param array SQL statement and parameters.
- * @return mixed record set if applicable.
- * @throws TSqlMapExecutionException if execution error or false record set.
- * @throws TSqlMapQueryExecutionException if any execution error
- */
-/* protected function executeSQLQuery($connection, $sql)
- {
- try
- {
- if(!($recordSet = $connection->execute($sql['sql'],$sql['parameters'])))
- {
- throw new TSqlMapExecutionException(
- 'sqlmap_execution_error_no_record', $this->getID(),
- $connection->ErrorMsg());
- }
- return $recordSet;
- }
- catch (Exception $e)
- {
- throw new TSqlMapQueryExecutionException($this->getStatement(), $e);
- }
- }*/
-
- /**
- * Execute SQL Query with limits.
- * @param IDbConnection database connection
- * @param array SQL statement and parameters.
- * @return mixed record set if applicable.
- * @throws TSqlMapExecutionException if execution error or false record set.
- * @throws TSqlMapQueryExecutionException if any execution error
- */
- protected function executeSQLQueryLimit($connection, $command, $max, $skip)
- {
- if($max>-1 || $skip > -1)
- {
- $maxStr=$max>0?' LIMIT '.$max:'';
- $skipStr=$skip>0?' OFFSET '.$skip:'';
- $command->setText($command->getText().$maxStr.$skipStr);
- }
- $connection->setActive(true);
- return $command->query();
-
- /*//var_dump($command);
- try
- {
- $recordSet = $connection->selectLimit($sql['sql'],$max,$skip,$sql['parameters']);
- if(!$recordSet)
- {
- throw new TSqlMapExecutionException(
- 'sqlmap_execution_error_query_for_list',
- $connection->ErrorMsg());
- }
- return $recordSet;
- }
- catch (Exception $e)
- {
- throw new TSqlMapQueryExecutionException($this->getStatement(), $e);
- }*/
- }
-
- /**
- * Executes the SQL and retuns a List of result objects.
- * @param IDbConnection database connection
- * @param mixed The object used to set the parameters in the SQL.
- * @param object result collection object.
- * @param integer The number of rows to skip over.
- * @param integer The maximum number of rows to return.
- * @return array a list of result objects
- * @param callback row delegate handler
- * @see executeQueryForList()
- */
- public function executeQueryForList($connection, $parameter, $result=null, $skip=-1, $max=-1, $delegate=null)
- {
- $sql = $this->_command->create($this->_manager, $connection, $this->_statement, $parameter,$skip,$max);
- return $this->runQueryForList($connection, $parameter, $sql, $result, $delegate);
- }
-
- /**
- * Executes the SQL and retuns a List of result objects.
- *
- * This method should only be called by internal developers, consider using
- * <tt>executeQueryForList()</tt> first.
- *
- * @param IDbConnection database connection
- * @param mixed The object used to set the parameters in the SQL.
- * @param array SQL string and subsititution parameters.
- * @param object result collection object.
- * @param integer The number of rows to skip over.
- * @param integer The maximum number of rows to return.
- * @param callback row delegate handler
- * @return array a list of result objects
- * @see executeQueryForList()
- */
- public function runQueryForList($connection, $parameter, $sql, $result, $delegate=null)
- {
- $registry=$this->getManager()->getTypeHandlers();
- $list = $result instanceof ArrayAccess ? $result :
- $this->_statement->createInstanceOfListClass($registry);
- $connection->setActive(true);
- $reader = $sql->query();
- //$reader = $this->executeSQLQueryLimit($connection, $sql, $max, $skip);
- if($delegate!==null)
- {
- foreach($reader as $row)
- {
- $obj = $this->applyResultMap($row);
- $param = new TResultSetListItemParameter($obj, $parameter, $list);
- $this->raiseRowDelegate($delegate, $param);
- }
- }
- else
- {
- //var_dump($sql,$parameter);
- foreach($reader as $row)
- {
-// var_dump($row);
- $list[] = $this->applyResultMap($row);
- }
- }
-
- if(!$this->_groupBy->isEmpty())
- {
- $list = $this->_groupBy->collect();
- $this->initialGroupByResults();
- }
-
- $this->executePostSelect($connection);
- $this->onExecuteQuery($sql);
-
- return $list;
- }
-
- /**
- * Executes the SQL and retuns all rows selected in a map that is keyed on
- * the property named in the keyProperty parameter. The value at each key
- * will be the value of the property specified in the valueProperty parameter.
- * If valueProperty is null, the entire result object will be entered.
- * @param IDbConnection database connection
- * @param mixed The object used to set the parameters in the SQL.
- * @param string The property of the result object to be used as the key.
- * @param string The property of the result object to be used as the value (or null).
- * @param callback row delegate handler
- * @return array An array of object containing the rows keyed by keyProperty.
- */
- public function executeQueryForMap($connection, $parameter, $keyProperty, $valueProperty=null, $skip=-1, $max=-1, $delegate=null)
- {
- $sql = $this->_command->create($this->_manager, $connection, $this->_statement, $parameter, $skip, $max);
- return $this->runQueryForMap($connection, $parameter, $sql, $keyProperty, $valueProperty, $delegate);
- }
-
- /**
- * Executes the SQL and retuns all rows selected in a map that is keyed on
- * the property named in the keyProperty parameter. The value at each key
- * will be the value of the property specified in the valueProperty parameter.
- * If valueProperty is null, the entire result object will be entered.
- *
- * This method should only be called by internal developers, consider using
- * <tt>executeQueryForMap()</tt> first.
- *
- * @param IDbConnection database connection
- * @param mixed The object used to set the parameters in the SQL.
- * @param array SQL string and subsititution parameters.
- * @param string The property of the result object to be used as the key.
- * @param string The property of the result object to be used as the value (or null).
- * @param callback row delegate, a callback function
- * @return array An array of object containing the rows keyed by keyProperty.
- * @see executeQueryForMap()
- */
- public function runQueryForMap($connection, $parameter, $command, $keyProperty, $valueProperty=null, $delegate=null)
- {
- $map = array();
- //$recordSet = $this->executeSQLQuery($connection, $sql);
- $connection->setActive(true);
- $reader = $command->query();
- if($delegate!==null)
- {
- //while($row = $recordSet->fetchRow())
- foreach($reader as $row)
- {
- $obj = $this->applyResultMap($row);
- $key = TPropertyAccess::get($obj, $keyProperty);
- $value = ($valueProperty===null) ? $obj :
- TPropertyAccess::get($obj, $valueProperty);
- $param = new TResultSetMapItemParameter($key, $value, $parameter, $map);
- $this->raiseRowDelegate($delegate, $param);
- }
- }
- else
- {
- //while($row = $recordSet->fetchRow())
- foreach($reader as $row)
- {
- $obj = $this->applyResultMap($row);
- $key = TPropertyAccess::get($obj, $keyProperty);
- $map[$key] = ($valueProperty===null) ? $obj :
- TPropertyAccess::get($obj, $valueProperty);
- }
- }
- $this->onExecuteQuery($command);
- return $map;
- }
-
- /**
- * Raises delegate handler.
- * This method is invoked for each new list item. It is the responsibility
- * of the handler to add the item to the list.
- * @param object event parameter
- */
- protected function raiseRowDelegate($handler, $param)
- {
- if(is_string($handler))
- {
- call_user_func($handler,$this,$param);
- }
- else if(is_callable($handler,true))
- {
- // an array: 0 - object, 1 - method name/path
- list($object,$method)=$handler;
- if(is_string($object)) // static method call
- call_user_func($handler,$this,$param);
- else
- {
- if(($pos=strrpos($method,'.'))!==false)
- {
- $object=$this->getSubProperty(substr($method,0,$pos));
- $method=substr($method,$pos+1);
- }
- $object->$method($this,$param);
- }
- }
- else
- throw new TInvalidDataValueException('sqlmap_invalid_delegate', $this->getID(), $handler);
- }
-
- /**
- * Executes an SQL statement that returns a single row as an object of the
- * type of the <tt>$result</tt> passed in as a parameter.
- * @param IDbConnection database connection
- * @param mixed The parameter data (object, arrary, primitive) used to set the parameters in the SQL
- * @param mixed The result object.
- * @return ${return}
- */
- public function executeQueryForObject($connection, $parameter, $result=null)
- {
- $sql = $this->_command->create($this->_manager, $connection, $this->_statement, $parameter);
- return $this->runQueryForObject($connection, $sql, $result);
- }
-
- /**
- * Executes an SQL statement that returns a single row as an object of the
- * type of the <tt>$result</tt> passed in as a parameter.
- *
- * This method should only be called by internal developers, consider using
- * <tt>executeQueryForObject()</tt> first.
- *
- * @param IDbConnection database connection
- * @param array SQL string and subsititution parameters.
- * @param object The result object.
- * @return object the object.
- * @see executeQueryForObject()
- */
- public function runQueryForObject($connection, $command, &$result)
- {
- $object = null;
- $connection->setActive(true);
- foreach($command->query() as $row)
- $object = $this->applyResultMap($row, $result);
-
- if(!$this->_groupBy->isEmpty())
- {
- $list = $this->_groupBy->collect();
- $this->initialGroupByResults();
- $object = $list[0];
- }
-
- $this->executePostSelect($connection);
- $this->onExecuteQuery($command);
-
- return $object;
- }
-
- /**
- * Execute an insert statement. Fill the parameter object with the ouput
- * parameters if any, also could return the insert generated key.
- * @param IDbConnection database connection
- * @param mixed The parameter object used to fill the statement.
- * @return string the insert generated key.
- */
- public function executeInsert($connection, $parameter)
- {
- $generatedKey = $this->getPreGeneratedSelectKey($connection, $parameter);
-
- $command = $this->_command->create($this->_manager, $connection, $this->_statement, $parameter);
-// var_dump($command,$parameter);
- $result = $command->execute();
-
- if($generatedKey===null)
- $generatedKey = $this->getPostGeneratedSelectKey($connection, $parameter);
-
- $this->executePostSelect($connection);
- $this->onExecuteQuery($command);
- return $generatedKey;
- }
-
- /**
- * Gets the insert generated ID before executing an insert statement.
- * @param IDbConnection database connection
- * @param mixed insert statement parameter.
- * @return string new insert ID if pre-select key statement was executed, null otherwise.
- */
- protected function getPreGeneratedSelectKey($connection, $parameter)
- {
- if($this->_statement instanceof TSqlMapInsert)
- {
- $selectKey = $this->_statement->getSelectKey();
- if(($selectKey!==null) && !$selectKey->getIsAfter())
- return $this->executeSelectKey($connection, $parameter, $selectKey);
- }
- }
-
- /**
- * Gets the inserted row ID after executing an insert statement.
- * @param IDbConnection database connection
- * @param mixed insert statement parameter.
- * @return string last insert ID, null otherwise.
- */
- protected function getPostGeneratedSelectKey($connection, $parameter)
- {
- if($this->_statement instanceof TSqlMapInsert)
- {
- $selectKey = $this->_statement->getSelectKey();
- if(($selectKey!==null) && $selectKey->getIsAfter())
- return $this->executeSelectKey($connection, $parameter, $selectKey);
- }
- }
-
- /**
- * Execute the select key statement, used to obtain last insert ID.
- * @param IDbConnection database connection
- * @param mixed insert statement parameter
- * @param TSqlMapSelectKey select key statement
- * @return string last insert ID.
- */
- protected function executeSelectKey($connection, $parameter, $selectKey)
- {
- $mappedStatement = $this->getManager()->getMappedStatement($selectKey->getID());
- $generatedKey = $mappedStatement->executeQueryForObject(
- $connection, $parameter, null);
- if(strlen($prop = $selectKey->getProperty()) > 0)
- TPropertyAccess::set($parameter, $prop, $generatedKey);
- return $generatedKey;
- }
-
- /**
- * Execute an update statement. Also used for delete statement.
- * Return the number of rows effected.
- * @param IDbConnection database connection
- * @param mixed The object used to set the parameters in the SQL.
- * @return integer The number of rows effected.
- */
- public function executeUpdate($connection, $parameter)
- {
- $sql = $this->_command->create($this->getManager(),$connection, $this->_statement, $parameter);
- $affectedRows = $sql->execute();
- //$this->executeSQLQuery($connection, $sql);
- $this->executePostSelect($connection);
- $this->onExecuteQuery($sql);
- return $affectedRows;
- }
-
- /**
- * Process 'select' result properties
- * @param IDbConnection database connection
- */
- protected function executePostSelect($connection)
- {
- while(count($this->_selectQueue))
- {
- $postSelect = array_shift($this->_selectQueue);
- $method = $postSelect->getMethod();
- $statement = $postSelect->getStatement();
- $property = $postSelect->getResultProperty()->getProperty();
- $keys = $postSelect->getKeys();
- $resultObject = $postSelect->getResultObject();
-
- if($method == self::QUERY_FOR_LIST || $method == self::QUERY_FOR_ARRAY)
- {
- $values = $statement->executeQueryForList($connection, $keys, null);
-
- if($method == self::QUERY_FOR_ARRAY)
- $values = $values->toArray();
- TPropertyAccess::set($resultObject, $property, $values);
- }
- else if($method == self::QUERY_FOR_OBJECT)
- {
- $value = $statement->executeQueryForObject($connection, $keys, null);
- TPropertyAccess::set($resultObject, $property, $value);
- }
- }
- }
-
- /**
- * Raise the execute query event.
- * @param array prepared SQL statement and subsititution parameters
- */
- public function onExecuteQuery($sql)
- {
- $this->raiseEvent('OnExecuteQuery', $this, $sql);
- }
-
- /**
- * Apply result mapping.
- * @param array a result set row retrieved from the database
- * @param object the result object, will create if necessary.
- * @return object the result filled with data, null if not filled.
- */
- protected function applyResultMap($row, &$resultObject=null)
- {
- if($row === false) return null;
-
- $resultMapName = $this->_statement->getResultMap();
- $resultClass = $this->_statement->getResultClass();
-
- $obj=null;
- if($this->getManager()->getResultMaps()->contains($resultMapName))
- $obj = $this->fillResultMap($resultMapName, $row, null, $resultObject);
- else if(strlen($resultClass) > 0)
- $obj = $this->fillResultClass($resultClass, $row, $resultObject);
- else
- $obj = $this->fillDefaultResultMap(null, $row, $resultObject);
- if(class_exists('TActiveRecord',false) && $obj instanceof TActiveRecord)
- //Create a new clean active record.
- $obj=TActiveRecord::createRecord(get_class($obj),$obj);
- return $obj;
- }
-
- /**
- * Fill the result using ResultClass, will creates new result object if required.
- * @param string result object class name
- * @param array a result set row retrieved from the database
- * @param object the result object, will create if necessary.
- * @return object result object filled with data
- */
- protected function fillResultClass($resultClass, $row, $resultObject)
- {
- if($resultObject===null)
- {
- $registry = $this->getManager()->getTypeHandlers();
- $resultObject = $this->_statement->createInstanceOfResultClass($registry,$row);
- }
-
- if($resultObject instanceOf ArrayAccess)
- return $this->fillResultArrayList($row, $resultObject);
- else if(is_object($resultObject))
- return $this->fillResultObjectProperty($row, $resultObject);
- else
- return $this->fillDefaultResultMap(null, $row, $resultObject);
- }
-
- /**
- * Apply the result to a TList or an array.
- * @param array a result set row retrieved from the database
- * @param object result object, array or list
- * @return object result filled with data.
- */
- protected function fillResultArrayList($row, $resultObject)
- {
- if($resultObject instanceof TList)
- foreach($row as $v)
- $resultObject[] = $v;
- else
- foreach($row as $k => $v)
- $resultObject[$k] = $v;
- return $resultObject;
- }
-
- /**
- * Apply the result to an object.
- * @param array a result set row retrieved from the database
- * @param object result object, array or list
- * @return object result filled with data.
- */
- protected function fillResultObjectProperty($row, $resultObject)
- {
- $index = 0;
- $registry=$this->getManager()->getTypeHandlers();
- foreach($row as $k=>$v)
- {
- $property = new TResultProperty;
- if(is_string($k) && strlen($k) > 0)
- $property->setColumn($k);
- $property->setColumnIndex(++$index);
- $type = gettype(TPropertyAccess::get($resultObject,$k));
- $property->setType($type);
- $value = $property->getPropertyValue($registry,$row);
- TPropertyAccess::set($resultObject, $k,$value);
- }
- return $resultObject;
- }
-
- /**
- * Fills the result object according to result mappings.
- * @param string result map name.
- * @param array a result set row retrieved from the database
- * @param object result object to fill, will create new instances if required.
- * @return object result object filled with data.
- */
- protected function fillResultMap($resultMapName, $row, $parentGroup=null, &$resultObject=null)
- {
- $resultMap = $this->getManager()->getResultMap($resultMapName);
- $registry = $this->getManager()->getTypeHandlers();
- $resultMap = $resultMap->resolveSubMap($registry,$row);
-
- if($resultObject===null)
- $resultObject = $resultMap->createInstanceOfResult($registry);
-
- if(is_object($resultObject))
- {
- if(strlen($resultMap->getGroupBy()) > 0)
- return $this->addResultMapGroupBy($resultMap, $row, $parentGroup, $resultObject);
- else
- foreach($resultMap->getColumns() as $property)
- $this->setObjectProperty($resultMap, $property, $row, $resultObject);
- }
- else
- {
- $resultObject = $this->fillDefaultResultMap($resultMap, $row, $resultObject);
- }
- return $resultObject;
- }
-
- /**
- * ResultMap with GroupBy property. Save object collection graph in a tree
- * and collect the result later.
- * @param TResultMap result mapping details.
- * @param array a result set row retrieved from the database
- * @param object the result object
- * @return object result object.
- */
- protected function addResultMapGroupBy($resultMap, $row, $parent, &$resultObject)
- {
- $group = $this->getResultMapGroupKey($resultMap, $row);
-
- if(empty($parent))
- {
- $rootObject = array('object'=>$resultObject, 'property' => null);
- $this->_groupBy->add(null, $group, $rootObject);
- }
-
- foreach($resultMap->getColumns() as $property)
- {
- //set properties.
- $this->setObjectProperty($resultMap, $property, $row, $resultObject);
- $nested = $property->getResultMapping();
-
- //nested property
- if($this->getManager()->getResultMaps()->contains($nested))
- {
- $nestedMap = $this->getManager()->getResultMap($nested);
- $groupKey = $this->getResultMapGroupKey($nestedMap, $row);
-
- //add the node reference first
- if(empty($parent))
- $this->_groupBy->add($group, $groupKey, '');
-
- //get the nested result mapping value
- $value = $this->fillResultMap($nested, $row, $groupKey);
-
- //add it to the object tree graph
- $groupObject = array('object'=>$value, 'property' => $property->getProperty());
- if(empty($parent))
- $this->_groupBy->add($group, $groupKey, $groupObject);
- else
- $this->_groupBy->add($parent, $groupKey, $groupObject);
- }
- }
- return $resultObject;
- }
-
- /**
- * Gets the result 'group by' groupping key for each row.
- * @param TResultMap result mapping details.
- * @param array a result set row retrieved from the database
- * @return string groupping key.
- */
- protected function getResultMapGroupKey($resultMap, $row)
- {
- $groupBy = $resultMap->getGroupBy();
- if(isset($row[$groupBy]))
- return $resultMap->getID().$row[$groupBy];
- else
- return $resultMap->getID().crc32(serialize($row));
- }
-
- /**
- * Fill the result map using default settings. If <tt>$resultMap</tt> is null
- * the result object returned will be guessed from <tt>$resultObject</tt>.
- * @param TResultMap result mapping details.
- * @param array a result set row retrieved from the database
- * @param object the result object
- * @return mixed the result object filled with data.
- */
- protected function fillDefaultResultMap($resultMap, $row, $resultObject)
- {
- if($resultObject===null)
- $resultObject='';
-
- if($resultMap!==null)
- $result = $this->fillArrayResultMap($resultMap, $row, $resultObject);
- else
- $result = $row;
-
- //if scalar result types
- if(count($result) == 1 && ($type = gettype($resultObject))!= 'array')
- return $this->getScalarResult($result, $type);
- else
- return $result;
- }
-
- /**
- * Retrieve the result map as an array.
- * @param TResultMap result mapping details.
- * @param array a result set row retrieved from the database
- * @param object the result object
- * @return array array list of result objects.
- */
- protected function fillArrayResultMap($resultMap, $row, $resultObject)
- {
- $result = array();
- $registry=$this->getManager()->getTypeHandlers();
- foreach($resultMap->getColumns() as $column)
- {
- if(($column->getType()===null)
- && ($resultObject!==null) && !is_object($resultObject))
- $column->setType(gettype($resultObject));
- $result[$column->getProperty()] = $column->getPropertyValue($registry,$row);
- }
- return $result;
- }
-
- /**
- * Converts the first array value to scalar value of given type.
- * @param array list of results
- * @param string scalar type.
- * @return mixed scalar value.
- */
- protected function getScalarResult($result, $type)
- {
- $scalar = array_shift($result);
- settype($scalar, $type);
- return $scalar;
- }
-
- /**
- * Set a property of the result object with appropriate value.
- * @param TResultMap result mapping details.
- * @param TResultProperty the result property to fill.
- * @param array a result set row retrieved from the database
- * @param object the result object
- */
- protected function setObjectProperty($resultMap, $property, $row, &$resultObject)
- {
- $select = $property->getSelect();
- $key = $property->getProperty();
- $nested = $property->getNestedResultMap();
- $registry=$this->getManager()->getTypeHandlers();
- if($key === '')
- {
- $resultObject = $property->getPropertyValue($registry,$row);
- }
- else if(strlen($select) == 0 && ($nested===null))
- {
- $value = $property->getPropertyValue($registry,$row);
-
- $this->_IsRowDataFound = $this->_IsRowDataFound || ($value != null);
- if(is_array($resultObject) || is_object($resultObject))
- TPropertyAccess::set($resultObject, $key, $value);
- else
- $resultObject = $value;
- }
- else if($nested!==null)
- {
- if($property->instanceOfListType($resultObject) || $property->instanceOfArrayType($resultObject))
- {
- if(strlen($resultMap->getGroupBy()) <= 0)
- throw new TSqlMapExecutionException(
- 'sqlmap_non_groupby_array_list_type', $resultMap->getID(),
- get_class($resultObject), $key);
- }
- else
- {
- $obj = $nested->createInstanceOfResult($this->getManager()->getTypeHandlers());
- if($this->fillPropertyWithResultMap($nested, $row, $obj) == false)
- $obj = null;
- TPropertyAccess::set($resultObject, $key, $obj);
- }
- }
- else //'select' ResultProperty
- {
- $this->enquequePostSelect($select, $resultMap, $property, $row, $resultObject);
- }
- }
-
- /**
- * Add nested result property to post select queue.
- * @param string post select statement ID
- * @param TResultMap current result mapping details.
- * @param TResultProperty current result property.
- * @param array a result set row retrieved from the database
- * @param object the result object
- */
- protected function enquequePostSelect($select, $resultMap, $property, $row, $resultObject)
- {
- $statement = $this->getManager()->getMappedStatement($select);
- $key = $this->getPostSelectKeys($resultMap, $property, $row);
- $postSelect = new TPostSelectBinding;
- $postSelect->setStatement($statement);
- $postSelect->setResultObject($resultObject);
- $postSelect->setResultProperty($property);
- $postSelect->setKeys($key);
-
- if($property->instanceOfListType($resultObject))
- {
- $values = null;
- if($property->getLazyLoad())
- {
- $values = TLazyLoadList::newInstance($statement, $key,
- $resultObject, $property->getProperty());
- TPropertyAccess::set($resultObject, $property->getProperty(), $values);
- }
- else
- $postSelect->setMethod(self::QUERY_FOR_LIST);
- }
- else if($property->instanceOfArrayType($resultObject))
- $postSelect->setMethod(self::QUERY_FOR_ARRAY);
- else
- $postSelect->setMethod(self::QUERY_FOR_OBJECT);
-
- if(!$property->getLazyLoad())
- $this->_selectQueue[] = $postSelect;
- }
-
- /**
- * Finds in the post select property the SQL statement primary selection keys.
- * @param TResultMap result mapping details
- * @param TResultProperty result property
- * @param array current row data.
- * @return array list of primary key values.
- */
- protected function getPostSelectKeys($resultMap, $property,$row)
- {
- $value = $property->getColumn();
- if(is_int(strpos($value.',',0)) || is_int(strpos($value, '=',0)))
- {
- $keys = array();
- foreach(explode(',', $value) as $entry)
- {
- $pair =explode('=',$entry);
- $keys[trim($pair[0])] = $row[trim($pair[1])];
- }
- return $keys;
- }
- else
- {
- $registry=$this->getManager()->getTypeHandlers();
- return $property->getPropertyValue($registry,$row);
- }
- }
-
- /**
- * Fills the property with result mapping results.
- * @param TResultMap nested result mapping details.
- * @param array a result set row retrieved from the database
- * @param object the result object
- * @return boolean true if the data was found, false otherwise.
- */
- protected function fillPropertyWithResultMap($resultMap, $row, &$resultObject)
- {
- $dataFound = false;
- foreach($resultMap->getColumns() as $property)
- {
- $this->_IsRowDataFound = false;
- $this->setObjectProperty($resultMap, $property, $row, $resultObject);
- $dataFound = $dataFound || $this->_IsRowDataFound;
- }
- $this->_IsRowDataFound = $dataFound;
- return $dataFound;
- }
-
- public function __wakeup()
- {
- parent::__wakeup();
- if (is_null($this->_selectQueue)) $this->_selectQueue = array();
- }
-
- public function __sleep()
- {
- $exprops = array(); $cn = __CLASS__;
- if (!count($this->_selectQueue)) $exprops[] = "\0$cn\0_selectQueue";
- if (is_null($this->_groupBy)) $exprops[] = "\0$cn\0_groupBy";
- if (!$this->_IsRowDataFound) $exprops[] = "\0$cn\0_IsRowDataFound";
- return array_diff(parent::__sleep(),$exprops);
- }
-}
-
-/**
- * TPostSelectBinding class.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.SqlMap.Statements
- * @since 3.1
- */
-class TPostSelectBinding
-{
- private $_statement=null;
- private $_property=null;
- private $_resultObject=null;
- private $_keys=null;
- private $_method=TMappedStatement::QUERY_FOR_LIST;
-
- public function getStatement(){ return $this->_statement; }
- public function setStatement($value){ $this->_statement = $value; }
-
- public function getResultProperty(){ return $this->_property; }
- public function setResultProperty($value){ $this->_property = $value; }
-
- public function getResultObject(){ return $this->_resultObject; }
- public function setResultObject($value){ $this->_resultObject = $value; }
-
- public function getKeys(){ return $this->_keys; }
- public function setKeys($value){ $this->_keys = $value; }
-
- public function getMethod(){ return $this->_method; }
- public function setMethod($value){ $this->_method = $value; }
-}
-
-/**
- * TSQLMapObjectCollectionTree class.
- *
- * Maps object collection graphs as trees. Nodes in the collection can
- * be {@link add} using parent relationships. The object collections can be
- * build using the {@link collect} method.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.SqlMap.Statements
- * @since 3.1
- */
-class TSqlMapObjectCollectionTree extends TComponent
-{
- /**
- * @var array object graph as tree
- */
- private $_tree = array();
- /**
- * @var array tree node values
- */
- private $_entries = array();
- /**
- * @var array resulting object collection
- */
- private $_list = array();
-
- /**
- * @return boolean true if the graph is empty
- */
- public function isEmpty()
- {
- return count($this->_entries) == 0;
- }
-
- /**
- * Add a new node to the object tree graph.
- * @param string parent node id
- * @param string new node id
- * @param mixed node value
- */
- public function add($parent, $node, $object='')
- {
- if(isset($this->_entries[$parent]) && ($this->_entries[$parent]!==null)
- && isset($this->_entries[$node]) && ($this->_entries[$node]!==null))
- {
- $this->_entries[$node] = $object;
- return;
- }
- $this->_entries[$node] = $object;
- if(empty($parent))
- {
- if(isset($this->_entries[$node]))
- return;
- $this->_tree[$node] = array();
- }
- $found = $this->addNode($this->_tree, $parent, $node);
- if(!$found && !empty($parent))
- {
- $this->_tree[$parent] = array();
- if(!isset($this->_entries[$parent]) || $object !== '')
- $this->_entries[$parent] = $object;
- $this->addNode($this->_tree, $parent, $node);
- }
- }
-
- /**
- * Find the parent node and add the new node as its child.
- * @param array list of nodes to check
- * @param string parent node id
- * @param string new node id
- * @return boolean true if parent node is found.
- */
- protected function addNode(&$childs, $parent, $node)
- {
- $found = false;
- reset($childs);
- for($i = 0, $k = count($childs); $i < $k; $i++)
- {
- $key = key($childs);
- next($childs);
- if($key == $parent)
- {
- $found = true;
- $childs[$key][$node] = array();
- }
- else
- {
- $found = $found || $this->addNode($childs[$key], $parent, $node);
- }
- }
- return $found;
- }
-
- /**
- * @return array object collection
- */
- public function collect()
- {
- while(count($this->_tree) > 0)
- $this->collectChildren(null, $this->_tree);
- return $this->getCollection();
- }
-
- /**
- * @param array list of nodes to check
- * @return boolean true if all nodes are leaf nodes, false otherwise
- */
- protected function hasChildren(&$nodes)
- {
- $hasChildren = false;
- foreach($nodes as $node)
- if(count($node) != 0)
- return true;
- return $hasChildren;
- }
-
- /**
- * Visit all the child nodes and collect them by removing.
- * @param string parent node id
- * @param array list of child nodes.
- */
- protected function collectChildren($parent, &$nodes)
- {
- $noChildren = !$this->hasChildren($nodes);
- $childs = array();
- for(reset($nodes); $key = key($nodes);)
- {
- next($nodes);
- if($noChildren)
- {
- $childs[] = $key;
- unset($nodes[$key]);
- }
- else
- $this->collectChildren($key, $nodes[$key]);
- }
- if(count($childs) > 0)
- $this->onChildNodesVisited($parent, $childs);
- }
-
- /**
- * Set the object properties for all the child nodes visited.
- * @param string parent node id
- * @param array list of child nodes visited.
- */
- protected function onChildNodesVisited($parent, $nodes)
- {
- if(empty($parent) || empty($this->_entries[$parent]))
- return;
-
- $parentObject = $this->_entries[$parent]['object'];
- $property = $this->_entries[$nodes[0]]['property'];
-
- $list = TPropertyAccess::get($parentObject, $property);
-
- foreach($nodes as $node)
- {
- if($list instanceof TList)
- $parentObject->{$property}[] = $this->_entries[$node]['object'];
- else if(is_array($list))
- $list[] = $this->_entries[$node]['object'];
- else
- throw new TSqlMapExecutionException(
- 'sqlmap_property_must_be_list');
- }
-
- if(is_array($list))
- TPropertyAccess::set($parentObject, $property, $list);
-
- if($this->_entries[$parent]['property'] === null)
- $this->_list[] = $parentObject;
- }
-
- /**
- * @return array object collection.
- */
- protected function getCollection()
- {
- return $this->_list;
- }
-
- public function __sleep()
- {
- $exprops = array(); $cn = __CLASS__;
- if (!count($this->_tree)) $exprops[] = "\0$cn\0_tree";
- if (!count($this->_entries)) $exprops[] = "\0$cn\0_entries";
- if (!count($this->_list)) $exprops[] = "\0$cn\0_list";
- return array_diff(parent::__sleep(),$exprops);
- }
-}
-
-/**
- * TResultSetListItemParameter class
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.SqlMap.Statements
- * @since 3.1
- */
-class TResultSetListItemParameter extends TComponent
-{
- private $_resultObject;
- private $_parameterObject;
- private $_list;
-
- public function __construct($result, $parameter, &$list)
- {
- $this->_resultObject = $result;
- $this->_parameterObject = $parameter;
- $this->_list = &$list;
- }
-
- public function getResult()
- {
- return $this->_resultObject;
- }
-
- public function getParameter()
- {
- return $this->_parameterObject;
- }
-
- public function &getList()
- {
- return $this->_list;
- }
-}
-
-/**
- * TResultSetMapItemParameter class.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.SqlMap.Statements
- * @since 3.1
- */
-class TResultSetMapItemParameter extends TComponent
-{
- private $_key;
- private $_value;
- private $_parameterObject;
- private $_map;
-
- public function __construct($key, $value, $parameter, &$map)
- {
- $this->_key = $key;
- $this->_value = $value;
- $this->_parameterObject = $parameter;
- $this->_map = &$map;
- }
-
- public function getKey()
- {
- return $this->_key;
- }
-
- public function getValue()
- {
- return $this->_value;
- }
-
- public function getParameter()
- {
- return $this->_parameterObject;
- }
-
- public function &getMap()
- {
- return $this->_map;
- }
-}
-
+<?php +/** + * TMappedStatement and related classes. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.SqlMap.Statements + */ + +/** + * TMappedStatement class executes SQL mapped statements. Mapped Statements can + * hold any SQL statement and use Parameter Maps and Result Maps for input and output. + * + * This class is usualy instantiated during SQLMap configuration by TSqlDomBuilder. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.SqlMap.Statements + * @since 3.0 + */ +class TMappedStatement extends TComponent implements IMappedStatement +{ + /** + * @var TSqlMapStatement current SQL statement. + */ + private $_statement; + + /** + * @var TPreparedCommand SQL command prepareer + */ + private $_command; + + /** + * @var TSqlMapper sqlmap used by this mapper. + */ + private $_manager; + + /** + * @var TPostSelectBinding[] post select statement queue. + */ + private $_selectQueue=array(); + + /** + * @var boolean true when data is mapped to a particular row. + */ + private $_IsRowDataFound = false; + + /** + * @var TSQLMapObjectCollectionTree group by object collection tree + */ + private $_groupBy; + + /** + * @var Post select is to query for list. + */ + const QUERY_FOR_LIST = 0; + + /** + * @var Post select is to query for list. + */ + const QUERY_FOR_ARRAY = 1; + + /** + * @var Post select is to query for object. + */ + const QUERY_FOR_OBJECT = 2; + + /** + * @return string Name used to identify the TMappedStatement amongst the others. + * This the name of the SQL statement by default. + */ + public function getID() + { + return $this->_statement->ID; + } + + /** + * @return TSqlMapStatement The SQL statment used by this MappedStatement + */ + public function getStatement() + { + return $this->_statement; + } + + /** + * @return TSqlMapper The SqlMap used by this MappedStatement + */ + public function getManager() + { + return $this->_manager; + } + + /** + * @return TPreparedCommand command to prepare SQL statements. + */ + public function getCommand() + { + return $this->_command; + } + + /** + * Empty the group by results cache. + */ + protected function initialGroupByResults() + { + $this->_groupBy = new TSqlMapObjectCollectionTree(); + } + + /** + * Creates a new mapped statement. + * @param TSqlMapper an sqlmap. + * @param TSqlMapStatement An SQL statement. + */ + public function __construct(TSqlMapManager $sqlMap, TSqlMapStatement $statement) + { + $this->_manager = $sqlMap; + $this->_statement = $statement; + $this->_command = new TPreparedCommand(); + $this->initialGroupByResults(); + } + + public function getSqlString() + { + return $this->getStatement()->getSqlText()->getPreparedStatement()->getPreparedSql(); + } + + /** + * Execute SQL Query. + * @param IDbConnection database connection + * @param array SQL statement and parameters. + * @return mixed record set if applicable. + * @throws TSqlMapExecutionException if execution error or false record set. + * @throws TSqlMapQueryExecutionException if any execution error + */ +/* protected function executeSQLQuery($connection, $sql) + { + try + { + if(!($recordSet = $connection->execute($sql['sql'],$sql['parameters']))) + { + throw new TSqlMapExecutionException( + 'sqlmap_execution_error_no_record', $this->getID(), + $connection->ErrorMsg()); + } + return $recordSet; + } + catch (Exception $e) + { + throw new TSqlMapQueryExecutionException($this->getStatement(), $e); + } + }*/ + + /** + * Execute SQL Query with limits. + * @param IDbConnection database connection + * @param array SQL statement and parameters. + * @return mixed record set if applicable. + * @throws TSqlMapExecutionException if execution error or false record set. + * @throws TSqlMapQueryExecutionException if any execution error + */ + protected function executeSQLQueryLimit($connection, $command, $max, $skip) + { + if($max>-1 || $skip > -1) + { + $maxStr=$max>0?' LIMIT '.$max:''; + $skipStr=$skip>0?' OFFSET '.$skip:''; + $command->setText($command->getText().$maxStr.$skipStr); + } + $connection->setActive(true); + return $command->query(); + + /*//var_dump($command); + try + { + $recordSet = $connection->selectLimit($sql['sql'],$max,$skip,$sql['parameters']); + if(!$recordSet) + { + throw new TSqlMapExecutionException( + 'sqlmap_execution_error_query_for_list', + $connection->ErrorMsg()); + } + return $recordSet; + } + catch (Exception $e) + { + throw new TSqlMapQueryExecutionException($this->getStatement(), $e); + }*/ + } + + /** + * Executes the SQL and retuns a List of result objects. + * @param IDbConnection database connection + * @param mixed The object used to set the parameters in the SQL. + * @param object result collection object. + * @param integer The number of rows to skip over. + * @param integer The maximum number of rows to return. + * @return array a list of result objects + * @param callback row delegate handler + * @see executeQueryForList() + */ + public function executeQueryForList($connection, $parameter, $result=null, $skip=-1, $max=-1, $delegate=null) + { + $sql = $this->_command->create($this->_manager, $connection, $this->_statement, $parameter,$skip,$max); + return $this->runQueryForList($connection, $parameter, $sql, $result, $delegate); + } + + /** + * Executes the SQL and retuns a List of result objects. + * + * This method should only be called by internal developers, consider using + * <tt>executeQueryForList()</tt> first. + * + * @param IDbConnection database connection + * @param mixed The object used to set the parameters in the SQL. + * @param array SQL string and subsititution parameters. + * @param object result collection object. + * @param integer The number of rows to skip over. + * @param integer The maximum number of rows to return. + * @param callback row delegate handler + * @return array a list of result objects + * @see executeQueryForList() + */ + public function runQueryForList($connection, $parameter, $sql, $result, $delegate=null) + { + $registry=$this->getManager()->getTypeHandlers(); + $list = $result instanceof ArrayAccess ? $result : + $this->_statement->createInstanceOfListClass($registry); + $connection->setActive(true); + $reader = $sql->query(); + //$reader = $this->executeSQLQueryLimit($connection, $sql, $max, $skip); + if($delegate!==null) + { + foreach($reader as $row) + { + $obj = $this->applyResultMap($row); + $param = new TResultSetListItemParameter($obj, $parameter, $list); + $this->raiseRowDelegate($delegate, $param); + } + } + else + { + //var_dump($sql,$parameter); + foreach($reader as $row) + { +// var_dump($row); + $list[] = $this->applyResultMap($row); + } + } + + if(!$this->_groupBy->isEmpty()) + { + $list = $this->_groupBy->collect(); + $this->initialGroupByResults(); + } + + $this->executePostSelect($connection); + $this->onExecuteQuery($sql); + + return $list; + } + + /** + * Executes the SQL and retuns all rows selected in a map that is keyed on + * the property named in the keyProperty parameter. The value at each key + * will be the value of the property specified in the valueProperty parameter. + * If valueProperty is null, the entire result object will be entered. + * @param IDbConnection database connection + * @param mixed The object used to set the parameters in the SQL. + * @param string The property of the result object to be used as the key. + * @param string The property of the result object to be used as the value (or null). + * @param callback row delegate handler + * @return array An array of object containing the rows keyed by keyProperty. + */ + public function executeQueryForMap($connection, $parameter, $keyProperty, $valueProperty=null, $skip=-1, $max=-1, $delegate=null) + { + $sql = $this->_command->create($this->_manager, $connection, $this->_statement, $parameter, $skip, $max); + return $this->runQueryForMap($connection, $parameter, $sql, $keyProperty, $valueProperty, $delegate); + } + + /** + * Executes the SQL and retuns all rows selected in a map that is keyed on + * the property named in the keyProperty parameter. The value at each key + * will be the value of the property specified in the valueProperty parameter. + * If valueProperty is null, the entire result object will be entered. + * + * This method should only be called by internal developers, consider using + * <tt>executeQueryForMap()</tt> first. + * + * @param IDbConnection database connection + * @param mixed The object used to set the parameters in the SQL. + * @param array SQL string and subsititution parameters. + * @param string The property of the result object to be used as the key. + * @param string The property of the result object to be used as the value (or null). + * @param callback row delegate, a callback function + * @return array An array of object containing the rows keyed by keyProperty. + * @see executeQueryForMap() + */ + public function runQueryForMap($connection, $parameter, $command, $keyProperty, $valueProperty=null, $delegate=null) + { + $map = array(); + //$recordSet = $this->executeSQLQuery($connection, $sql); + $connection->setActive(true); + $reader = $command->query(); + if($delegate!==null) + { + //while($row = $recordSet->fetchRow()) + foreach($reader as $row) + { + $obj = $this->applyResultMap($row); + $key = TPropertyAccess::get($obj, $keyProperty); + $value = ($valueProperty===null) ? $obj : + TPropertyAccess::get($obj, $valueProperty); + $param = new TResultSetMapItemParameter($key, $value, $parameter, $map); + $this->raiseRowDelegate($delegate, $param); + } + } + else + { + //while($row = $recordSet->fetchRow()) + foreach($reader as $row) + { + $obj = $this->applyResultMap($row); + $key = TPropertyAccess::get($obj, $keyProperty); + $map[$key] = ($valueProperty===null) ? $obj : + TPropertyAccess::get($obj, $valueProperty); + } + } + $this->onExecuteQuery($command); + return $map; + } + + /** + * Raises delegate handler. + * This method is invoked for each new list item. It is the responsibility + * of the handler to add the item to the list. + * @param object event parameter + */ + protected function raiseRowDelegate($handler, $param) + { + if(is_string($handler)) + { + call_user_func($handler,$this,$param); + } + else if(is_callable($handler,true)) + { + // an array: 0 - object, 1 - method name/path + list($object,$method)=$handler; + if(is_string($object)) // static method call + call_user_func($handler,$this,$param); + else + { + if(($pos=strrpos($method,'.'))!==false) + { + $object=$this->getSubProperty(substr($method,0,$pos)); + $method=substr($method,$pos+1); + } + $object->$method($this,$param); + } + } + else + throw new TInvalidDataValueException('sqlmap_invalid_delegate', $this->getID(), $handler); + } + + /** + * Executes an SQL statement that returns a single row as an object of the + * type of the <tt>$result</tt> passed in as a parameter. + * @param IDbConnection database connection + * @param mixed The parameter data (object, arrary, primitive) used to set the parameters in the SQL + * @param mixed The result object. + * @return ${return} + */ + public function executeQueryForObject($connection, $parameter, $result=null) + { + $sql = $this->_command->create($this->_manager, $connection, $this->_statement, $parameter); + return $this->runQueryForObject($connection, $sql, $result); + } + + /** + * Executes an SQL statement that returns a single row as an object of the + * type of the <tt>$result</tt> passed in as a parameter. + * + * This method should only be called by internal developers, consider using + * <tt>executeQueryForObject()</tt> first. + * + * @param IDbConnection database connection + * @param array SQL string and subsititution parameters. + * @param object The result object. + * @return object the object. + * @see executeQueryForObject() + */ + public function runQueryForObject($connection, $command, &$result) + { + $object = null; + $connection->setActive(true); + foreach($command->query() as $row) + $object = $this->applyResultMap($row, $result); + + if(!$this->_groupBy->isEmpty()) + { + $list = $this->_groupBy->collect(); + $this->initialGroupByResults(); + $object = $list[0]; + } + + $this->executePostSelect($connection); + $this->onExecuteQuery($command); + + return $object; + } + + /** + * Execute an insert statement. Fill the parameter object with the ouput + * parameters if any, also could return the insert generated key. + * @param IDbConnection database connection + * @param mixed The parameter object used to fill the statement. + * @return string the insert generated key. + */ + public function executeInsert($connection, $parameter) + { + $generatedKey = $this->getPreGeneratedSelectKey($connection, $parameter); + + $command = $this->_command->create($this->_manager, $connection, $this->_statement, $parameter); +// var_dump($command,$parameter); + $result = $command->execute(); + + if($generatedKey===null) + $generatedKey = $this->getPostGeneratedSelectKey($connection, $parameter); + + $this->executePostSelect($connection); + $this->onExecuteQuery($command); + return $generatedKey; + } + + /** + * Gets the insert generated ID before executing an insert statement. + * @param IDbConnection database connection + * @param mixed insert statement parameter. + * @return string new insert ID if pre-select key statement was executed, null otherwise. + */ + protected function getPreGeneratedSelectKey($connection, $parameter) + { + if($this->_statement instanceof TSqlMapInsert) + { + $selectKey = $this->_statement->getSelectKey(); + if(($selectKey!==null) && !$selectKey->getIsAfter()) + return $this->executeSelectKey($connection, $parameter, $selectKey); + } + } + + /** + * Gets the inserted row ID after executing an insert statement. + * @param IDbConnection database connection + * @param mixed insert statement parameter. + * @return string last insert ID, null otherwise. + */ + protected function getPostGeneratedSelectKey($connection, $parameter) + { + if($this->_statement instanceof TSqlMapInsert) + { + $selectKey = $this->_statement->getSelectKey(); + if(($selectKey!==null) && $selectKey->getIsAfter()) + return $this->executeSelectKey($connection, $parameter, $selectKey); + } + } + + /** + * Execute the select key statement, used to obtain last insert ID. + * @param IDbConnection database connection + * @param mixed insert statement parameter + * @param TSqlMapSelectKey select key statement + * @return string last insert ID. + */ + protected function executeSelectKey($connection, $parameter, $selectKey) + { + $mappedStatement = $this->getManager()->getMappedStatement($selectKey->getID()); + $generatedKey = $mappedStatement->executeQueryForObject( + $connection, $parameter, null); + if(strlen($prop = $selectKey->getProperty()) > 0) + TPropertyAccess::set($parameter, $prop, $generatedKey); + return $generatedKey; + } + + /** + * Execute an update statement. Also used for delete statement. + * Return the number of rows effected. + * @param IDbConnection database connection + * @param mixed The object used to set the parameters in the SQL. + * @return integer The number of rows effected. + */ + public function executeUpdate($connection, $parameter) + { + $sql = $this->_command->create($this->getManager(),$connection, $this->_statement, $parameter); + $affectedRows = $sql->execute(); + //$this->executeSQLQuery($connection, $sql); + $this->executePostSelect($connection); + $this->onExecuteQuery($sql); + return $affectedRows; + } + + /** + * Process 'select' result properties + * @param IDbConnection database connection + */ + protected function executePostSelect($connection) + { + while(count($this->_selectQueue)) + { + $postSelect = array_shift($this->_selectQueue); + $method = $postSelect->getMethod(); + $statement = $postSelect->getStatement(); + $property = $postSelect->getResultProperty()->getProperty(); + $keys = $postSelect->getKeys(); + $resultObject = $postSelect->getResultObject(); + + if($method == self::QUERY_FOR_LIST || $method == self::QUERY_FOR_ARRAY) + { + $values = $statement->executeQueryForList($connection, $keys, null); + + if($method == self::QUERY_FOR_ARRAY) + $values = $values->toArray(); + TPropertyAccess::set($resultObject, $property, $values); + } + else if($method == self::QUERY_FOR_OBJECT) + { + $value = $statement->executeQueryForObject($connection, $keys, null); + TPropertyAccess::set($resultObject, $property, $value); + } + } + } + + /** + * Raise the execute query event. + * @param array prepared SQL statement and subsititution parameters + */ + public function onExecuteQuery($sql) + { + $this->raiseEvent('OnExecuteQuery', $this, $sql); + } + + /** + * Apply result mapping. + * @param array a result set row retrieved from the database + * @param object the result object, will create if necessary. + * @return object the result filled with data, null if not filled. + */ + protected function applyResultMap($row, &$resultObject=null) + { + if($row === false) return null; + + $resultMapName = $this->_statement->getResultMap(); + $resultClass = $this->_statement->getResultClass(); + + $obj=null; + if($this->getManager()->getResultMaps()->contains($resultMapName)) + $obj = $this->fillResultMap($resultMapName, $row, null, $resultObject); + else if(strlen($resultClass) > 0) + $obj = $this->fillResultClass($resultClass, $row, $resultObject); + else + $obj = $this->fillDefaultResultMap(null, $row, $resultObject); + if(class_exists('TActiveRecord',false) && $obj instanceof TActiveRecord) + //Create a new clean active record. + $obj=TActiveRecord::createRecord(get_class($obj),$obj); + return $obj; + } + + /** + * Fill the result using ResultClass, will creates new result object if required. + * @param string result object class name + * @param array a result set row retrieved from the database + * @param object the result object, will create if necessary. + * @return object result object filled with data + */ + protected function fillResultClass($resultClass, $row, $resultObject) + { + if($resultObject===null) + { + $registry = $this->getManager()->getTypeHandlers(); + $resultObject = $this->_statement->createInstanceOfResultClass($registry,$row); + } + + if($resultObject instanceOf ArrayAccess) + return $this->fillResultArrayList($row, $resultObject); + else if(is_object($resultObject)) + return $this->fillResultObjectProperty($row, $resultObject); + else + return $this->fillDefaultResultMap(null, $row, $resultObject); + } + + /** + * Apply the result to a TList or an array. + * @param array a result set row retrieved from the database + * @param object result object, array or list + * @return object result filled with data. + */ + protected function fillResultArrayList($row, $resultObject) + { + if($resultObject instanceof TList) + foreach($row as $v) + $resultObject[] = $v; + else + foreach($row as $k => $v) + $resultObject[$k] = $v; + return $resultObject; + } + + /** + * Apply the result to an object. + * @param array a result set row retrieved from the database + * @param object result object, array or list + * @return object result filled with data. + */ + protected function fillResultObjectProperty($row, $resultObject) + { + $index = 0; + $registry=$this->getManager()->getTypeHandlers(); + foreach($row as $k=>$v) + { + $property = new TResultProperty; + if(is_string($k) && strlen($k) > 0) + $property->setColumn($k); + $property->setColumnIndex(++$index); + $type = gettype(TPropertyAccess::get($resultObject,$k)); + $property->setType($type); + $value = $property->getPropertyValue($registry,$row); + TPropertyAccess::set($resultObject, $k,$value); + } + return $resultObject; + } + + /** + * Fills the result object according to result mappings. + * @param string result map name. + * @param array a result set row retrieved from the database + * @param object result object to fill, will create new instances if required. + * @return object result object filled with data. + */ + protected function fillResultMap($resultMapName, $row, $parentGroup=null, &$resultObject=null) + { + $resultMap = $this->getManager()->getResultMap($resultMapName); + $registry = $this->getManager()->getTypeHandlers(); + $resultMap = $resultMap->resolveSubMap($registry,$row); + + if($resultObject===null) + $resultObject = $resultMap->createInstanceOfResult($registry); + + if(is_object($resultObject)) + { + if(strlen($resultMap->getGroupBy()) > 0) + return $this->addResultMapGroupBy($resultMap, $row, $parentGroup, $resultObject); + else + foreach($resultMap->getColumns() as $property) + $this->setObjectProperty($resultMap, $property, $row, $resultObject); + } + else + { + $resultObject = $this->fillDefaultResultMap($resultMap, $row, $resultObject); + } + return $resultObject; + } + + /** + * ResultMap with GroupBy property. Save object collection graph in a tree + * and collect the result later. + * @param TResultMap result mapping details. + * @param array a result set row retrieved from the database + * @param object the result object + * @return object result object. + */ + protected function addResultMapGroupBy($resultMap, $row, $parent, &$resultObject) + { + $group = $this->getResultMapGroupKey($resultMap, $row); + + if(empty($parent)) + { + $rootObject = array('object'=>$resultObject, 'property' => null); + $this->_groupBy->add(null, $group, $rootObject); + } + + foreach($resultMap->getColumns() as $property) + { + //set properties. + $this->setObjectProperty($resultMap, $property, $row, $resultObject); + $nested = $property->getResultMapping(); + + //nested property + if($this->getManager()->getResultMaps()->contains($nested)) + { + $nestedMap = $this->getManager()->getResultMap($nested); + $groupKey = $this->getResultMapGroupKey($nestedMap, $row); + + //add the node reference first + if(empty($parent)) + $this->_groupBy->add($group, $groupKey, ''); + + //get the nested result mapping value + $value = $this->fillResultMap($nested, $row, $groupKey); + + //add it to the object tree graph + $groupObject = array('object'=>$value, 'property' => $property->getProperty()); + if(empty($parent)) + $this->_groupBy->add($group, $groupKey, $groupObject); + else + $this->_groupBy->add($parent, $groupKey, $groupObject); + } + } + return $resultObject; + } + + /** + * Gets the result 'group by' groupping key for each row. + * @param TResultMap result mapping details. + * @param array a result set row retrieved from the database + * @return string groupping key. + */ + protected function getResultMapGroupKey($resultMap, $row) + { + $groupBy = $resultMap->getGroupBy(); + if(isset($row[$groupBy])) + return $resultMap->getID().$row[$groupBy]; + else + return $resultMap->getID().crc32(serialize($row)); + } + + /** + * Fill the result map using default settings. If <tt>$resultMap</tt> is null + * the result object returned will be guessed from <tt>$resultObject</tt>. + * @param TResultMap result mapping details. + * @param array a result set row retrieved from the database + * @param object the result object + * @return mixed the result object filled with data. + */ + protected function fillDefaultResultMap($resultMap, $row, $resultObject) + { + if($resultObject===null) + $resultObject=''; + + if($resultMap!==null) + $result = $this->fillArrayResultMap($resultMap, $row, $resultObject); + else + $result = $row; + + //if scalar result types + if(count($result) == 1 && ($type = gettype($resultObject))!= 'array') + return $this->getScalarResult($result, $type); + else + return $result; + } + + /** + * Retrieve the result map as an array. + * @param TResultMap result mapping details. + * @param array a result set row retrieved from the database + * @param object the result object + * @return array array list of result objects. + */ + protected function fillArrayResultMap($resultMap, $row, $resultObject) + { + $result = array(); + $registry=$this->getManager()->getTypeHandlers(); + foreach($resultMap->getColumns() as $column) + { + if(($column->getType()===null) + && ($resultObject!==null) && !is_object($resultObject)) + $column->setType(gettype($resultObject)); + $result[$column->getProperty()] = $column->getPropertyValue($registry,$row); + } + return $result; + } + + /** + * Converts the first array value to scalar value of given type. + * @param array list of results + * @param string scalar type. + * @return mixed scalar value. + */ + protected function getScalarResult($result, $type) + { + $scalar = array_shift($result); + settype($scalar, $type); + return $scalar; + } + + /** + * Set a property of the result object with appropriate value. + * @param TResultMap result mapping details. + * @param TResultProperty the result property to fill. + * @param array a result set row retrieved from the database + * @param object the result object + */ + protected function setObjectProperty($resultMap, $property, $row, &$resultObject) + { + $select = $property->getSelect(); + $key = $property->getProperty(); + $nested = $property->getNestedResultMap(); + $registry=$this->getManager()->getTypeHandlers(); + if($key === '') + { + $resultObject = $property->getPropertyValue($registry,$row); + } + else if(strlen($select) == 0 && ($nested===null)) + { + $value = $property->getPropertyValue($registry,$row); + + $this->_IsRowDataFound = $this->_IsRowDataFound || ($value != null); + if(is_array($resultObject) || is_object($resultObject)) + TPropertyAccess::set($resultObject, $key, $value); + else + $resultObject = $value; + } + else if($nested!==null) + { + if($property->instanceOfListType($resultObject) || $property->instanceOfArrayType($resultObject)) + { + if(strlen($resultMap->getGroupBy()) <= 0) + throw new TSqlMapExecutionException( + 'sqlmap_non_groupby_array_list_type', $resultMap->getID(), + get_class($resultObject), $key); + } + else + { + $obj = $nested->createInstanceOfResult($this->getManager()->getTypeHandlers()); + if($this->fillPropertyWithResultMap($nested, $row, $obj) == false) + $obj = null; + TPropertyAccess::set($resultObject, $key, $obj); + } + } + else //'select' ResultProperty + { + $this->enquequePostSelect($select, $resultMap, $property, $row, $resultObject); + } + } + + /** + * Add nested result property to post select queue. + * @param string post select statement ID + * @param TResultMap current result mapping details. + * @param TResultProperty current result property. + * @param array a result set row retrieved from the database + * @param object the result object + */ + protected function enquequePostSelect($select, $resultMap, $property, $row, $resultObject) + { + $statement = $this->getManager()->getMappedStatement($select); + $key = $this->getPostSelectKeys($resultMap, $property, $row); + $postSelect = new TPostSelectBinding; + $postSelect->setStatement($statement); + $postSelect->setResultObject($resultObject); + $postSelect->setResultProperty($property); + $postSelect->setKeys($key); + + if($property->instanceOfListType($resultObject)) + { + $values = null; + if($property->getLazyLoad()) + { + $values = TLazyLoadList::newInstance($statement, $key, + $resultObject, $property->getProperty()); + TPropertyAccess::set($resultObject, $property->getProperty(), $values); + } + else + $postSelect->setMethod(self::QUERY_FOR_LIST); + } + else if($property->instanceOfArrayType($resultObject)) + $postSelect->setMethod(self::QUERY_FOR_ARRAY); + else + $postSelect->setMethod(self::QUERY_FOR_OBJECT); + + if(!$property->getLazyLoad()) + $this->_selectQueue[] = $postSelect; + } + + /** + * Finds in the post select property the SQL statement primary selection keys. + * @param TResultMap result mapping details + * @param TResultProperty result property + * @param array current row data. + * @return array list of primary key values. + */ + protected function getPostSelectKeys($resultMap, $property,$row) + { + $value = $property->getColumn(); + if(is_int(strpos($value.',',0)) || is_int(strpos($value, '=',0))) + { + $keys = array(); + foreach(explode(',', $value) as $entry) + { + $pair =explode('=',$entry); + $keys[trim($pair[0])] = $row[trim($pair[1])]; + } + return $keys; + } + else + { + $registry=$this->getManager()->getTypeHandlers(); + return $property->getPropertyValue($registry,$row); + } + } + + /** + * Fills the property with result mapping results. + * @param TResultMap nested result mapping details. + * @param array a result set row retrieved from the database + * @param object the result object + * @return boolean true if the data was found, false otherwise. + */ + protected function fillPropertyWithResultMap($resultMap, $row, &$resultObject) + { + $dataFound = false; + foreach($resultMap->getColumns() as $property) + { + $this->_IsRowDataFound = false; + $this->setObjectProperty($resultMap, $property, $row, $resultObject); + $dataFound = $dataFound || $this->_IsRowDataFound; + } + $this->_IsRowDataFound = $dataFound; + return $dataFound; + } + + public function __wakeup() + { + parent::__wakeup(); + if (is_null($this->_selectQueue)) $this->_selectQueue = array(); + } + + public function __sleep() + { + $exprops = array(); $cn = __CLASS__; + if (!count($this->_selectQueue)) $exprops[] = "\0$cn\0_selectQueue"; + if (is_null($this->_groupBy)) $exprops[] = "\0$cn\0_groupBy"; + if (!$this->_IsRowDataFound) $exprops[] = "\0$cn\0_IsRowDataFound"; + return array_diff(parent::__sleep(),$exprops); + } +} + +/** + * TPostSelectBinding class. + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.SqlMap.Statements + * @since 3.1 + */ +class TPostSelectBinding +{ + private $_statement=null; + private $_property=null; + private $_resultObject=null; + private $_keys=null; + private $_method=TMappedStatement::QUERY_FOR_LIST; + + public function getStatement(){ return $this->_statement; } + public function setStatement($value){ $this->_statement = $value; } + + public function getResultProperty(){ return $this->_property; } + public function setResultProperty($value){ $this->_property = $value; } + + public function getResultObject(){ return $this->_resultObject; } + public function setResultObject($value){ $this->_resultObject = $value; } + + public function getKeys(){ return $this->_keys; } + public function setKeys($value){ $this->_keys = $value; } + + public function getMethod(){ return $this->_method; } + public function setMethod($value){ $this->_method = $value; } +} + +/** + * TSQLMapObjectCollectionTree class. + * + * Maps object collection graphs as trees. Nodes in the collection can + * be {@link add} using parent relationships. The object collections can be + * build using the {@link collect} method. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.SqlMap.Statements + * @since 3.1 + */ +class TSqlMapObjectCollectionTree extends TComponent +{ + /** + * @var array object graph as tree + */ + private $_tree = array(); + /** + * @var array tree node values + */ + private $_entries = array(); + /** + * @var array resulting object collection + */ + private $_list = array(); + + /** + * @return boolean true if the graph is empty + */ + public function isEmpty() + { + return count($this->_entries) == 0; + } + + /** + * Add a new node to the object tree graph. + * @param string parent node id + * @param string new node id + * @param mixed node value + */ + public function add($parent, $node, $object='') + { + if(isset($this->_entries[$parent]) && ($this->_entries[$parent]!==null) + && isset($this->_entries[$node]) && ($this->_entries[$node]!==null)) + { + $this->_entries[$node] = $object; + return; + } + $this->_entries[$node] = $object; + if(empty($parent)) + { + if(isset($this->_entries[$node])) + return; + $this->_tree[$node] = array(); + } + $found = $this->addNode($this->_tree, $parent, $node); + if(!$found && !empty($parent)) + { + $this->_tree[$parent] = array(); + if(!isset($this->_entries[$parent]) || $object !== '') + $this->_entries[$parent] = $object; + $this->addNode($this->_tree, $parent, $node); + } + } + + /** + * Find the parent node and add the new node as its child. + * @param array list of nodes to check + * @param string parent node id + * @param string new node id + * @return boolean true if parent node is found. + */ + protected function addNode(&$childs, $parent, $node) + { + $found = false; + reset($childs); + for($i = 0, $k = count($childs); $i < $k; $i++) + { + $key = key($childs); + next($childs); + if($key == $parent) + { + $found = true; + $childs[$key][$node] = array(); + } + else + { + $found = $found || $this->addNode($childs[$key], $parent, $node); + } + } + return $found; + } + + /** + * @return array object collection + */ + public function collect() + { + while(count($this->_tree) > 0) + $this->collectChildren(null, $this->_tree); + return $this->getCollection(); + } + + /** + * @param array list of nodes to check + * @return boolean true if all nodes are leaf nodes, false otherwise + */ + protected function hasChildren(&$nodes) + { + $hasChildren = false; + foreach($nodes as $node) + if(count($node) != 0) + return true; + return $hasChildren; + } + + /** + * Visit all the child nodes and collect them by removing. + * @param string parent node id + * @param array list of child nodes. + */ + protected function collectChildren($parent, &$nodes) + { + $noChildren = !$this->hasChildren($nodes); + $childs = array(); + for(reset($nodes); $key = key($nodes);) + { + next($nodes); + if($noChildren) + { + $childs[] = $key; + unset($nodes[$key]); + } + else + $this->collectChildren($key, $nodes[$key]); + } + if(count($childs) > 0) + $this->onChildNodesVisited($parent, $childs); + } + + /** + * Set the object properties for all the child nodes visited. + * @param string parent node id + * @param array list of child nodes visited. + */ + protected function onChildNodesVisited($parent, $nodes) + { + if(empty($parent) || empty($this->_entries[$parent])) + return; + + $parentObject = $this->_entries[$parent]['object']; + $property = $this->_entries[$nodes[0]]['property']; + + $list = TPropertyAccess::get($parentObject, $property); + + foreach($nodes as $node) + { + if($list instanceof TList) + $parentObject->{$property}[] = $this->_entries[$node]['object']; + else if(is_array($list)) + $list[] = $this->_entries[$node]['object']; + else + throw new TSqlMapExecutionException( + 'sqlmap_property_must_be_list'); + } + + if(is_array($list)) + TPropertyAccess::set($parentObject, $property, $list); + + if($this->_entries[$parent]['property'] === null) + $this->_list[] = $parentObject; + } + + /** + * @return array object collection. + */ + protected function getCollection() + { + return $this->_list; + } + + public function __sleep() + { + $exprops = array(); $cn = __CLASS__; + if (!count($this->_tree)) $exprops[] = "\0$cn\0_tree"; + if (!count($this->_entries)) $exprops[] = "\0$cn\0_entries"; + if (!count($this->_list)) $exprops[] = "\0$cn\0_list"; + return array_diff(parent::__sleep(),$exprops); + } +} + +/** + * TResultSetListItemParameter class + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.SqlMap.Statements + * @since 3.1 + */ +class TResultSetListItemParameter extends TComponent +{ + private $_resultObject; + private $_parameterObject; + private $_list; + + public function __construct($result, $parameter, &$list) + { + $this->_resultObject = $result; + $this->_parameterObject = $parameter; + $this->_list = &$list; + } + + public function getResult() + { + return $this->_resultObject; + } + + public function getParameter() + { + return $this->_parameterObject; + } + + public function &getList() + { + return $this->_list; + } +} + +/** + * TResultSetMapItemParameter class. + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.SqlMap.Statements + * @since 3.1 + */ +class TResultSetMapItemParameter extends TComponent +{ + private $_key; + private $_value; + private $_parameterObject; + private $_map; + + public function __construct($key, $value, $parameter, &$map) + { + $this->_key = $key; + $this->_value = $value; + $this->_parameterObject = $parameter; + $this->_map = &$map; + } + + public function getKey() + { + return $this->_key; + } + + public function getValue() + { + return $this->_value; + } + + public function getParameter() + { + return $this->_parameterObject; + } + + public function &getMap() + { + return $this->_map; + } +} + diff --git a/framework/Data/SqlMap/Statements/TPreparedCommand.php b/framework/Data/SqlMap/Statements/TPreparedCommand.php index 1d4a7088..7aa249ee 100644 --- a/framework/Data/SqlMap/Statements/TPreparedCommand.php +++ b/framework/Data/SqlMap/Statements/TPreparedCommand.php @@ -1,67 +1,67 @@ -<?php
-/**
- * TPreparedCommand class file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
+<?php +/** + * TPreparedCommand class file. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.SqlMap.Statements
- */
-
-Prado::using('System.Data.Common.TDbMetaData');
-Prado::using('System.Data.Common.TDbCommandBuilder');
-
-/**
- * TPreparedCommand class.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.SqlMap.Statements
- * @since 3.1
- */
-class TPreparedCommand
-{
- public function create(TSqlMapManager $manager, $connection, $statement, $parameterObject,$skip=null,$max=null)
- {
- $sqlText = $statement->getSQLText();
-
- $prepared = $sqlText->getPreparedStatement($parameterObject);
- $connection->setActive(true);
- $sql = $prepared->getPreparedSql();
-
- if($sqlText instanceof TSimpleDynamicSql)
- $sql = $sqlText->replaceDynamicParameter($sql, $parameterObject);
-
- if($max!==null || $skip!==null)
- {
- $builder = TDbMetaData::getInstance($connection)->createCommandBuilder();
- $sql = $builder->applyLimitOffset($sql,$max,$skip);
- }
- $command = $connection->createCommand($sql);
- $this->applyParameterMap($manager, $command, $prepared, $statement, $parameterObject);
-
- return $command;
- }
-
- protected function applyParameterMap($manager,$command,$prepared, $statement, $parameterObject)
- {
- $properties = $prepared->getParameterNames();
- $parameters = $prepared->getParameterValues();
- $registry=$manager->getTypeHandlers();
- for($i = 0, $k=$properties->getCount(); $i<$k; $i++)
- {
- $property = $statement->parameterMap()->getProperty($i);
- $value = $statement->parameterMap()->getPropertyValue($registry,$property, $parameterObject);
- $dbType = $property->getDbType();
- if($dbType=='') //relies on PHP lax comparison
- $command->bindValue($i+1,$value, TDbCommandBuilder::getPdoType($value));
- else if(strpos($dbType, 'PDO::')===0)
- $command->bindValue($i+1,$value, constant($property->getDbType())); //assumes PDO types, e.g. PDO::PARAM_INT
- else
- $command->bindValue($i+1,$value);
- }
- }
-}
-
+ * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.SqlMap.Statements + */ + +Prado::using('System.Data.Common.TDbMetaData'); +Prado::using('System.Data.Common.TDbCommandBuilder'); + +/** + * TPreparedCommand class. + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.SqlMap.Statements + * @since 3.1 + */ +class TPreparedCommand +{ + public function create(TSqlMapManager $manager, $connection, $statement, $parameterObject,$skip=null,$max=null) + { + $sqlText = $statement->getSQLText(); + + $prepared = $sqlText->getPreparedStatement($parameterObject); + $connection->setActive(true); + $sql = $prepared->getPreparedSql(); + + if($sqlText instanceof TSimpleDynamicSql) + $sql = $sqlText->replaceDynamicParameter($sql, $parameterObject); + + if($max!==null || $skip!==null) + { + $builder = TDbMetaData::getInstance($connection)->createCommandBuilder(); + $sql = $builder->applyLimitOffset($sql,$max,$skip); + } + $command = $connection->createCommand($sql); + $this->applyParameterMap($manager, $command, $prepared, $statement, $parameterObject); + + return $command; + } + + protected function applyParameterMap($manager,$command,$prepared, $statement, $parameterObject) + { + $properties = $prepared->getParameterNames(); + $parameters = $prepared->getParameterValues(); + $registry=$manager->getTypeHandlers(); + for($i = 0, $k=$properties->getCount(); $i<$k; $i++) + { + $property = $statement->parameterMap()->getProperty($i); + $value = $statement->parameterMap()->getPropertyValue($registry,$property, $parameterObject); + $dbType = $property->getDbType(); + if($dbType=='') //relies on PHP lax comparison + $command->bindValue($i+1,$value, TDbCommandBuilder::getPdoType($value)); + else if(strpos($dbType, 'PDO::')===0) + $command->bindValue($i+1,$value, constant($property->getDbType())); //assumes PDO types, e.g. PDO::PARAM_INT + else + $command->bindValue($i+1,$value); + } + } +} + diff --git a/framework/Data/SqlMap/Statements/TPreparedStatement.php b/framework/Data/SqlMap/Statements/TPreparedStatement.php index d091861f..3abd7442 100644 --- a/framework/Data/SqlMap/Statements/TPreparedStatement.php +++ b/framework/Data/SqlMap/Statements/TPreparedStatement.php @@ -1,57 +1,57 @@ -<?php
-/**
- * TPreparedStatement class file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
+<?php +/** + * TPreparedStatement class file. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.SqlMap.Statements
- */
-
-/**
- * TpreparedStatement class.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.SqlMap.Statements
- * @since 3.1
- */
-class TPreparedStatement extends TComponent
-{
- private $_sqlString='';
- private $_parameterNames;
- private $_parameterValues;
-
- public function __construct()
- {
- $this->_parameterNames=new TList;
- $this->_parameterValues=new TMap;
- }
-
- public function getPreparedSql(){ return $this->_sqlString; }
- public function setPreparedSql($value){ $this->_sqlString = $value; }
-
- public function getParameterNames(){ return $this->_parameterNames; }
- public function setParameterNames($value){ $this->_parameterNames = $value; }
-
- public function getParameterValues(){ return $this->_parameterValues; }
- public function setParameterValues($value){ $this->_parameterValues = $value; }
-
- public function __wakeup()
- {
- parent::__wakeup();
- if (!$this->_parameterNames) $this->_parameterNames = new TList;
- if (!$this->_parameterValues) $this->_parameterValues = new TMap;
- }
-
- public function __sleep()
- {
- $exprops = array(); $cn = __CLASS__;
- if (!$this->_parameterNames->getCount()) $exprops[] = "\0$cn\0_parameterNames";
- if (!$this->_parameterValues->getCount()) $exprops[] = "\0$cn\0_parameterValues";
- return array_diff(parent::__sleep(),$exprops);
- }
-}
-
+ * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.SqlMap.Statements + */ + +/** + * TpreparedStatement class. + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.SqlMap.Statements + * @since 3.1 + */ +class TPreparedStatement extends TComponent +{ + private $_sqlString=''; + private $_parameterNames; + private $_parameterValues; + + public function __construct() + { + $this->_parameterNames=new TList; + $this->_parameterValues=new TMap; + } + + public function getPreparedSql(){ return $this->_sqlString; } + public function setPreparedSql($value){ $this->_sqlString = $value; } + + public function getParameterNames(){ return $this->_parameterNames; } + public function setParameterNames($value){ $this->_parameterNames = $value; } + + public function getParameterValues(){ return $this->_parameterValues; } + public function setParameterValues($value){ $this->_parameterValues = $value; } + + public function __wakeup() + { + parent::__wakeup(); + if (!$this->_parameterNames) $this->_parameterNames = new TList; + if (!$this->_parameterValues) $this->_parameterValues = new TMap; + } + + public function __sleep() + { + $exprops = array(); $cn = __CLASS__; + if (!$this->_parameterNames->getCount()) $exprops[] = "\0$cn\0_parameterNames"; + if (!$this->_parameterValues->getCount()) $exprops[] = "\0$cn\0_parameterValues"; + return array_diff(parent::__sleep(),$exprops); + } +} + diff --git a/framework/Data/SqlMap/Statements/TPreparedStatementFactory.php b/framework/Data/SqlMap/Statements/TPreparedStatementFactory.php index 0d8286fa..9e70ba00 100644 --- a/framework/Data/SqlMap/Statements/TPreparedStatementFactory.php +++ b/framework/Data/SqlMap/Statements/TPreparedStatementFactory.php @@ -1,49 +1,49 @@ -<?php
-/**
- * TPreparedStatementFactory class file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.SqlMap.Statements
- */
-
-/**
- * TPreparedStatementFactory class.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.SqlMap.Statements
- * @since 3.1
- */
-class TPreparedStatementFactory
-{
- private $_statement;
- private $_preparedStatement;
- private $_parameterPrefix = 'param';
- private $_commandText;
-
- public function __construct($statement, $sqlString)
- {
- $this->_statement = $statement;
- $this->_commandText = $sqlString;
- }
-
- public function prepare()
- {
- $this->_preparedStatement = new TPreparedStatement();
- $this->_preparedStatement->setPreparedSql($this->_commandText);
- if($this->_statement->parameterMap()!==null)
- $this->createParametersForTextCommand();
- return $this->_preparedStatement;
- }
-
- protected function createParametersForTextCommand()
- {
- foreach($this->_statement->ParameterMap()->getProperties() as $prop)
- $this->_preparedStatement->getParameterNames()->add($prop->getProperty());
- }
-}
-
+<?php +/** + * TPreparedStatementFactory class file. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.SqlMap.Statements + */ + +/** + * TPreparedStatementFactory class. + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.SqlMap.Statements + * @since 3.1 + */ +class TPreparedStatementFactory +{ + private $_statement; + private $_preparedStatement; + private $_parameterPrefix = 'param'; + private $_commandText; + + public function __construct($statement, $sqlString) + { + $this->_statement = $statement; + $this->_commandText = $sqlString; + } + + public function prepare() + { + $this->_preparedStatement = new TPreparedStatement(); + $this->_preparedStatement->setPreparedSql($this->_commandText); + if($this->_statement->parameterMap()!==null) + $this->createParametersForTextCommand(); + return $this->_preparedStatement; + } + + protected function createParametersForTextCommand() + { + foreach($this->_statement->ParameterMap()->getProperties() as $prop) + $this->_preparedStatement->getParameterNames()->add($prop->getProperty()); + } +} + diff --git a/framework/Data/SqlMap/Statements/TSelectMappedStatement.php b/framework/Data/SqlMap/Statements/TSelectMappedStatement.php index 4b1e5c65..5a2b25f0 100644 --- a/framework/Data/SqlMap/Statements/TSelectMappedStatement.php +++ b/framework/Data/SqlMap/Statements/TSelectMappedStatement.php @@ -1,36 +1,36 @@ -<?php
-/**
- * TSelectMappedStatement class.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
+<?php +/** + * TSelectMappedStatement class. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.SqlMap.Statements
- */
-
-/**
- * TSelectMappedStatment class.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.SqlMap.Statements
- * @since 3.1
- */
-class TSelectMappedStatement extends TMappedStatement
-{
- public function executeInsert($connection, $parameter)
- {
- throw new TSqlMapExecutionException(
- 'sqlmap_cannot_execute_insert', get_class($this), $this->getID());
- }
-
- public function executeUpdate($connection, $parameter)
- {
- throw new TSqlMapExecutionException(
- 'sqlmap_cannot_execute_update', get_class($this), $this->getID());
- }
-
-}
-
+ * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.SqlMap.Statements + */ + +/** + * TSelectMappedStatment class. + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.SqlMap.Statements + * @since 3.1 + */ +class TSelectMappedStatement extends TMappedStatement +{ + public function executeInsert($connection, $parameter) + { + throw new TSqlMapExecutionException( + 'sqlmap_cannot_execute_insert', get_class($this), $this->getID()); + } + + public function executeUpdate($connection, $parameter) + { + throw new TSqlMapExecutionException( + 'sqlmap_cannot_execute_update', get_class($this), $this->getID()); + } + +} + diff --git a/framework/Data/SqlMap/Statements/TSimpleDynamicSql.php b/framework/Data/SqlMap/Statements/TSimpleDynamicSql.php index 81ae4d76..377563ed 100644 --- a/framework/Data/SqlMap/Statements/TSimpleDynamicSql.php +++ b/framework/Data/SqlMap/Statements/TSimpleDynamicSql.php @@ -1,40 +1,40 @@ -<?php
-/**
- * TSimpleDynamicSql class file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
+<?php +/** + * TSimpleDynamicSql class file. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.SqlMap.Statements
- */
-
-/**
- * TSimpleDynamicSql class.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.SqlMap.Statements
- * @since 3.1
- */
-class TSimpleDynamicSql extends TStaticSql
-{
- private $_mappings=array();
-
- public function __construct($mappings)
- {
- $this->_mappings = $mappings;
- }
-
- public function replaceDynamicParameter($sql, $parameter)
- {
- foreach($this->_mappings as $property)
- {
- $value = TPropertyAccess::get($parameter, $property);
- $sql = preg_replace('/'.TSimpleDynamicParser::DYNAMIC_TOKEN.'/', str_replace('$', '\$', $value), $sql, 1);
- }
- return $sql;
- }
-}
-
+ * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.SqlMap.Statements + */ + +/** + * TSimpleDynamicSql class. + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.SqlMap.Statements + * @since 3.1 + */ +class TSimpleDynamicSql extends TStaticSql +{ + private $_mappings=array(); + + public function __construct($mappings) + { + $this->_mappings = $mappings; + } + + public function replaceDynamicParameter($sql, $parameter) + { + foreach($this->_mappings as $property) + { + $value = TPropertyAccess::get($parameter, $property); + $sql = preg_replace('/'.TSimpleDynamicParser::DYNAMIC_TOKEN.'/', str_replace('$', '\$', $value), $sql, 1); + } + return $sql; + } +} + diff --git a/framework/Data/SqlMap/Statements/TStaticSql.php b/framework/Data/SqlMap/Statements/TStaticSql.php index e6949c35..6374745d 100644 --- a/framework/Data/SqlMap/Statements/TStaticSql.php +++ b/framework/Data/SqlMap/Statements/TStaticSql.php @@ -1,36 +1,36 @@ -<?php
-/**
- * TStaticSql class file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
+<?php +/** + * TStaticSql class file. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.SqlMap.Statements
- */
-
-/**
- * TStaticSql class.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.SqlMap.Statements
- * @since 3.1
- */
-class TStaticSql extends TComponent
-{
- private $_preparedStatement;
-
- public function buildPreparedStatement($statement, $sqlString)
- {
- $factory = new TPreparedStatementFactory($statement, $sqlString);
- $this->_preparedStatement = $factory->prepare();
- }
-
- public function getPreparedStatement($parameter=null)
- {
- return $this->_preparedStatement;
- }
-}
-
+ * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.SqlMap.Statements + */ + +/** + * TStaticSql class. + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.SqlMap.Statements + * @since 3.1 + */ +class TStaticSql extends TComponent +{ + private $_preparedStatement; + + public function buildPreparedStatement($statement, $sqlString) + { + $factory = new TPreparedStatementFactory($statement, $sqlString); + $this->_preparedStatement = $factory->prepare(); + } + + public function getPreparedStatement($parameter=null) + { + return $this->_preparedStatement; + } +} + diff --git a/framework/Data/SqlMap/Statements/TUpdateMappedStatement.php b/framework/Data/SqlMap/Statements/TUpdateMappedStatement.php index 76bc8ae5..af300a0e 100644 --- a/framework/Data/SqlMap/Statements/TUpdateMappedStatement.php +++ b/framework/Data/SqlMap/Statements/TUpdateMappedStatement.php @@ -1,49 +1,49 @@ -<?php
-/**
- * TUpdateMappedStatement class file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
+<?php +/** + * TUpdateMappedStatement class file. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.SqlMap.Statements
- */
-
-/**
- * TUpdateMappedStatement class.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.SqlMap.Statements
- * @since 3.1
- */
-class TUpdateMappedStatement extends TMappedStatement
-{
- public function executeInsert($connection, $parameter)
- {
- throw new TSqlMapExecutionException(
- 'sqlmap_cannot_execute_insert', get_class($this), $this->getID());
- }
-
- public function executeQueryForMap($connection, $parameter, $keyProperty,
- $valueProperty=null)
- {
- throw new TSqlMapExecutionException(
- 'sqlmap_cannot_execute_query_for_map', get_class($this), $this->getID());
- }
-
- public function executeQueryForList($connection, $parameter, $result=null,
- $skip=-1, $max=-1)
- {
- throw new TSqlMapExecutionException(
- 'sqlmap_cannot_execute_query_for_list', get_class($this), $this->getID());
- }
-
- public function executeQueryForObject($connection, $parameter, $result=null)
- {
- throw new TSqlMapExecutionException(
- 'sqlmap_cannot_execute_query_for_object', get_class($this), $this->getID());
- }
-}
-
+ * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.SqlMap.Statements + */ + +/** + * TUpdateMappedStatement class. + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.SqlMap.Statements + * @since 3.1 + */ +class TUpdateMappedStatement extends TMappedStatement +{ + public function executeInsert($connection, $parameter) + { + throw new TSqlMapExecutionException( + 'sqlmap_cannot_execute_insert', get_class($this), $this->getID()); + } + + public function executeQueryForMap($connection, $parameter, $keyProperty, + $valueProperty=null) + { + throw new TSqlMapExecutionException( + 'sqlmap_cannot_execute_query_for_map', get_class($this), $this->getID()); + } + + public function executeQueryForList($connection, $parameter, $result=null, + $skip=-1, $max=-1) + { + throw new TSqlMapExecutionException( + 'sqlmap_cannot_execute_query_for_list', get_class($this), $this->getID()); + } + + public function executeQueryForObject($connection, $parameter, $result=null) + { + throw new TSqlMapExecutionException( + 'sqlmap_cannot_execute_query_for_object', get_class($this), $this->getID()); + } +} + diff --git a/framework/Data/SqlMap/TSqlMapConfig.php b/framework/Data/SqlMap/TSqlMapConfig.php index 1e9dcdd3..ee3685fe 100644 --- a/framework/Data/SqlMap/TSqlMapConfig.php +++ b/framework/Data/SqlMap/TSqlMapConfig.php @@ -1,181 +1,181 @@ -<?php
-/**
- * TSqlMapConfig class file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
+<?php +/** + * TSqlMapConfig class file. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.SqlMap
- */
-
-Prado::using('System.Data.TDataSourceConfig');
-
-/**
- * TSqlMapConfig module configuration class.
- *
- * Database connection and TSqlMapManager configuration.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.SqlMap
- * @since 3.1
- */
-class TSqlMapConfig extends TDataSourceConfig
-{
- private $_configFile;
- private $_sqlmap;
- private $_enableCache=false;
-
- /**
- * File extension of external configuration file
- */
- const CONFIG_FILE_EXT='.xml';
-
- /**
- * @return string module ID + configuration file path.
- */
- private function getCacheKey()
- {
- return $this->getID().$this->getConfigFile();
- }
-
- /**
- * Deletes the configuration cache.
- */
- public function clearCache()
- {
- $cache = $this->getApplication()->getCache();
- if($cache !== null) {
- $cache->delete($this->getCacheKey());
- }
- }
-
- /**
- * Create and configure the data mapper using sqlmap configuration file.
- * Or if cache is enabled and manager already cached load from cache.
- * If cache is enabled, the data mapper instance is cached.
- *
- * @return TSqlMapManager SqlMap manager instance
- * @since 3.1.7
- */
- public function getSqlMapManager() {
- Prado::using('System.Data.SqlMap.TSqlMapManager');
- if(($manager = $this->loadCachedSqlMapManager())===null)
- {
- $manager = new TSqlMapManager($this->getDbConnection());
- if(strlen($file=$this->getConfigFile()) > 0)
- {
- $manager->configureXml($file);
- $this->cacheSqlMapManager($manager);
- }
- }
- elseif($this->getConnectionID() !== '') {
- $manager->setDbConnection($this->getDbConnection());
- }
- return $manager;
- }
-
- /**
- * Saves the current SqlMap manager to cache.
- * @return boolean true if SqlMap manager was cached, false otherwise.
- */
- protected function cacheSqlMapManager($manager)
- {
- if($this->getEnableCache())
- {
- $cache = $this->getApplication()->getCache();
- if($cache !== null) {
- $dependencies = null;
- if($this->getApplication()->getMode() !== TApplicationMode::Performance)
- $dependencies = $manager->getCacheDependencies();
- return $cache->set($this->getCacheKey(), $manager, 0, $dependencies);
- }
- }
- return false;
- }
-
- /**
- * Loads SqlMap manager from cache.
- * @return TSqlMapManager SqlMap manager intance if load was successful, null otherwise.
- */
- protected function loadCachedSqlMapManager()
- {
- if($this->getEnableCache())
- {
- $cache = $this->getApplication()->getCache();
- if($cache !== null)
- {
- $manager = $cache->get($this->getCacheKey());
- if($manager instanceof TSqlMapManager)
- return $manager;
- }
- }
- return null;
- }
-
- /**
- * @return string SqlMap configuration file.
- */
- public function getConfigFile()
- {
- return $this->_configFile;
- }
-
- /**
- * @param string external configuration file in namespace format. The file
- * extension must be '.xml'.
- * @throws TConfigurationException if the file is invalid.
- */
- public function setConfigFile($value)
- {
- if(is_file($value))
- $this->_configFile=$value;
- else
- {
- $file = Prado::getPathOfNamespace($value,self::CONFIG_FILE_EXT);
- if($file === null || !is_file($file))
- throw new TConfigurationException('sqlmap_configfile_invalid',$value);
- else
- $this->_configFile = $file;
- }
- }
-
- /**
- * Set true to cache sqlmap instances.
- * @param boolean true to cache sqlmap instance.
- */
- public function setEnableCache($value)
- {
- $this->_enableCache = TPropertyValue::ensureBoolean($value);
- }
-
- /**
- * @return boolean true if configuration should be cached, false otherwise.
- */
- public function getEnableCache()
- {
- return $this->_enableCache;
- }
-
- /**
- * @return TSqlMapGateway SqlMap gateway instance.
- */
- protected function createSqlMapGateway()
- {
- return $this->getSqlMapManager()->getSqlmapGateway();
- }
-
- /**
- * Initialize the sqlmap if necessary, returns the TSqlMapGateway instance.
- * @return TSqlMapGateway SqlMap gateway instance.
- */
- public function getClient()
- {
- if($this->_sqlmap===null )
- $this->_sqlmap=$this->createSqlMapGateway();
- return $this->_sqlmap;
- }
-}
-
+ * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.SqlMap + */ + +Prado::using('System.Data.TDataSourceConfig'); + +/** + * TSqlMapConfig module configuration class. + * + * Database connection and TSqlMapManager configuration. + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.SqlMap + * @since 3.1 + */ +class TSqlMapConfig extends TDataSourceConfig +{ + private $_configFile; + private $_sqlmap; + private $_enableCache=false; + + /** + * File extension of external configuration file + */ + const CONFIG_FILE_EXT='.xml'; + + /** + * @return string module ID + configuration file path. + */ + private function getCacheKey() + { + return $this->getID().$this->getConfigFile(); + } + + /** + * Deletes the configuration cache. + */ + public function clearCache() + { + $cache = $this->getApplication()->getCache(); + if($cache !== null) { + $cache->delete($this->getCacheKey()); + } + } + + /** + * Create and configure the data mapper using sqlmap configuration file. + * Or if cache is enabled and manager already cached load from cache. + * If cache is enabled, the data mapper instance is cached. + * + * @return TSqlMapManager SqlMap manager instance + * @since 3.1.7 + */ + public function getSqlMapManager() { + Prado::using('System.Data.SqlMap.TSqlMapManager'); + if(($manager = $this->loadCachedSqlMapManager())===null) + { + $manager = new TSqlMapManager($this->getDbConnection()); + if(strlen($file=$this->getConfigFile()) > 0) + { + $manager->configureXml($file); + $this->cacheSqlMapManager($manager); + } + } + elseif($this->getConnectionID() !== '') { + $manager->setDbConnection($this->getDbConnection()); + } + return $manager; + } + + /** + * Saves the current SqlMap manager to cache. + * @return boolean true if SqlMap manager was cached, false otherwise. + */ + protected function cacheSqlMapManager($manager) + { + if($this->getEnableCache()) + { + $cache = $this->getApplication()->getCache(); + if($cache !== null) { + $dependencies = null; + if($this->getApplication()->getMode() !== TApplicationMode::Performance) + $dependencies = $manager->getCacheDependencies(); + return $cache->set($this->getCacheKey(), $manager, 0, $dependencies); + } + } + return false; + } + + /** + * Loads SqlMap manager from cache. + * @return TSqlMapManager SqlMap manager intance if load was successful, null otherwise. + */ + protected function loadCachedSqlMapManager() + { + if($this->getEnableCache()) + { + $cache = $this->getApplication()->getCache(); + if($cache !== null) + { + $manager = $cache->get($this->getCacheKey()); + if($manager instanceof TSqlMapManager) + return $manager; + } + } + return null; + } + + /** + * @return string SqlMap configuration file. + */ + public function getConfigFile() + { + return $this->_configFile; + } + + /** + * @param string external configuration file in namespace format. The file + * extension must be '.xml'. + * @throws TConfigurationException if the file is invalid. + */ + public function setConfigFile($value) + { + if(is_file($value)) + $this->_configFile=$value; + else + { + $file = Prado::getPathOfNamespace($value,self::CONFIG_FILE_EXT); + if($file === null || !is_file($file)) + throw new TConfigurationException('sqlmap_configfile_invalid',$value); + else + $this->_configFile = $file; + } + } + + /** + * Set true to cache sqlmap instances. + * @param boolean true to cache sqlmap instance. + */ + public function setEnableCache($value) + { + $this->_enableCache = TPropertyValue::ensureBoolean($value); + } + + /** + * @return boolean true if configuration should be cached, false otherwise. + */ + public function getEnableCache() + { + return $this->_enableCache; + } + + /** + * @return TSqlMapGateway SqlMap gateway instance. + */ + protected function createSqlMapGateway() + { + return $this->getSqlMapManager()->getSqlmapGateway(); + } + + /** + * Initialize the sqlmap if necessary, returns the TSqlMapGateway instance. + * @return TSqlMapGateway SqlMap gateway instance. + */ + public function getClient() + { + if($this->_sqlmap===null ) + $this->_sqlmap=$this->createSqlMapGateway(); + return $this->_sqlmap; + } +} + diff --git a/framework/Data/SqlMap/TSqlMapGateway.php b/framework/Data/SqlMap/TSqlMapGateway.php index f1915e86..3be4e12f 100644 --- a/framework/Data/SqlMap/TSqlMapGateway.php +++ b/framework/Data/SqlMap/TSqlMapGateway.php @@ -1,261 +1,261 @@ -<?php
-/**
- * TSqlMapGateway class file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
+<?php +/** + * TSqlMapGateway class file. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.SqlMap
- */
-
-Prado::using('System.Data.SqlMap.TSqlMapManager');
-
-/**
- * DataMapper client, a fascade to provide access the rest of the DataMapper
- * framework. It provides three core functions:
- *
- * # execute an update query (including insert and delete).
- * # execute a select query for a single object
- * # execute a select query for a list of objects
- *
- * This class should be instantiated from a TSqlMapManager instance.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.SqlMap
- * @since 3.1
- */
-class TSqlMapGateway extends TComponent
-{
- /**
- * @var TSqlMapManager manager
- */
- private $_manager;
-
- public function __construct($manager)
- {
- $this->_manager=$manager;
- }
-
- /**
- * @return TSqlMapManager sqlmap manager.
- */
- public function getSqlMapManager()
- {
- return $this->_manager;
- }
-
- /**
- * @return TDbConnection database connection.
- */
- public function getDbConnection()
- {
- return $this->getSqlMapManager()->getDbConnection();
- }
-
- /**
- * Executes a Sql SELECT statement that returns that returns data
- * to populate a single object instance.
- *
- * The parameter object is generally used to supply the input
- * data for the WHERE clause parameter(s) of the SELECT statement.
- *
- * @param string The name of the sql statement to execute.
- * @param mixed The object used to set the parameters in the SQL.
- * @param mixed An object of the type to be returned.
- * @return object A single result object populated with the result set data.
- */
- public function queryForObject($statementName, $parameter=null, $result=null)
- {
- $statement = $this->getSqlMapManager()->getMappedStatement($statementName);
- return $statement->executeQueryForObject($this->getDbConnection(), $parameter, $result);
- }
-
- /**
- * Executes a Sql SELECT statement that returns data to populate a number
- * of result objects.
- *
- * The parameter object is generally used to supply the input
- * data for the WHERE clause parameter(s) of the SELECT statement.
- *
- * @param string The name of the sql statement to execute.
- * @param mixed The object used to set the parameters in the SQL.
- * @param TList An Ilist object used to hold the objects,
- * pass in null if want to return a list instead.
- * @param int The number of rows to skip over.
- * @param int The maximum number of rows to return.
- * @return TList A List of result objects.
- */
- public function queryForList($statementName, $parameter=null, $result=null, $skip=-1, $max=-1)
- {
- $statement = $this->getSqlMapManager()->getMappedStatement($statementName);
- return $statement->executeQueryForList($this->getDbConnection(),$parameter, $result, $skip, $max);
- }
-
- /**
- * Runs a query for list with a custom object that gets a chance to deal
- * with each row as it is processed.
- *
- * Example: $sqlmap->queryWithRowDelegate('getAccounts', array($this, 'rowHandler'));
- *
- * @param string The name of the sql statement to execute.
- * @param callback Row delegate handler, a valid callback required.
- * @param mixed The object used to set the parameters in the SQL.
- * @param TList An Ilist object used to hold the objects,
- * pass in null if want to return a list instead.
- * @param int The number of rows to skip over.
- * @param int The maximum number of rows to return.
- * @return TList A List of result objects.
- */
- public function queryWithRowDelegate($statementName, $delegate, $parameter=null, $result=null, $skip=-1, $max=-1)
- {
- $statement = $this->getSqlMapManager()->getMappedStatement($statementName);
- return $statement->executeQueryForList($this->getDbConnection(), $parameter, $result, $skip, $max, $delegate);
- }
-
- /**
- * Executes the SQL and retuns a subset of the results in a dynamic
- * TPagedList that can be used to automatically scroll through results
- * from a database table.
- * @param string The name of the sql statement to execute.
- * @param mixed The object used to set the parameters in the SQL.
- * @param integer The maximum number of objects to store in each page.
+ * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.SqlMap + */ + +Prado::using('System.Data.SqlMap.TSqlMapManager'); + +/** + * DataMapper client, a fascade to provide access the rest of the DataMapper + * framework. It provides three core functions: + * + * # execute an update query (including insert and delete). + * # execute a select query for a single object + * # execute a select query for a list of objects + * + * This class should be instantiated from a TSqlMapManager instance. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.SqlMap + * @since 3.1 + */ +class TSqlMapGateway extends TComponent +{ + /** + * @var TSqlMapManager manager + */ + private $_manager; + + public function __construct($manager) + { + $this->_manager=$manager; + } + + /** + * @return TSqlMapManager sqlmap manager. + */ + public function getSqlMapManager() + { + return $this->_manager; + } + + /** + * @return TDbConnection database connection. + */ + public function getDbConnection() + { + return $this->getSqlMapManager()->getDbConnection(); + } + + /** + * Executes a Sql SELECT statement that returns that returns data + * to populate a single object instance. + * + * The parameter object is generally used to supply the input + * data for the WHERE clause parameter(s) of the SELECT statement. + * + * @param string The name of the sql statement to execute. + * @param mixed The object used to set the parameters in the SQL. + * @param mixed An object of the type to be returned. + * @return object A single result object populated with the result set data. + */ + public function queryForObject($statementName, $parameter=null, $result=null) + { + $statement = $this->getSqlMapManager()->getMappedStatement($statementName); + return $statement->executeQueryForObject($this->getDbConnection(), $parameter, $result); + } + + /** + * Executes a Sql SELECT statement that returns data to populate a number + * of result objects. + * + * The parameter object is generally used to supply the input + * data for the WHERE clause parameter(s) of the SELECT statement. + * + * @param string The name of the sql statement to execute. + * @param mixed The object used to set the parameters in the SQL. + * @param TList An Ilist object used to hold the objects, + * pass in null if want to return a list instead. + * @param int The number of rows to skip over. + * @param int The maximum number of rows to return. + * @return TList A List of result objects. + */ + public function queryForList($statementName, $parameter=null, $result=null, $skip=-1, $max=-1) + { + $statement = $this->getSqlMapManager()->getMappedStatement($statementName); + return $statement->executeQueryForList($this->getDbConnection(),$parameter, $result, $skip, $max); + } + + /** + * Runs a query for list with a custom object that gets a chance to deal + * with each row as it is processed. + * + * Example: $sqlmap->queryWithRowDelegate('getAccounts', array($this, 'rowHandler')); + * + * @param string The name of the sql statement to execute. + * @param callback Row delegate handler, a valid callback required. + * @param mixed The object used to set the parameters in the SQL. + * @param TList An Ilist object used to hold the objects, + * pass in null if want to return a list instead. + * @param int The number of rows to skip over. + * @param int The maximum number of rows to return. + * @return TList A List of result objects. + */ + public function queryWithRowDelegate($statementName, $delegate, $parameter=null, $result=null, $skip=-1, $max=-1) + { + $statement = $this->getSqlMapManager()->getMappedStatement($statementName); + return $statement->executeQueryForList($this->getDbConnection(), $parameter, $result, $skip, $max, $delegate); + } + + /** + * Executes the SQL and retuns a subset of the results in a dynamic + * TPagedList that can be used to automatically scroll through results + * from a database table. + * @param string The name of the sql statement to execute. + * @param mixed The object used to set the parameters in the SQL. + * @param integer The maximum number of objects to store in each page. * @param integer The number of the page to initially load into the list. - * @return TPagedList A PaginatedList of beans containing the rows.
- */
- public function queryForPagedList($statementName, $parameter=null, $pageSize=10, $page=0)
- {
- $statement = $this->getSqlMapManager()->getMappedStatement($statementName);
- return new TSqlMapPagedList($statement, $parameter, $pageSize, null, $page);
- }
-
- /**
- * Executes the SQL and retuns a subset of the results in a dynamic
- * TPagedList that can be used to automatically scroll through results
- * from a database table.
- *
- * Runs paged list query with row delegate
- * Example: $sqlmap->queryForPagedListWithRowDelegate('getAccounts', array($this, 'rowHandler'));
- *
- * @param string The name of the sql statement to execute.
- * @param callback Row delegate handler, a valid callback required.
- * @param mixed The object used to set the parameters in the SQL.
- * @param integer The maximum number of objects to store in each page.
+ * @return TPagedList A PaginatedList of beans containing the rows. + */ + public function queryForPagedList($statementName, $parameter=null, $pageSize=10, $page=0) + { + $statement = $this->getSqlMapManager()->getMappedStatement($statementName); + return new TSqlMapPagedList($statement, $parameter, $pageSize, null, $page); + } + + /** + * Executes the SQL and retuns a subset of the results in a dynamic + * TPagedList that can be used to automatically scroll through results + * from a database table. + * + * Runs paged list query with row delegate + * Example: $sqlmap->queryForPagedListWithRowDelegate('getAccounts', array($this, 'rowHandler')); + * + * @param string The name of the sql statement to execute. + * @param callback Row delegate handler, a valid callback required. + * @param mixed The object used to set the parameters in the SQL. + * @param integer The maximum number of objects to store in each page. * @param integer The number of the page to initially load into the list. - * @return TPagedList A PaginatedList of beans containing the rows.
- */
- public function queryForPagedListWithRowDelegate($statementName,$delegate, $parameter=null, $pageSize=10, $page=0)
- {
- $statement = $this->getSqlMapManager()->getMappedStatement($statementName);
- return new TSqlMapPagedList($statement, $parameter, $pageSize, $delegate,$page);
- }
-
-
- /**
- * Executes the SQL and retuns all rows selected in a map that is keyed on
- * the property named in the keyProperty parameter. The value at each key
- * will be the value of the property specified in the valueProperty
- * parameter. If valueProperty is null, the entire result object will be
- * entered.
- * @param string The name of the sql statement to execute.
- * @param mixed The object used to set the parameters in the SQL.
- * @param string The property of the result object to be used as the key.
- * @param string The property of the result object to be used as the value.
- * @return TMap Array object containing the rows keyed by keyProperty.
- */
- public function queryForMap($statementName, $parameter=null, $keyProperty=null, $valueProperty=null, $skip=-1, $max=-1)
- {
- $statement = $this->getSqlMapManager()->getMappedStatement($statementName);
- return $statement->executeQueryForMap($this->getDbConnection(), $parameter, $keyProperty, $valueProperty, $skip, $max);
- }
-
- /**
- * Runs a query with a custom object that gets a chance to deal
- * with each row as it is processed.
- *
- * Example: $sqlmap->queryForMapWithRowDelegate('getAccounts', array($this, 'rowHandler'));
- *
- * @param string The name of the sql statement to execute.
- * @param callback Row delegate handler, a valid callback required.
- * @param mixed The object used to set the parameters in the SQL.
- * @param string The property of the result object to be used as the key.
- * @param string The property of the result object to be used as the value.
- * @return TMap Array object containing the rows keyed by keyProperty.
- */
- public function queryForMapWithRowDelegate($statementName, $delegate, $parameter=null, $keyProperty=null, $valueProperty=null, $skip=-1, $max=-1)
- {
- $statement = $this->getSqlMapManager()->getMappedStatement($statementName);
- return $statement->executeQueryForMap($this->getDbConnection(), $parameter, $keyProperty, $valueProperty, $skip, $max, $delegate);
- }
-
- /**
- * Executes a Sql INSERT statement.
- *
- * Insert is a bit different from other update methods, as it provides
- * facilities for returning the primary key of the newly inserted row
- * (rather than the effected rows),
- *
- * The parameter object is generally used to supply the input data for the
- * INSERT values.
- *
- * @param string The name of the statement to execute.
- * @param string The parameter object.
- * @return mixed The primary key of the newly inserted row.
- * This might be automatically generated by the RDBMS,
- * or selected from a sequence table or other source.
- */
- public function insert($statementName, $parameter=null)
- {
- $statement = $this->getSqlMapManager()->getMappedStatement($statementName);
- return $statement->executeInsert($this->getDbConnection(), $parameter);
- }
-
- /**
- * Executes a Sql UPDATE statement.
- *
- * Update can also be used for any other update statement type, such as
- * inserts and deletes. Update returns the number of rows effected.
- *
- * The parameter object is generally used to supply the input data for the
- * UPDATE values as well as the WHERE clause parameter(s).
- *
- * @param string The name of the statement to execute.
- * @param mixed The parameter object.
- * @return integer The number of rows effected.
- */
- public function update($statementName, $parameter=null)
- {
- $statement = $this->getSqlMapManager()->getMappedStatement($statementName);
- return $statement->executeUpdate($this->getDbConnection(), $parameter);
- }
-
- /**
- * Executes a Sql DELETE statement. Delete returns the number of rows effected.
- * @param string The name of the statement to execute.
- * @param mixed The parameter object.
- * @return integer The number of rows effected.
- */
- public function delete($statementName, $parameter=null)
- {
- return $this->update($statementName, $parameter);
- }
-
- /**
- * Flushes all cached objects that belong to this SqlMap
- */
- public function flushCaches()
- {
- $this->getSqlMapManager()->flushCacheModels();
- }
-
- /**
- * @param TSqlMapTypeHandler new type handler.
- */
- public function registerTypeHandler($typeHandler)
- {
- $this->getSqlMapManager()->getTypeHandlers()->registerTypeHandler($typeHandler);
- }
-}
-
+ * @return TPagedList A PaginatedList of beans containing the rows. + */ + public function queryForPagedListWithRowDelegate($statementName,$delegate, $parameter=null, $pageSize=10, $page=0) + { + $statement = $this->getSqlMapManager()->getMappedStatement($statementName); + return new TSqlMapPagedList($statement, $parameter, $pageSize, $delegate,$page); + } + + + /** + * Executes the SQL and retuns all rows selected in a map that is keyed on + * the property named in the keyProperty parameter. The value at each key + * will be the value of the property specified in the valueProperty + * parameter. If valueProperty is null, the entire result object will be + * entered. + * @param string The name of the sql statement to execute. + * @param mixed The object used to set the parameters in the SQL. + * @param string The property of the result object to be used as the key. + * @param string The property of the result object to be used as the value. + * @return TMap Array object containing the rows keyed by keyProperty. + */ + public function queryForMap($statementName, $parameter=null, $keyProperty=null, $valueProperty=null, $skip=-1, $max=-1) + { + $statement = $this->getSqlMapManager()->getMappedStatement($statementName); + return $statement->executeQueryForMap($this->getDbConnection(), $parameter, $keyProperty, $valueProperty, $skip, $max); + } + + /** + * Runs a query with a custom object that gets a chance to deal + * with each row as it is processed. + * + * Example: $sqlmap->queryForMapWithRowDelegate('getAccounts', array($this, 'rowHandler')); + * + * @param string The name of the sql statement to execute. + * @param callback Row delegate handler, a valid callback required. + * @param mixed The object used to set the parameters in the SQL. + * @param string The property of the result object to be used as the key. + * @param string The property of the result object to be used as the value. + * @return TMap Array object containing the rows keyed by keyProperty. + */ + public function queryForMapWithRowDelegate($statementName, $delegate, $parameter=null, $keyProperty=null, $valueProperty=null, $skip=-1, $max=-1) + { + $statement = $this->getSqlMapManager()->getMappedStatement($statementName); + return $statement->executeQueryForMap($this->getDbConnection(), $parameter, $keyProperty, $valueProperty, $skip, $max, $delegate); + } + + /** + * Executes a Sql INSERT statement. + * + * Insert is a bit different from other update methods, as it provides + * facilities for returning the primary key of the newly inserted row + * (rather than the effected rows), + * + * The parameter object is generally used to supply the input data for the + * INSERT values. + * + * @param string The name of the statement to execute. + * @param string The parameter object. + * @return mixed The primary key of the newly inserted row. + * This might be automatically generated by the RDBMS, + * or selected from a sequence table or other source. + */ + public function insert($statementName, $parameter=null) + { + $statement = $this->getSqlMapManager()->getMappedStatement($statementName); + return $statement->executeInsert($this->getDbConnection(), $parameter); + } + + /** + * Executes a Sql UPDATE statement. + * + * Update can also be used for any other update statement type, such as + * inserts and deletes. Update returns the number of rows effected. + * + * The parameter object is generally used to supply the input data for the + * UPDATE values as well as the WHERE clause parameter(s). + * + * @param string The name of the statement to execute. + * @param mixed The parameter object. + * @return integer The number of rows effected. + */ + public function update($statementName, $parameter=null) + { + $statement = $this->getSqlMapManager()->getMappedStatement($statementName); + return $statement->executeUpdate($this->getDbConnection(), $parameter); + } + + /** + * Executes a Sql DELETE statement. Delete returns the number of rows effected. + * @param string The name of the statement to execute. + * @param mixed The parameter object. + * @return integer The number of rows effected. + */ + public function delete($statementName, $parameter=null) + { + return $this->update($statementName, $parameter); + } + + /** + * Flushes all cached objects that belong to this SqlMap + */ + public function flushCaches() + { + $this->getSqlMapManager()->flushCacheModels(); + } + + /** + * @param TSqlMapTypeHandler new type handler. + */ + public function registerTypeHandler($typeHandler) + { + $this->getSqlMapManager()->getTypeHandlers()->registerTypeHandler($typeHandler); + } +} + diff --git a/framework/Data/SqlMap/TSqlMapManager.php b/framework/Data/SqlMap/TSqlMapManager.php index e27ad079..d8a16407 100644 --- a/framework/Data/SqlMap/TSqlMapManager.php +++ b/framework/Data/SqlMap/TSqlMapManager.php @@ -1,274 +1,274 @@ -<?php
-/**
- * TSqlMapManager class file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
+<?php +/** + * TSqlMapManager class file. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.SqlMap
- */
-
-Prado::using('System.Data.SqlMap.TSqlMapGateway');
-Prado::using('System.Data.SqlMap.DataMapper.TSqlMapException');
-Prado::using('System.Data.SqlMap.DataMapper.TSqlMapTypeHandlerRegistry');
-Prado::using('System.Data.SqlMap.DataMapper.TSqlMapCache');
-Prado::using('System.Data.SqlMap.Configuration.TSqlMapStatement');
-Prado::using('System.Data.SqlMap.Configuration.*');
-Prado::using('System.Data.SqlMap.DataMapper.*');
-Prado::using('System.Data.SqlMap.Statements.*');
-Prado::using('System.Caching.TCache');
-
-
-/**
- * TSqlMapManager class holds the sqlmap configuation result maps, statements
- * parameter maps and a type handler factory.
- *
- * Use {@link SqlMapGateway getSqlMapGateway()} property to obtain the gateway
- * instance used for querying statements defined in the SqlMap configuration files.
- *
- * <code>
- * $conn = new TDbConnection($dsn,$dbuser,$dbpass);
- * $manager = new TSqlMapManager($conn);
- * $manager->configureXml('mydb-sqlmap.xml');
- * $sqlmap = $manager->getSqlMapGateway();
- * $result = $sqlmap->queryForObject('Products');
- * </code>
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.SqlMap
- * @since 3.1
- */
-class TSqlMapManager extends TComponent
-{
- private $_mappedStatements;
- private $_resultMaps;
- private $_parameterMaps;
- private $_typeHandlers;
- private $_cacheModels;
-
- private $_connection;
- private $_gateway;
- private $_cacheDependencies;
-
- /**
- * Constructor, create a new SqlMap manager.
- * @param TDbConnection database connection
- * @param string configuration file.
- */
- public function __construct($connection=null)
- {
- $this->_connection=$connection;
-
- $this->_mappedStatements=new TMap;
- $this->_resultMaps=new TMap;
- $this->_parameterMaps=new TMap;
- $this->_cacheModels=new TMap;
- }
-
- /**
- * @param TDbConnection default database connection
- */
- public function setDbConnection($conn)
- {
- $this->_connection=$conn;
- }
-
- /**
- * @return TDbConnection default database connection
- */
- public function getDbConnection()
- {
- return $this->_connection;
- }
-
- /**
- * @return TTypeHandlerFactory The TypeHandlerFactory
- */
- public function getTypeHandlers()
- {
- if($this->_typeHandlers===null)
- $this->_typeHandlers= new TSqlMapTypeHandlerRegistry();
- return $this->_typeHandlers;
- }
-
- /**
- * @return TSqlMapGateway SqlMap gateway.
- */
- public function getSqlmapGateway()
- {
- if($this->_gateway===null)
- $this->_gateway=$this->createSqlMapGateway();
- return $this->_gateway;
- }
-
- /**
- * Loads and parses the SqlMap configuration file.
- * @param string xml configuration file.
- */
- public function configureXml($file)
- {
- $config = new TSqlMapXmlConfiguration($this);
- $config->configure($file);
- }
-
- /**
- * @return TChainedCacheDependency
- * @since 3.1.5
- */
- public function getCacheDependencies()
- {
- if($this->_cacheDependencies === null)
- $this->_cacheDependencies=new TChainedCacheDependency();
-
- return $this->_cacheDependencies;
- }
-
- /**
- * Configures the current TSqlMapManager using the given xml configuration file
- * defined in {@link ConfigFile setConfigFile()}.
- * @return TSqlMapGateway create and configure a new TSqlMapGateway.
- */
- protected function createSqlMapGateway()
- {
- return new TSqlMapGateway($this);
- }
-
- /**
- * @return TMap mapped statements collection.
- */
- public function getMappedStatements()
- {
- return $this->_mappedStatements;
- }
-
- /**
- * Gets a MappedStatement by name.
- * @param string The name of the statement.
- * @return IMappedStatement The MappedStatement
- * @throws TSqlMapUndefinedException
- */
- public function getMappedStatement($name)
- {
- if($this->_mappedStatements->contains($name) == false)
- throw new TSqlMapUndefinedException('sqlmap_contains_no_statement', $name);
- return $this->_mappedStatements[$name];
- }
-
- /**
- * Adds a (named) MappedStatement.
- * @param string The key name
- * @param IMappedStatement The statement to add
- * @throws TSqlMapDuplicateException
- */
- public function addMappedStatement(IMappedStatement $statement)
- {
- $key = $statement->getID();
- if($this->_mappedStatements->contains($key) == true)
- throw new TSqlMapDuplicateException('sqlmap_already_contains_statement', $key);
- $this->_mappedStatements->add($key, $statement);
- }
-
- /**
- * @return TMap result maps collection.
- */
- public function getResultMaps()
- {
- return $this->_resultMaps;
- }
-
- /**
- * Gets a named result map
- * @param string result name.
- * @return TResultMap the result map.
- * @throws TSqlMapUndefinedException
- */
- public function getResultMap($name)
- {
- if($this->_resultMaps->contains($name) == false)
- throw new TSqlMapUndefinedException('sqlmap_contains_no_result_map', $name);
- return $this->_resultMaps[$name];
- }
-
- /**
- * @param TResultMap add a new result map to this SQLMap
- * @throws TSqlMapDuplicateException
- */
- public function addResultMap(TResultMap $result)
- {
- $key = $result->getID();
- if($this->_resultMaps->contains($key) == true)
- throw new TSqlMapDuplicateException('sqlmap_already_contains_result_map', $key);
- $this->_resultMaps->add($key, $result);
- }
-
- /**
- * @return TMap parameter maps collection.
- */
- public function getParameterMaps()
- {
- return $this->_parameterMaps;
- }
-
- /**
- * @param string parameter map ID name.
- * @return TParameterMap the parameter with given ID.
- * @throws TSqlMapUndefinedException
- */
- public function getParameterMap($name)
- {
- if($this->_parameterMaps->contains($name) == false)
- throw new TSqlMapUndefinedException('sqlmap_contains_no_parameter_map', $name);
- return $this->_parameterMaps[$name];
- }
-
- /**
- * @param TParameterMap add a new parameter map to this SQLMap.
- * @throws TSqlMapDuplicateException
- */
- public function addParameterMap(TParameterMap $parameter)
- {
- $key = $parameter->getID();
- if($this->_parameterMaps->contains($key) == true)
- throw new TSqlMapDuplicateException('sqlmap_already_contains_parameter_map', $key);
- $this->_parameterMaps->add($key, $parameter);
- }
-
- /**
- * Adds a named cache.
- * @param TSqlMapCacheModel the cache to add.
- * @throws TSqlMapConfigurationException
- */
- public function addCacheModel(TSqlMapCacheModel $cacheModel)
- {
- if($this->_cacheModels->contains($cacheModel->getID()))
- throw new TSqlMapConfigurationException('sqlmap_cache_model_already_exists', $cacheModel->getID());
- else
- $this->_cacheModels->add($cacheModel->getID(), $cacheModel);
- }
-
- /**
- * Gets a cache by name
- * @param string the name of the cache to get.
- * @return TSqlMapCacheModel the cache object.
- * @throws TSqlMapConfigurationException
- */
- public function getCacheModel($name)
- {
- if(!$this->_cacheModels->contains($name))
- throw new TSqlMapConfigurationException('sqlmap_unable_to_find_cache_model', $name);
- return $this->_cacheModels[$name];
- }
-
- /**
- * Flushes all cached objects that belong to this SqlMap
- */
- public function flushCacheModels()
- {
- foreach($this->_cacheModels as $cache)
- $cache->flush();
- }
-}
-
+ * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.SqlMap + */ + +Prado::using('System.Data.SqlMap.TSqlMapGateway'); +Prado::using('System.Data.SqlMap.DataMapper.TSqlMapException'); +Prado::using('System.Data.SqlMap.DataMapper.TSqlMapTypeHandlerRegistry'); +Prado::using('System.Data.SqlMap.DataMapper.TSqlMapCache'); +Prado::using('System.Data.SqlMap.Configuration.TSqlMapStatement'); +Prado::using('System.Data.SqlMap.Configuration.*'); +Prado::using('System.Data.SqlMap.DataMapper.*'); +Prado::using('System.Data.SqlMap.Statements.*'); +Prado::using('System.Caching.TCache'); + + +/** + * TSqlMapManager class holds the sqlmap configuation result maps, statements + * parameter maps and a type handler factory. + * + * Use {@link SqlMapGateway getSqlMapGateway()} property to obtain the gateway + * instance used for querying statements defined in the SqlMap configuration files. + * + * <code> + * $conn = new TDbConnection($dsn,$dbuser,$dbpass); + * $manager = new TSqlMapManager($conn); + * $manager->configureXml('mydb-sqlmap.xml'); + * $sqlmap = $manager->getSqlMapGateway(); + * $result = $sqlmap->queryForObject('Products'); + * </code> + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.SqlMap + * @since 3.1 + */ +class TSqlMapManager extends TComponent +{ + private $_mappedStatements; + private $_resultMaps; + private $_parameterMaps; + private $_typeHandlers; + private $_cacheModels; + + private $_connection; + private $_gateway; + private $_cacheDependencies; + + /** + * Constructor, create a new SqlMap manager. + * @param TDbConnection database connection + * @param string configuration file. + */ + public function __construct($connection=null) + { + $this->_connection=$connection; + + $this->_mappedStatements=new TMap; + $this->_resultMaps=new TMap; + $this->_parameterMaps=new TMap; + $this->_cacheModels=new TMap; + } + + /** + * @param TDbConnection default database connection + */ + public function setDbConnection($conn) + { + $this->_connection=$conn; + } + + /** + * @return TDbConnection default database connection + */ + public function getDbConnection() + { + return $this->_connection; + } + + /** + * @return TTypeHandlerFactory The TypeHandlerFactory + */ + public function getTypeHandlers() + { + if($this->_typeHandlers===null) + $this->_typeHandlers= new TSqlMapTypeHandlerRegistry(); + return $this->_typeHandlers; + } + + /** + * @return TSqlMapGateway SqlMap gateway. + */ + public function getSqlmapGateway() + { + if($this->_gateway===null) + $this->_gateway=$this->createSqlMapGateway(); + return $this->_gateway; + } + + /** + * Loads and parses the SqlMap configuration file. + * @param string xml configuration file. + */ + public function configureXml($file) + { + $config = new TSqlMapXmlConfiguration($this); + $config->configure($file); + } + + /** + * @return TChainedCacheDependency + * @since 3.1.5 + */ + public function getCacheDependencies() + { + if($this->_cacheDependencies === null) + $this->_cacheDependencies=new TChainedCacheDependency(); + + return $this->_cacheDependencies; + } + + /** + * Configures the current TSqlMapManager using the given xml configuration file + * defined in {@link ConfigFile setConfigFile()}. + * @return TSqlMapGateway create and configure a new TSqlMapGateway. + */ + protected function createSqlMapGateway() + { + return new TSqlMapGateway($this); + } + + /** + * @return TMap mapped statements collection. + */ + public function getMappedStatements() + { + return $this->_mappedStatements; + } + + /** + * Gets a MappedStatement by name. + * @param string The name of the statement. + * @return IMappedStatement The MappedStatement + * @throws TSqlMapUndefinedException + */ + public function getMappedStatement($name) + { + if($this->_mappedStatements->contains($name) == false) + throw new TSqlMapUndefinedException('sqlmap_contains_no_statement', $name); + return $this->_mappedStatements[$name]; + } + + /** + * Adds a (named) MappedStatement. + * @param string The key name + * @param IMappedStatement The statement to add + * @throws TSqlMapDuplicateException + */ + public function addMappedStatement(IMappedStatement $statement) + { + $key = $statement->getID(); + if($this->_mappedStatements->contains($key) == true) + throw new TSqlMapDuplicateException('sqlmap_already_contains_statement', $key); + $this->_mappedStatements->add($key, $statement); + } + + /** + * @return TMap result maps collection. + */ + public function getResultMaps() + { + return $this->_resultMaps; + } + + /** + * Gets a named result map + * @param string result name. + * @return TResultMap the result map. + * @throws TSqlMapUndefinedException + */ + public function getResultMap($name) + { + if($this->_resultMaps->contains($name) == false) + throw new TSqlMapUndefinedException('sqlmap_contains_no_result_map', $name); + return $this->_resultMaps[$name]; + } + + /** + * @param TResultMap add a new result map to this SQLMap + * @throws TSqlMapDuplicateException + */ + public function addResultMap(TResultMap $result) + { + $key = $result->getID(); + if($this->_resultMaps->contains($key) == true) + throw new TSqlMapDuplicateException('sqlmap_already_contains_result_map', $key); + $this->_resultMaps->add($key, $result); + } + + /** + * @return TMap parameter maps collection. + */ + public function getParameterMaps() + { + return $this->_parameterMaps; + } + + /** + * @param string parameter map ID name. + * @return TParameterMap the parameter with given ID. + * @throws TSqlMapUndefinedException + */ + public function getParameterMap($name) + { + if($this->_parameterMaps->contains($name) == false) + throw new TSqlMapUndefinedException('sqlmap_contains_no_parameter_map', $name); + return $this->_parameterMaps[$name]; + } + + /** + * @param TParameterMap add a new parameter map to this SQLMap. + * @throws TSqlMapDuplicateException + */ + public function addParameterMap(TParameterMap $parameter) + { + $key = $parameter->getID(); + if($this->_parameterMaps->contains($key) == true) + throw new TSqlMapDuplicateException('sqlmap_already_contains_parameter_map', $key); + $this->_parameterMaps->add($key, $parameter); + } + + /** + * Adds a named cache. + * @param TSqlMapCacheModel the cache to add. + * @throws TSqlMapConfigurationException + */ + public function addCacheModel(TSqlMapCacheModel $cacheModel) + { + if($this->_cacheModels->contains($cacheModel->getID())) + throw new TSqlMapConfigurationException('sqlmap_cache_model_already_exists', $cacheModel->getID()); + else + $this->_cacheModels->add($cacheModel->getID(), $cacheModel); + } + + /** + * Gets a cache by name + * @param string the name of the cache to get. + * @return TSqlMapCacheModel the cache object. + * @throws TSqlMapConfigurationException + */ + public function getCacheModel($name) + { + if(!$this->_cacheModels->contains($name)) + throw new TSqlMapConfigurationException('sqlmap_unable_to_find_cache_model', $name); + return $this->_cacheModels[$name]; + } + + /** + * Flushes all cached objects that belong to this SqlMap + */ + public function flushCacheModels() + { + foreach($this->_cacheModels as $cache) + $cache->flush(); + } +} + diff --git a/framework/Data/TDataSourceConfig.php b/framework/Data/TDataSourceConfig.php index a880c522..84d23dc6 100644 --- a/framework/Data/TDataSourceConfig.php +++ b/framework/Data/TDataSourceConfig.php @@ -1,167 +1,167 @@ -<?php
-/**
- * TDataSourceConfig class file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data
- */
-
-Prado::using('System.Data.TDbConnection');
-
-/**
- * TDataSourceConfig module class provides <module> configuration for database connections.
- *
- * Example usage: mysql connection
- * <code>
- * <modules>
- * <module id="db1">
- * <database ConnectionString="mysqli:host=localhost;dbname=test"
- * username="dbuser" password="dbpass" />
- * </module>
- * </modules>
- * </code>
- *
- * Usage in php:
- * <code>
- * class Home extends TPage
- * {
- * function onLoad($param)
- * {
- * $db = $this->Application->Modules['db1']->DbConnection;
- * $db->createCommand('...'); //...
- * }
- * }
- * </code>
- *
- * The properties of <connection> are those of the class TDbConnection.
- * Set {@link setConnectionClass} attribute for a custom database connection class
- * that extends the TDbConnection class.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data
- * @since 3.1
- */
-class TDataSourceConfig extends TModule
-{
- private $_connID='';
- private $_conn;
- private $_connClass='System.Data.TDbConnection';
-
- /**
- * Initalize the database connection properties from attributes in <database> tag.
- * @param TXmlDocument xml configuration.
- */
- public function init($xml)
- {
- if($this->getApplication()->getConfigurationType()==TApplication::CONFIG_TYPE_PHP)
- {
- if(isset($xml['database']) && is_array($xml['database']))
- {
- $db=$this->getDbConnection();
- foreach($xml['database'] as $name=>$value)
- $db->setSubProperty($name,$value);
- }
- }
- else
- {
- if($prop=$xml->getElementByTagName('database'))
- {
- $db=$this->getDbConnection();
- foreach($prop->getAttributes() as $name=>$value)
- $db->setSubproperty($name,$value);
- }
- }
- }
-
- /**
- * The module ID of another TDataSourceConfig. The {@link getDbConnection DbConnection}
- * property of this configuration will equal to {@link getDbConnection DbConnection}
- * of the given TDataSourceConfig module.
- * @param string module ID.
- */
- public function setConnectionID($value)
- {
- $this->_connID=$value;
- }
-
- /**
- * @return string connection module ID.
- */
- public function getConnectionID()
- {
- return $this->_connID;
- }
-
- /**
- * Gets the TDbConnection from another module if {@link setConnectionID ConnectionID}
- * is supplied and valid. Otherwise, a connection of type given by
- * {@link setConnectionClass ConnectionClass} is created.
- * @return TDbConnection database connection.
- */
- public function getDbConnection()
- {
- if($this->_conn===null)
- {
- if($this->_connID!=='')
- $this->_conn = $this->findConnectionByID($this->getConnectionID());
- else
- $this->_conn = Prado::createComponent($this->getConnectionClass());
- }
- return $this->_conn;
- }
-
- /**
- * Alias for getDbConnection().
- * @return TDbConnection database connection.
- */
- public function getDatabase()
- {
- return $this->getDbConnection();
- }
-
- /**
- * @param string Database connection class name to be created.
- */
- public function getConnectionClass()
- {
- return $this->_connClass;
- }
-
- /**
- * The database connection class name to be created when {@link getDbConnection}
- * method is called <b>and</b> {@link setConnectionID ConnectionID} is null. The
- * {@link setConnectionClass ConnectionClass} property must be set before
- * calling {@link getDbConnection} if you wish to create the connection using the
- * given class name.
- * @param string Database connection class name.
- * @throws TConfigurationException when database connection is already established.
- */
- public function setConnectionClass($value)
- {
- if($this->_conn!==null)
- throw new TConfigurationException('datasource_dbconnection_exists', $value);
- $this->_connClass=$value;
- }
-
- /**
- * Finds the database connection instance from the Application modules.
- * @param string Database connection module ID.
- * @return TDbConnection database connection.
- * @throws TConfigurationException when module is not of TDbConnection or TDataSourceConfig.
- */
- protected function findConnectionByID($id)
- {
- $conn = $this->getApplication()->getModule($id);
- if($conn instanceof TDbConnection)
- return $conn;
- else if($conn instanceof TDataSourceConfig)
- return $conn->getDbConnection();
- else
- throw new TConfigurationException('datasource_dbconnection_invalid',$id);
- }
-}
+<?php +/** + * TDataSourceConfig class file. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data + */ + +Prado::using('System.Data.TDbConnection'); + +/** + * TDataSourceConfig module class provides <module> configuration for database connections. + * + * Example usage: mysql connection + * <code> + * <modules> + * <module id="db1"> + * <database ConnectionString="mysqli:host=localhost;dbname=test" + * username="dbuser" password="dbpass" /> + * </module> + * </modules> + * </code> + * + * Usage in php: + * <code> + * class Home extends TPage + * { + * function onLoad($param) + * { + * $db = $this->Application->Modules['db1']->DbConnection; + * $db->createCommand('...'); //... + * } + * } + * </code> + * + * The properties of <connection> are those of the class TDbConnection. + * Set {@link setConnectionClass} attribute for a custom database connection class + * that extends the TDbConnection class. + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id$ + * @package System.Data + * @since 3.1 + */ +class TDataSourceConfig extends TModule +{ + private $_connID=''; + private $_conn; + private $_connClass='System.Data.TDbConnection'; + + /** + * Initalize the database connection properties from attributes in <database> tag. + * @param TXmlDocument xml configuration. + */ + public function init($xml) + { + if($this->getApplication()->getConfigurationType()==TApplication::CONFIG_TYPE_PHP) + { + if(isset($xml['database']) && is_array($xml['database'])) + { + $db=$this->getDbConnection(); + foreach($xml['database'] as $name=>$value) + $db->setSubProperty($name,$value); + } + } + else + { + if($prop=$xml->getElementByTagName('database')) + { + $db=$this->getDbConnection(); + foreach($prop->getAttributes() as $name=>$value) + $db->setSubproperty($name,$value); + } + } + } + + /** + * The module ID of another TDataSourceConfig. The {@link getDbConnection DbConnection} + * property of this configuration will equal to {@link getDbConnection DbConnection} + * of the given TDataSourceConfig module. + * @param string module ID. + */ + public function setConnectionID($value) + { + $this->_connID=$value; + } + + /** + * @return string connection module ID. + */ + public function getConnectionID() + { + return $this->_connID; + } + + /** + * Gets the TDbConnection from another module if {@link setConnectionID ConnectionID} + * is supplied and valid. Otherwise, a connection of type given by + * {@link setConnectionClass ConnectionClass} is created. + * @return TDbConnection database connection. + */ + public function getDbConnection() + { + if($this->_conn===null) + { + if($this->_connID!=='') + $this->_conn = $this->findConnectionByID($this->getConnectionID()); + else + $this->_conn = Prado::createComponent($this->getConnectionClass()); + } + return $this->_conn; + } + + /** + * Alias for getDbConnection(). + * @return TDbConnection database connection. + */ + public function getDatabase() + { + return $this->getDbConnection(); + } + + /** + * @param string Database connection class name to be created. + */ + public function getConnectionClass() + { + return $this->_connClass; + } + + /** + * The database connection class name to be created when {@link getDbConnection} + * method is called <b>and</b> {@link setConnectionID ConnectionID} is null. The + * {@link setConnectionClass ConnectionClass} property must be set before + * calling {@link getDbConnection} if you wish to create the connection using the + * given class name. + * @param string Database connection class name. + * @throws TConfigurationException when database connection is already established. + */ + public function setConnectionClass($value) + { + if($this->_conn!==null) + throw new TConfigurationException('datasource_dbconnection_exists', $value); + $this->_connClass=$value; + } + + /** + * Finds the database connection instance from the Application modules. + * @param string Database connection module ID. + * @return TDbConnection database connection. + * @throws TConfigurationException when module is not of TDbConnection or TDataSourceConfig. + */ + protected function findConnectionByID($id) + { + $conn = $this->getApplication()->getModule($id); + if($conn instanceof TDbConnection) + return $conn; + else if($conn instanceof TDataSourceConfig) + return $conn->getDbConnection(); + else + throw new TConfigurationException('datasource_dbconnection_invalid',$id); + } +} diff --git a/framework/Data/TDbCommand.php b/framework/Data/TDbCommand.php index 44630fdd..054191a2 100644 --- a/framework/Data/TDbCommand.php +++ b/framework/Data/TDbCommand.php @@ -1,308 +1,308 @@ -<?php
-/**
- * TDbCommand class file
- *
- * @author Qiang Xue <qiang.xue@gmail.com>
- * @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data
- */
-
-/**
- * TDbCommand class.
- *
- * TDbCommand represents an SQL statement to execute against a database.
- * It is usually created by calling {@link TDbConnection::createCommand}.
- * The SQL statement to be executed may be set via {@link setText Text}.
- *
- * To execute a non-query SQL (such as insert, delete, update), call
- * {@link execute}. To execute an SQL statement that returns result data set
- * (such as select), use {@link query} or its convenient versions {@link queryRow}
- * and {@link queryScalar}.
- *
- * If an SQL statement returns results (such as a SELECT SQL), the results
- * can be accessed via the returned {@link TDbDataReader}.
- *
- * TDbCommand supports SQL statment preparation and parameter binding.
- * Call {@link bindParameter} to bind a PHP variable to a parameter in SQL.
- * Call {@link bindValue} to bind a value to an SQL parameter.
- * When binding a parameter, the SQL statement is automatically prepared.
- * You may also call {@link prepare} to explicitly prepare an SQL statement.
- *
- * @author Qiang Xue <qiang.xue@gmail.com>
- * @version $Id$
- * @package System.Data
- * @since 3.0
- */
-class TDbCommand extends TComponent
-{
- private $_connection;
- private $_text='';
- private $_statement=null;
-
- /**
- * Constructor.
- * @param TDbConnection the database connection
- * @param string the SQL statement to be executed
- */
- public function __construct(TDbConnection $connection,$text)
- {
- $this->_connection=$connection;
- $this->setText($text);
- }
-
- /**
- * Set the statement to null when serializing.
- */
- public function __sleep()
- {
- return array_diff(parent::__sleep(),array("\0TDbCommand\0_statement"));
- }
-
- /**
- * @return string the SQL statement to be executed
- */
- public function getText()
- {
- return $this->_text;
- }
-
- /**
- * Specifies the SQL statement to be executed.
- * Any previous execution will be terminated or cancel.
- * @param string the SQL statement to be executed
- */
- public function setText($value)
- {
- $this->_text=$value;
- $this->cancel();
- }
-
- /**
- * @return TDbConnection the connection associated with this command
- */
- public function getConnection()
- {
- return $this->_connection;
- }
-
- /**
- * @return PDOStatement the underlying PDOStatement for this command
- * It could be null if the statement is not prepared yet.
- */
- public function getPdoStatement()
- {
- return $this->_statement;
- }
-
- /**
- * Prepares the SQL statement to be executed.
- * For complex SQL statement that is to be executed multiple times,
- * this may improve performance.
- * For SQL statement with binding parameters, this method is invoked
- * automatically.
- */
- public function prepare()
- {
- if($this->_statement==null)
- {
- try
- {
- $this->_statement=$this->getConnection()->getPdoInstance()->prepare($this->getText());
- }
- catch(Exception $e)
- {
- throw new TDbException('dbcommand_prepare_failed',$e->getMessage(),$this->getText());
- }
- }
- }
-
- /**
- * Cancels the execution of the SQL statement.
- */
- public function cancel()
- {
- $this->_statement=null;
- }
-
- /**
- * Binds a parameter to the SQL statement to be executed.
- * @param mixed Parameter identifier. For a prepared statement
- * using named placeholders, this will be a parameter name of
- * the form :name. For a prepared statement using question mark
- * placeholders, this will be the 1-indexed position of the parameter.
- * Unlike {@link bindValue}, the variable is bound as a reference and will
- * only be evaluated at the time that {@link execute} or {@link query} is called.
- * @param mixed Name of the PHP variable to bind to the SQL statement parameter
- * @param int SQL data type of the parameter
- * @param int length of the data type
- * @see http://www.php.net/manual/en/function.PDOStatement-bindParam.php
- */
- public function bindParameter($name, &$value, $dataType=null, $length=null)
- {
- $this->prepare();
- if($dataType===null)
- $this->_statement->bindParam($name,$value);
- else if($length===null)
- $this->_statement->bindParam($name,$value,$dataType);
- else
- $this->_statement->bindParam($name,$value,$dataType,$length);
- }
-
- /**
- * Binds a value to a parameter.
- * @param mixed Parameter identifier. For a prepared statement
- * using named placeholders, this will be a parameter name of
- * the form :name. For a prepared statement using question mark
- * placeholders, this will be the 1-indexed position of the parameter.
- * @param mixed The value to bind to the parameter
- * @param int SQL data type of the parameter
- * @see http://www.php.net/manual/en/function.PDOStatement-bindValue.php
- */
- public function bindValue($name, $value, $dataType=null)
- {
- $this->prepare();
- if($dataType===null)
- $this->_statement->bindValue($name,$value);
- else
- $this->_statement->bindValue($name,$value,$dataType);
- }
-
- /**
- * Executes the SQL statement.
- * This method is meant only for executing non-query SQL statement.
- * No result set will be returned.
- * @return integer number of rows affected by the execution.
- * @throws TDbException execution failed
- */
- public function execute()
- {
- try
- {
- // Do not trace because it will remain even in
- // Performance mode or when pradolite.php is used
- // Prado::trace('Execute Command: '.$this->getDebugStatementText(), 'System.Data');
- if($this->_statement instanceof PDOStatement)
- {
- $this->_statement->execute();
- return $this->_statement->rowCount();
- }
- else
- return $this->getConnection()->getPdoInstance()->exec($this->getText());
- }
- catch(Exception $e)
- {
- throw new TDbException('dbcommand_execute_failed',$e->getMessage(),$this->getDebugStatementText());
- }
- }
-
- /**
- * @return String prepared SQL text for debugging purposes.
- */
- public function getDebugStatementText()
- {
- if(Prado::getApplication()->getMode() === TApplicationMode::Debug)
- return $this->_statement instanceof PDOStatement ?
- $this->_statement->queryString
- : $this->getText();
- }
-
- /**
- * Executes the SQL statement and returns query result.
- * This method is for executing an SQL query that returns result set.
- * @return TDbDataReader the reader object for fetching the query result
- * @throws TDbException execution failed
- */
- public function query()
- {
- try
- {
- // Prado::trace('Query: '.$this->getDebugStatementText(), 'System.Data');
- if($this->_statement instanceof PDOStatement)
- $this->_statement->execute();
- else
- $this->_statement=$this->getConnection()->getPdoInstance()->query($this->getText());
- return new TDbDataReader($this);
- }
- catch(Exception $e)
- {
- throw new TDbException('dbcommand_query_failed',$e->getMessage(),$this->getDebugStatementText());
- }
- }
-
- /**
- * Executes the SQL statement and returns the first row of the result.
- * This is a convenient method of {@link query} when only the first row of data is needed.
- * @param boolean whether the row should be returned as an associated array with
- * column names as the keys or the array keys are column indexes (0-based).
- * @return array the first row of the query result, false if no result.
- * @throws TDbException execution failed
- */
- public function queryRow($fetchAssociative=true)
- {
- try
- {
- // Prado::trace('Query Row: '.$this->getDebugStatementText(), 'System.Data');
- if($this->_statement instanceof PDOStatement)
- $this->_statement->execute();
- else
- $this->_statement=$this->getConnection()->getPdoInstance()->query($this->getText());
- $result=$this->_statement->fetch($fetchAssociative ? PDO::FETCH_ASSOC : PDO::FETCH_NUM);
- $this->_statement->closeCursor();
- return $result;
- }
- catch(Exception $e)
- {
- throw new TDbException('dbcommand_query_failed',$e->getMessage(),$this->getDebugStatementText());
- }
- }
-
- /**
- * Executes the SQL statement and returns the value of the first column in the first row of data.
- * This is a convenient method of {@link query} when only a single scalar
- * value is needed (e.g. obtaining the count of the records).
- * @return mixed the value of the first column in the first row of the query result. False is returned if there is no value.
- * @throws TDbException execution failed
- */
- public function queryScalar()
- {
- try
- {
- // Prado::trace('Query Scalar: '.$this->getDebugStatementText(), 'System.Data');
- if($this->_statement instanceof PDOStatement)
- $this->_statement->execute();
- else
- $this->_statement=$this->getConnection()->getPdoInstance()->query($this->getText());
- $result=$this->_statement->fetchColumn();
- $this->_statement->closeCursor();
- if(is_resource($result) && get_resource_type($result)==='stream')
- return stream_get_contents($result);
- else
- return $result;
- }
- catch(Exception $e)
- {
- throw new TDbException('dbcommand_query_failed',$e->getMessage(),$this->getDebugStatementText());
- }
- }
-
- /**
- * Executes the SQL statement and returns the first column of the result.
- * This is a convenient method of {@link query} when only the first column of data is needed.
- * Note, the column returned will contain the first element in each row of result.
- * @return array the first column of the query result. Empty array if no result.
- * @throws TDbException execution failed
- * @since 3.1.2
- */
- public function queryColumn()
- {
- $rows=$this->query()->readAll();
- $column=array();
- foreach($rows as $row)
- $column[]=current($row);
- return $column;
- }
-}
-
+<?php +/** + * TDbCommand class file + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data + */ + +/** + * TDbCommand class. + * + * TDbCommand represents an SQL statement to execute against a database. + * It is usually created by calling {@link TDbConnection::createCommand}. + * The SQL statement to be executed may be set via {@link setText Text}. + * + * To execute a non-query SQL (such as insert, delete, update), call + * {@link execute}. To execute an SQL statement that returns result data set + * (such as select), use {@link query} or its convenient versions {@link queryRow} + * and {@link queryScalar}. + * + * If an SQL statement returns results (such as a SELECT SQL), the results + * can be accessed via the returned {@link TDbDataReader}. + * + * TDbCommand supports SQL statment preparation and parameter binding. + * Call {@link bindParameter} to bind a PHP variable to a parameter in SQL. + * Call {@link bindValue} to bind a value to an SQL parameter. + * When binding a parameter, the SQL statement is automatically prepared. + * You may also call {@link prepare} to explicitly prepare an SQL statement. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id$ + * @package System.Data + * @since 3.0 + */ +class TDbCommand extends TComponent +{ + private $_connection; + private $_text=''; + private $_statement=null; + + /** + * Constructor. + * @param TDbConnection the database connection + * @param string the SQL statement to be executed + */ + public function __construct(TDbConnection $connection,$text) + { + $this->_connection=$connection; + $this->setText($text); + } + + /** + * Set the statement to null when serializing. + */ + public function __sleep() + { + return array_diff(parent::__sleep(),array("\0TDbCommand\0_statement")); + } + + /** + * @return string the SQL statement to be executed + */ + public function getText() + { + return $this->_text; + } + + /** + * Specifies the SQL statement to be executed. + * Any previous execution will be terminated or cancel. + * @param string the SQL statement to be executed + */ + public function setText($value) + { + $this->_text=$value; + $this->cancel(); + } + + /** + * @return TDbConnection the connection associated with this command + */ + public function getConnection() + { + return $this->_connection; + } + + /** + * @return PDOStatement the underlying PDOStatement for this command + * It could be null if the statement is not prepared yet. + */ + public function getPdoStatement() + { + return $this->_statement; + } + + /** + * Prepares the SQL statement to be executed. + * For complex SQL statement that is to be executed multiple times, + * this may improve performance. + * For SQL statement with binding parameters, this method is invoked + * automatically. + */ + public function prepare() + { + if($this->_statement==null) + { + try + { + $this->_statement=$this->getConnection()->getPdoInstance()->prepare($this->getText()); + } + catch(Exception $e) + { + throw new TDbException('dbcommand_prepare_failed',$e->getMessage(),$this->getText()); + } + } + } + + /** + * Cancels the execution of the SQL statement. + */ + public function cancel() + { + $this->_statement=null; + } + + /** + * Binds a parameter to the SQL statement to be executed. + * @param mixed Parameter identifier. For a prepared statement + * using named placeholders, this will be a parameter name of + * the form :name. For a prepared statement using question mark + * placeholders, this will be the 1-indexed position of the parameter. + * Unlike {@link bindValue}, the variable is bound as a reference and will + * only be evaluated at the time that {@link execute} or {@link query} is called. + * @param mixed Name of the PHP variable to bind to the SQL statement parameter + * @param int SQL data type of the parameter + * @param int length of the data type + * @see http://www.php.net/manual/en/function.PDOStatement-bindParam.php + */ + public function bindParameter($name, &$value, $dataType=null, $length=null) + { + $this->prepare(); + if($dataType===null) + $this->_statement->bindParam($name,$value); + else if($length===null) + $this->_statement->bindParam($name,$value,$dataType); + else + $this->_statement->bindParam($name,$value,$dataType,$length); + } + + /** + * Binds a value to a parameter. + * @param mixed Parameter identifier. For a prepared statement + * using named placeholders, this will be a parameter name of + * the form :name. For a prepared statement using question mark + * placeholders, this will be the 1-indexed position of the parameter. + * @param mixed The value to bind to the parameter + * @param int SQL data type of the parameter + * @see http://www.php.net/manual/en/function.PDOStatement-bindValue.php + */ + public function bindValue($name, $value, $dataType=null) + { + $this->prepare(); + if($dataType===null) + $this->_statement->bindValue($name,$value); + else + $this->_statement->bindValue($name,$value,$dataType); + } + + /** + * Executes the SQL statement. + * This method is meant only for executing non-query SQL statement. + * No result set will be returned. + * @return integer number of rows affected by the execution. + * @throws TDbException execution failed + */ + public function execute() + { + try + { + // Do not trace because it will remain even in + // Performance mode or when pradolite.php is used + // Prado::trace('Execute Command: '.$this->getDebugStatementText(), 'System.Data'); + if($this->_statement instanceof PDOStatement) + { + $this->_statement->execute(); + return $this->_statement->rowCount(); + } + else + return $this->getConnection()->getPdoInstance()->exec($this->getText()); + } + catch(Exception $e) + { + throw new TDbException('dbcommand_execute_failed',$e->getMessage(),$this->getDebugStatementText()); + } + } + + /** + * @return String prepared SQL text for debugging purposes. + */ + public function getDebugStatementText() + { + if(Prado::getApplication()->getMode() === TApplicationMode::Debug) + return $this->_statement instanceof PDOStatement ? + $this->_statement->queryString + : $this->getText(); + } + + /** + * Executes the SQL statement and returns query result. + * This method is for executing an SQL query that returns result set. + * @return TDbDataReader the reader object for fetching the query result + * @throws TDbException execution failed + */ + public function query() + { + try + { + // Prado::trace('Query: '.$this->getDebugStatementText(), 'System.Data'); + if($this->_statement instanceof PDOStatement) + $this->_statement->execute(); + else + $this->_statement=$this->getConnection()->getPdoInstance()->query($this->getText()); + return new TDbDataReader($this); + } + catch(Exception $e) + { + throw new TDbException('dbcommand_query_failed',$e->getMessage(),$this->getDebugStatementText()); + } + } + + /** + * Executes the SQL statement and returns the first row of the result. + * This is a convenient method of {@link query} when only the first row of data is needed. + * @param boolean whether the row should be returned as an associated array with + * column names as the keys or the array keys are column indexes (0-based). + * @return array the first row of the query result, false if no result. + * @throws TDbException execution failed + */ + public function queryRow($fetchAssociative=true) + { + try + { + // Prado::trace('Query Row: '.$this->getDebugStatementText(), 'System.Data'); + if($this->_statement instanceof PDOStatement) + $this->_statement->execute(); + else + $this->_statement=$this->getConnection()->getPdoInstance()->query($this->getText()); + $result=$this->_statement->fetch($fetchAssociative ? PDO::FETCH_ASSOC : PDO::FETCH_NUM); + $this->_statement->closeCursor(); + return $result; + } + catch(Exception $e) + { + throw new TDbException('dbcommand_query_failed',$e->getMessage(),$this->getDebugStatementText()); + } + } + + /** + * Executes the SQL statement and returns the value of the first column in the first row of data. + * This is a convenient method of {@link query} when only a single scalar + * value is needed (e.g. obtaining the count of the records). + * @return mixed the value of the first column in the first row of the query result. False is returned if there is no value. + * @throws TDbException execution failed + */ + public function queryScalar() + { + try + { + // Prado::trace('Query Scalar: '.$this->getDebugStatementText(), 'System.Data'); + if($this->_statement instanceof PDOStatement) + $this->_statement->execute(); + else + $this->_statement=$this->getConnection()->getPdoInstance()->query($this->getText()); + $result=$this->_statement->fetchColumn(); + $this->_statement->closeCursor(); + if(is_resource($result) && get_resource_type($result)==='stream') + return stream_get_contents($result); + else + return $result; + } + catch(Exception $e) + { + throw new TDbException('dbcommand_query_failed',$e->getMessage(),$this->getDebugStatementText()); + } + } + + /** + * Executes the SQL statement and returns the first column of the result. + * This is a convenient method of {@link query} when only the first column of data is needed. + * Note, the column returned will contain the first element in each row of result. + * @return array the first column of the query result. Empty array if no result. + * @throws TDbException execution failed + * @since 3.1.2 + */ + public function queryColumn() + { + $rows=$this->query()->readAll(); + $column=array(); + foreach($rows as $row) + $column[]=current($row); + return $column; + } +} + diff --git a/framework/Data/TDbConnection.php b/framework/Data/TDbConnection.php index 0fd17ae1..fb93f076 100644 --- a/framework/Data/TDbConnection.php +++ b/framework/Data/TDbConnection.php @@ -1,685 +1,685 @@ -<?php
-/**
- * TDbConnection class file
- *
- * @author Qiang Xue <qiang.xue@gmail.com>
- * @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data
- */
-
-Prado::using('System.Data.TDbTransaction');
-Prado::using('System.Data.TDbCommand');
-
-/**
- * TDbConnection class
- *
- * TDbConnection represents a connection to a database.
- *
- * TDbConnection works together with {@link TDbCommand}, {@link TDbDataReader}
- * and {@link TDbTransaction} to provide data access to various DBMS
- * in a common set of APIs. They are a thin wrapper of the {@link http://www.php.net/manual/en/ref.pdo.php PDO}
- * PHP extension.
- *
- * To establish a connection, set {@link setActive Active} to true after
- * specifying {@link setConnectionString ConnectionString}, {@link setUsername Username}
- * and {@link setPassword Password}.
- *
- * Since 3.1.2, the connection charset can be set (for MySQL and PostgreSQL databases only)
- * using the {@link setCharset Charset} property. The value of this property is database dependant.
- * e.g. for mysql, you can use 'latin1' for cp1252 West European, 'utf8' for unicode, ...
- *
- * The following example shows how to create a TDbConnection instance and establish
- * the actual connection:
- * <code>
- * $connection=new TDbConnection($dsn,$username,$password);
- * $connection->Active=true;
- * </code>
- *
- * After the DB connection is established, one can execute an SQL statement like the following:
- * <code>
- * $command=$connection->createCommand($sqlStatement);
- * $command->execute(); // a non-query SQL statement execution
- * // or execute an SQL query and fetch the result set
- * $reader=$command->query();
- *
- * // each $row is an array representing a row of data
- * foreach($reader as $row) ...
- * </code>
- *
- * One can do prepared SQL execution and bind parameters to the prepared SQL:
- * <code>
- * $command=$connection->createCommand($sqlStatement);
- * $command->bindParameter($name1,$value1);
- * $command->bindParameter($name2,$value2);
- * $command->execute();
- * </code>
- *
- * To use transaction, do like the following:
- * <code>
- * $transaction=$connection->beginTransaction();
- * try
- * {
- * $connection->createCommand($sql1)->execute();
- * $connection->createCommand($sql2)->execute();
- * //.... other SQL executions
- * $transaction->commit();
- * }
- * catch(Exception $e)
- * {
- * $transaction->rollBack();
- * }
- * </code>
- *
- * TDbConnection provides a set of methods to support setting and querying
- * of certain DBMS attributes, such as {@link getNullConversion NullConversion}.
- *
- * @author Qiang Xue <qiang.xue@gmail.com>
- * @version $Id$
- * @package System.Data
- * @since 3.0
- */
-class TDbConnection extends TComponent
-{
- /**
- *
- * @since 3.1.7
- */
- const DEFAULT_TRANSACTION_CLASS = 'System.Data.TDbTransaction';
-
- private $_dsn='';
- private $_username='';
- private $_password='';
- private $_charset='';
- private $_attributes=array();
- private $_active=false;
- private $_pdo=null;
- private $_transaction;
-
- /**
- * @var TDbMetaData
- */
- private $_dbMeta = null;
-
- /**
- * @var string
- * @since 3.1.7
- */
- private $_transactionClass=self::DEFAULT_TRANSACTION_CLASS;
-
- /**
- * Constructor.
- * Note, the DB connection is not established when this connection
- * instance is created. Set {@link setActive Active} property to true
- * to establish the connection.
- * Since 3.1.2, you can set the charset for MySql connection
- *
- * @param string The Data Source Name, or DSN, contains the information required to connect to the database.
- * @param string The user name for the DSN string.
- * @param string The password for the DSN string.
- * @param string Charset used for DB Connection (MySql & pgsql only). If not set, will use the default charset of your database server
- * @see http://www.php.net/manual/en/function.PDO-construct.php
- */
- public function __construct($dsn='',$username='',$password='', $charset='')
- {
- $this->_dsn=$dsn;
- $this->_username=$username;
- $this->_password=$password;
- $this->_charset=$charset;
- }
-
- /**
- * Close the connection when serializing.
- */
- public function __sleep()
- {
-// $this->close(); - DO NOT CLOSE the current connection as serializing doesn't neccessarily mean we don't this connection anymore in the current session
- return array_diff(parent::__sleep(),array("\0TDbConnection\0_pdo","\0TDbConnection\0_active"));
- }
-
- /**
- * @return array list of available PDO drivers
- * @see http://www.php.net/manual/en/function.PDO-getAvailableDrivers.php
- */
- public static function getAvailableDrivers()
- {
- return PDO::getAvailableDrivers();
- }
-
- /**
- * @return boolean whether the DB connection is established
- */
- public function getActive()
- {
- return $this->_active;
- }
-
- /**
- * Open or close the DB connection.
- * @param boolean whether to open or close DB connection
- * @throws TDbException if connection fails
- */
- public function setActive($value)
- {
- $value=TPropertyValue::ensureBoolean($value);
- if($value!==$this->_active)
- {
- if($value)
- $this->open();
- else
- $this->close();
- }
- }
-
- /**
- * Opens DB connection if it is currently not
- * @throws TDbException if connection fails
- */
- protected function open()
- {
- if($this->_pdo===null)
- {
- try
- {
- $this->_pdo=new PDO($this->getConnectionString(),$this->getUsername(),
+<?php +/** + * TDbConnection class file + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data + */ + +Prado::using('System.Data.TDbTransaction'); +Prado::using('System.Data.TDbCommand'); + +/** + * TDbConnection class + * + * TDbConnection represents a connection to a database. + * + * TDbConnection works together with {@link TDbCommand}, {@link TDbDataReader} + * and {@link TDbTransaction} to provide data access to various DBMS + * in a common set of APIs. They are a thin wrapper of the {@link http://www.php.net/manual/en/ref.pdo.php PDO} + * PHP extension. + * + * To establish a connection, set {@link setActive Active} to true after + * specifying {@link setConnectionString ConnectionString}, {@link setUsername Username} + * and {@link setPassword Password}. + * + * Since 3.1.2, the connection charset can be set (for MySQL and PostgreSQL databases only) + * using the {@link setCharset Charset} property. The value of this property is database dependant. + * e.g. for mysql, you can use 'latin1' for cp1252 West European, 'utf8' for unicode, ... + * + * The following example shows how to create a TDbConnection instance and establish + * the actual connection: + * <code> + * $connection=new TDbConnection($dsn,$username,$password); + * $connection->Active=true; + * </code> + * + * After the DB connection is established, one can execute an SQL statement like the following: + * <code> + * $command=$connection->createCommand($sqlStatement); + * $command->execute(); // a non-query SQL statement execution + * // or execute an SQL query and fetch the result set + * $reader=$command->query(); + * + * // each $row is an array representing a row of data + * foreach($reader as $row) ... + * </code> + * + * One can do prepared SQL execution and bind parameters to the prepared SQL: + * <code> + * $command=$connection->createCommand($sqlStatement); + * $command->bindParameter($name1,$value1); + * $command->bindParameter($name2,$value2); + * $command->execute(); + * </code> + * + * To use transaction, do like the following: + * <code> + * $transaction=$connection->beginTransaction(); + * try + * { + * $connection->createCommand($sql1)->execute(); + * $connection->createCommand($sql2)->execute(); + * //.... other SQL executions + * $transaction->commit(); + * } + * catch(Exception $e) + * { + * $transaction->rollBack(); + * } + * </code> + * + * TDbConnection provides a set of methods to support setting and querying + * of certain DBMS attributes, such as {@link getNullConversion NullConversion}. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id$ + * @package System.Data + * @since 3.0 + */ +class TDbConnection extends TComponent +{ + /** + * + * @since 3.1.7 + */ + const DEFAULT_TRANSACTION_CLASS = 'System.Data.TDbTransaction'; + + private $_dsn=''; + private $_username=''; + private $_password=''; + private $_charset=''; + private $_attributes=array(); + private $_active=false; + private $_pdo=null; + private $_transaction; + + /** + * @var TDbMetaData + */ + private $_dbMeta = null; + + /** + * @var string + * @since 3.1.7 + */ + private $_transactionClass=self::DEFAULT_TRANSACTION_CLASS; + + /** + * Constructor. + * Note, the DB connection is not established when this connection + * instance is created. Set {@link setActive Active} property to true + * to establish the connection. + * Since 3.1.2, you can set the charset for MySql connection + * + * @param string The Data Source Name, or DSN, contains the information required to connect to the database. + * @param string The user name for the DSN string. + * @param string The password for the DSN string. + * @param string Charset used for DB Connection (MySql & pgsql only). If not set, will use the default charset of your database server + * @see http://www.php.net/manual/en/function.PDO-construct.php + */ + public function __construct($dsn='',$username='',$password='', $charset='') + { + $this->_dsn=$dsn; + $this->_username=$username; + $this->_password=$password; + $this->_charset=$charset; + } + + /** + * Close the connection when serializing. + */ + public function __sleep() + { +// $this->close(); - DO NOT CLOSE the current connection as serializing doesn't neccessarily mean we don't this connection anymore in the current session + return array_diff(parent::__sleep(),array("\0TDbConnection\0_pdo","\0TDbConnection\0_active")); + } + + /** + * @return array list of available PDO drivers + * @see http://www.php.net/manual/en/function.PDO-getAvailableDrivers.php + */ + public static function getAvailableDrivers() + { + return PDO::getAvailableDrivers(); + } + + /** + * @return boolean whether the DB connection is established + */ + public function getActive() + { + return $this->_active; + } + + /** + * Open or close the DB connection. + * @param boolean whether to open or close DB connection + * @throws TDbException if connection fails + */ + public function setActive($value) + { + $value=TPropertyValue::ensureBoolean($value); + if($value!==$this->_active) + { + if($value) + $this->open(); + else + $this->close(); + } + } + + /** + * Opens DB connection if it is currently not + * @throws TDbException if connection fails + */ + protected function open() + { + if($this->_pdo===null) + { + try + { + $this->_pdo=new PDO($this->getConnectionString(),$this->getUsername(), $this->getPassword(),$this->_attributes); - // This attribute is only useful for PDO::MySql driver.
+ // This attribute is only useful for PDO::MySql driver. // Ignore the warning if a driver doesn't understand this. - @$this->_pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
- $this->_pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
- $this->_active=true;
- $this->setConnectionCharset();
- }
- catch(PDOException $e)
- {
- throw new TDbException('dbconnection_open_failed',$e->getMessage());
- }
- }
- }
-
- /**
- * Closes the currently active DB connection.
- * It does nothing if the connection is already closed.
- */
- protected function close()
- {
- $this->_pdo=null;
- $this->_active=false;
- }
-
- /*
- * Set the database connection charset.
- * Only MySql databases are supported for now.
- * @since 3.1.2
- */
- protected function setConnectionCharset()
- {
- if ($this->_charset === '' || $this->_active === false)
- return;
- switch ($this->_pdo->getAttribute(PDO::ATTR_DRIVER_NAME))
- {
- case 'mysql':
- case 'sqlite':
- $stmt = $this->_pdo->prepare('SET NAMES ?');
- break;
- case 'pgsql':
- $stmt = $this->_pdo->prepare('SET client_encoding TO ?');
- break;
- default:
- throw new TDbException('dbconnection_unsupported_driver_charset', $driver);
- }
- $stmt->execute(array($this->_charset));
- }
-
- /**
- * @return string The Data Source Name, or DSN, contains the information required to connect to the database.
- */
- public function getConnectionString()
- {
- return $this->_dsn;
- }
-
- /**
- * @param string The Data Source Name, or DSN, contains the information required to connect to the database.
- * @see http://www.php.net/manual/en/function.PDO-construct.php
- */
- public function setConnectionString($value)
- {
- $this->_dsn=$value;
- }
-
- /**
- * @return string the username for establishing DB connection. Defaults to empty string.
- */
- public function getUsername()
- {
- return $this->_username;
- }
-
- /**
- * @param string the username for establishing DB connection
- */
- public function setUsername($value)
- {
- $this->_username=$value;
- }
-
- /**
- * @return string the password for establishing DB connection. Defaults to empty string.
- */
- public function getPassword()
- {
- return $this->_password;
- }
-
- /**
- * @param string the password for establishing DB connection
- */
- public function setPassword($value)
- {
- $this->_password=$value;
- }
-
- /**
- * @return string the charset used for database connection. Defaults to emtpy string.
- */
- public function getCharset ()
- {
- return $this->_charset;
- }
-
- /**
- * @param string the charset used for database connection
- */
- public function setCharset ($value)
- {
- $this->_charset=$value;
- $this->setConnectionCharset();
- }
-
- /**
- * @return PDO the PDO instance, null if the connection is not established yet
- */
- public function getPdoInstance()
- {
- return $this->_pdo;
- }
-
- /**
- * Creates a command for execution.
- * @param string SQL statement associated with the new command.
- * @return TDbCommand the DB command
- * @throws TDbException if the connection is not active
- */
- public function createCommand($sql)
- {
- if($this->getActive())
- return new TDbCommand($this,$sql);
- else
- throw new TDbException('dbconnection_connection_inactive');
- }
-
- /**
- * @return TDbTransaction the currently active transaction. Null if no active transaction.
- */
- public function getCurrentTransaction()
- {
- if($this->_transaction!==null)
- {
- if($this->_transaction->getActive())
- return $this->_transaction;
- }
- return null;
- }
-
- /**
- * Starts a transaction.
- * @return TDbTransaction the transaction initiated
- * @throws TDbException if the connection is not active
- */
- public function beginTransaction()
- {
- if($this->getActive())
- {
- $this->_pdo->beginTransaction();
- return $this->_transaction=Prado::createComponent($this->getTransactionClass(), $this);
- }
- else
- throw new TDbException('dbconnection_connection_inactive');
- }
-
- /**
- * @return string Transaction class name to be created by calling {@link TDbConnection::beginTransaction}. Defaults to 'System.Data.TDbTransaction'.
- * @since 3.1.7
- */
- public function getTransactionClass()
- {
- return $this->_transactionClass;
- }
-
-
- /**
- * @param string Transaction class name to be created by calling {@link TDbConnection::beginTransaction}.
- * @since 3.1.7
- */
- public function setTransactionClass($value)
- {
- $this->_transactionClass = (string)$value;
- }
-
- /**
- * Returns the ID of the last inserted row or sequence value.
- * @param string name of the sequence object (required by some DBMS)
- * @return string the row ID of the last row inserted, or the last value retrieved from the sequence object
- * @see http://www.php.net/manual/en/function.PDO-lastInsertId.php
- */
- public function getLastInsertID($sequenceName='')
- {
- if($this->getActive())
- return $this->_pdo->lastInsertId($sequenceName);
- else
- throw new TDbException('dbconnection_connection_inactive');
- }
-
- /**
- * Quotes a string for use in a query.
- * @param string string to be quoted
- * @return string the properly quoted string
- * @see http://www.php.net/manual/en/function.PDO-quote.php
- */
- public function quoteString($str)
- {
- if($this->getActive())
- return $this->_pdo->quote($str);
- else
- throw new TDbException('dbconnection_connection_inactive');
- }
-
- /**
- * Quotes a table name for use in a query.
- * @param string $name table name
- * @return string the properly quoted table name
- */
- public function quoteTableName($name)
- {
- return $this->getDbMetaData()->quoteTableName($name);
- }
-
- /**
- * Quotes a column name for use in a query.
- * @param string $name column name
- * @return string the properly quoted column name
- */
- public function quoteColumnName($name)
- {
- return $this->getDbMetaData()->quoteColumnName($name);
- }
-
- /**
- * Quotes a column alias for use in a query.
- * @param string $name column name
- * @return string the properly quoted column alias
- */
- public function quoteColumnAlias($name)
- {
- return $this->getDbMetaData()->quoteColumnAlias($name);
- }
-
- /**
- * @return TDbMetaData
- */
- public function getDbMetaData()
- {
- if($this->_dbMeta===null)
- {
- Prado::using('System.Data.Common.TDbMetaData');
- $this->_dbMeta = TDbMetaData::getInstance($this);
- }
- return $this->_dbMeta;
- }
-
- /**
- * @return TDbColumnCaseMode the case of the column names
- */
- public function getColumnCase()
- {
- switch($this->getAttribute(PDO::ATTR_CASE))
- {
- case PDO::CASE_NATURAL:
- return TDbColumnCaseMode::Preserved;
- case PDO::CASE_LOWER:
- return TDbColumnCaseMode::LowerCase;
- case PDO::CASE_UPPER:
- return TDbColumnCaseMode::UpperCase;
- }
- }
-
- /**
- * @param TDbColumnCaseMode the case of the column names
- */
- public function setColumnCase($value)
- {
- switch(TPropertyValue::ensureEnum($value,'TDbColumnCaseMode'))
- {
- case TDbColumnCaseMode::Preserved:
- $value=PDO::CASE_NATURAL;
- break;
- case TDbColumnCaseMode::LowerCase:
- $value=PDO::CASE_LOWER;
- break;
- case TDbColumnCaseMode::UpperCase:
- $value=PDO::CASE_UPPER;
- break;
- }
- $this->setAttribute(PDO::ATTR_CASE,$value);
- }
-
- /**
- * @return TDbNullConversionMode how the null and empty strings are converted
- */
- public function getNullConversion()
- {
- switch($this->getAttribute(PDO::ATTR_ORACLE_NULLS))
- {
- case PDO::NULL_NATURAL:
- return TDbNullConversionMode::Preserved;
- case PDO::NULL_EMPTY_STRING:
- return TDbNullConversionMode::EmptyStringToNull;
- case PDO::NULL_TO_STRING:
- return TDbNullConversionMode::NullToEmptyString;
- }
- }
-
- /**
- * @param TDbNullConversionMode how the null and empty strings are converted
- */
- public function setNullConversion($value)
- {
- switch(TPropertyValue::ensureEnum($value,'TDbNullConversionMode'))
- {
- case TDbNullConversionMode::Preserved:
- $value=PDO::NULL_NATURAL;
- break;
- case TDbNullConversionMode::EmptyStringToNull:
- $value=PDO::NULL_EMPTY_STRING;
- break;
- case TDbNullConversionMode::NullToEmptyString:
- $value=PDO::NULL_TO_STRING;
- break;
- }
- $this->setAttribute(PDO::ATTR_ORACLE_NULLS,$value);
- }
-
- /**
- * @return boolean whether creating or updating a DB record will be automatically committed.
- * Some DBMS (such as sqlite) may not support this feature.
- */
- public function getAutoCommit()
- {
- return $this->getAttribute(PDO::ATTR_AUTOCOMMIT);
- }
-
- /**
- * @param boolean whether creating or updating a DB record will be automatically committed.
- * Some DBMS (such as sqlite) may not support this feature.
- */
- public function setAutoCommit($value)
- {
- $this->setAttribute(PDO::ATTR_AUTOCOMMIT,TPropertyValue::ensureBoolean($value));
- }
-
- /**
- * @return boolean whether the connection is persistent or not
- * Some DBMS (such as sqlite) may not support this feature.
- */
- public function getPersistent()
- {
- return $this->getAttribute(PDO::ATTR_PERSISTENT);
- }
-
- /**
- * @param boolean whether the connection is persistent or not
- * Some DBMS (such as sqlite) may not support this feature.
- */
- public function setPersistent($value)
- {
- return $this->setAttribute(PDO::ATTR_PERSISTENT,TPropertyValue::ensureBoolean($value));
- }
-
- /**
- * @return string name of the DB driver
- */
- public function getDriverName()
- {
- return $this->getAttribute(PDO::ATTR_DRIVER_NAME);
- }
-
- /**
- * @return string the version information of the DB driver
- */
- public function getClientVersion()
- {
- return $this->getAttribute(PDO::ATTR_CLIENT_VERSION);
- }
-
- /**
- * @return string the status of the connection
- * Some DBMS (such as sqlite) may not support this feature.
- */
- public function getConnectionStatus()
- {
- return $this->getAttribute(PDO::ATTR_CONNECTION_STATUS);
- }
-
- /**
- * @return boolean whether the connection performs data prefetching
- */
- public function getPrefetch()
- {
- return $this->getAttribute(PDO::ATTR_PREFETCH);
- }
-
- /**
- * @return string the information of DBMS server
- */
- public function getServerInfo()
- {
- return $this->getAttribute(PDO::ATTR_SERVER_INFO);
- }
-
- /**
- * @return string the version information of DBMS server
- */
- public function getServerVersion()
- {
- return $this->getAttribute(PDO::ATTR_SERVER_VERSION);
- }
-
- /**
- * @return int timeout settings for the connection
- */
- public function getTimeout()
- {
- return $this->getAttribute(PDO::ATTR_TIMEOUT);
- }
-
- /**
- * Obtains a specific DB connection attribute information.
- * @param int the attribute to be queried
- * @return mixed the corresponding attribute information
- * @see http://www.php.net/manual/en/function.PDO-getAttribute.php
- */
- public function getAttribute($name)
- {
- if($this->getActive())
- return $this->_pdo->getAttribute($name);
- else
- throw new TDbException('dbconnection_connection_inactive');
- }
-
- /**
- * Sets an attribute on the database connection.
- * @param int the attribute to be set
- * @param mixed the attribute value
- * @see http://www.php.net/manual/en/function.PDO-setAttribute.php
- */
- public function setAttribute($name,$value)
- {
- if($this->_pdo instanceof PDO)
- $this->_pdo->setAttribute($name,$value);
- else
- $this->_attributes[$name]=$value;
- }
-}
-
-/**
- * TDbColumnCaseMode
- *
- * @author Qiang Xue <qiang.xue@gmail.com>
- * @version $Id$
- * @package System.Data
- * @since 3.0
- */
-class TDbColumnCaseMode extends TEnumerable
-{
- /**
- * Column name cases are kept as is from the database
- */
- const Preserved='Preserved';
- /**
- * Column names are converted to lower case
- */
- const LowerCase='LowerCase';
- /**
- * Column names are converted to upper case
- */
- const UpperCase='UpperCase';
-}
-
-/**
- * TDbNullConversionMode
- *
- * @author Qiang Xue <qiang.xue@gmail.com>
- * @version $Id$
- * @package System.Data
- * @since 3.0
- */
-class TDbNullConversionMode extends TEnumerable
-{
- /**
- * No conversion is performed for null and empty values.
- */
- const Preserved='Preserved';
- /**
- * NULL is converted to empty string
- */
- const NullToEmptyString='NullToEmptyString';
- /**
- * Empty string is converted to NULL
- */
- const EmptyStringToNull='EmptyStringToNull';
-}
-
-?>
+ @$this->_pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, true); + $this->_pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + $this->_active=true; + $this->setConnectionCharset(); + } + catch(PDOException $e) + { + throw new TDbException('dbconnection_open_failed',$e->getMessage()); + } + } + } + + /** + * Closes the currently active DB connection. + * It does nothing if the connection is already closed. + */ + protected function close() + { + $this->_pdo=null; + $this->_active=false; + } + + /* + * Set the database connection charset. + * Only MySql databases are supported for now. + * @since 3.1.2 + */ + protected function setConnectionCharset() + { + if ($this->_charset === '' || $this->_active === false) + return; + switch ($this->_pdo->getAttribute(PDO::ATTR_DRIVER_NAME)) + { + case 'mysql': + case 'sqlite': + $stmt = $this->_pdo->prepare('SET NAMES ?'); + break; + case 'pgsql': + $stmt = $this->_pdo->prepare('SET client_encoding TO ?'); + break; + default: + throw new TDbException('dbconnection_unsupported_driver_charset', $driver); + } + $stmt->execute(array($this->_charset)); + } + + /** + * @return string The Data Source Name, or DSN, contains the information required to connect to the database. + */ + public function getConnectionString() + { + return $this->_dsn; + } + + /** + * @param string The Data Source Name, or DSN, contains the information required to connect to the database. + * @see http://www.php.net/manual/en/function.PDO-construct.php + */ + public function setConnectionString($value) + { + $this->_dsn=$value; + } + + /** + * @return string the username for establishing DB connection. Defaults to empty string. + */ + public function getUsername() + { + return $this->_username; + } + + /** + * @param string the username for establishing DB connection + */ + public function setUsername($value) + { + $this->_username=$value; + } + + /** + * @return string the password for establishing DB connection. Defaults to empty string. + */ + public function getPassword() + { + return $this->_password; + } + + /** + * @param string the password for establishing DB connection + */ + public function setPassword($value) + { + $this->_password=$value; + } + + /** + * @return string the charset used for database connection. Defaults to emtpy string. + */ + public function getCharset () + { + return $this->_charset; + } + + /** + * @param string the charset used for database connection + */ + public function setCharset ($value) + { + $this->_charset=$value; + $this->setConnectionCharset(); + } + + /** + * @return PDO the PDO instance, null if the connection is not established yet + */ + public function getPdoInstance() + { + return $this->_pdo; + } + + /** + * Creates a command for execution. + * @param string SQL statement associated with the new command. + * @return TDbCommand the DB command + * @throws TDbException if the connection is not active + */ + public function createCommand($sql) + { + if($this->getActive()) + return new TDbCommand($this,$sql); + else + throw new TDbException('dbconnection_connection_inactive'); + } + + /** + * @return TDbTransaction the currently active transaction. Null if no active transaction. + */ + public function getCurrentTransaction() + { + if($this->_transaction!==null) + { + if($this->_transaction->getActive()) + return $this->_transaction; + } + return null; + } + + /** + * Starts a transaction. + * @return TDbTransaction the transaction initiated + * @throws TDbException if the connection is not active + */ + public function beginTransaction() + { + if($this->getActive()) + { + $this->_pdo->beginTransaction(); + return $this->_transaction=Prado::createComponent($this->getTransactionClass(), $this); + } + else + throw new TDbException('dbconnection_connection_inactive'); + } + + /** + * @return string Transaction class name to be created by calling {@link TDbConnection::beginTransaction}. Defaults to 'System.Data.TDbTransaction'. + * @since 3.1.7 + */ + public function getTransactionClass() + { + return $this->_transactionClass; + } + + + /** + * @param string Transaction class name to be created by calling {@link TDbConnection::beginTransaction}. + * @since 3.1.7 + */ + public function setTransactionClass($value) + { + $this->_transactionClass = (string)$value; + } + + /** + * Returns the ID of the last inserted row or sequence value. + * @param string name of the sequence object (required by some DBMS) + * @return string the row ID of the last row inserted, or the last value retrieved from the sequence object + * @see http://www.php.net/manual/en/function.PDO-lastInsertId.php + */ + public function getLastInsertID($sequenceName='') + { + if($this->getActive()) + return $this->_pdo->lastInsertId($sequenceName); + else + throw new TDbException('dbconnection_connection_inactive'); + } + + /** + * Quotes a string for use in a query. + * @param string string to be quoted + * @return string the properly quoted string + * @see http://www.php.net/manual/en/function.PDO-quote.php + */ + public function quoteString($str) + { + if($this->getActive()) + return $this->_pdo->quote($str); + else + throw new TDbException('dbconnection_connection_inactive'); + } + + /** + * Quotes a table name for use in a query. + * @param string $name table name + * @return string the properly quoted table name + */ + public function quoteTableName($name) + { + return $this->getDbMetaData()->quoteTableName($name); + } + + /** + * Quotes a column name for use in a query. + * @param string $name column name + * @return string the properly quoted column name + */ + public function quoteColumnName($name) + { + return $this->getDbMetaData()->quoteColumnName($name); + } + + /** + * Quotes a column alias for use in a query. + * @param string $name column name + * @return string the properly quoted column alias + */ + public function quoteColumnAlias($name) + { + return $this->getDbMetaData()->quoteColumnAlias($name); + } + + /** + * @return TDbMetaData + */ + public function getDbMetaData() + { + if($this->_dbMeta===null) + { + Prado::using('System.Data.Common.TDbMetaData'); + $this->_dbMeta = TDbMetaData::getInstance($this); + } + return $this->_dbMeta; + } + + /** + * @return TDbColumnCaseMode the case of the column names + */ + public function getColumnCase() + { + switch($this->getAttribute(PDO::ATTR_CASE)) + { + case PDO::CASE_NATURAL: + return TDbColumnCaseMode::Preserved; + case PDO::CASE_LOWER: + return TDbColumnCaseMode::LowerCase; + case PDO::CASE_UPPER: + return TDbColumnCaseMode::UpperCase; + } + } + + /** + * @param TDbColumnCaseMode the case of the column names + */ + public function setColumnCase($value) + { + switch(TPropertyValue::ensureEnum($value,'TDbColumnCaseMode')) + { + case TDbColumnCaseMode::Preserved: + $value=PDO::CASE_NATURAL; + break; + case TDbColumnCaseMode::LowerCase: + $value=PDO::CASE_LOWER; + break; + case TDbColumnCaseMode::UpperCase: + $value=PDO::CASE_UPPER; + break; + } + $this->setAttribute(PDO::ATTR_CASE,$value); + } + + /** + * @return TDbNullConversionMode how the null and empty strings are converted + */ + public function getNullConversion() + { + switch($this->getAttribute(PDO::ATTR_ORACLE_NULLS)) + { + case PDO::NULL_NATURAL: + return TDbNullConversionMode::Preserved; + case PDO::NULL_EMPTY_STRING: + return TDbNullConversionMode::EmptyStringToNull; + case PDO::NULL_TO_STRING: + return TDbNullConversionMode::NullToEmptyString; + } + } + + /** + * @param TDbNullConversionMode how the null and empty strings are converted + */ + public function setNullConversion($value) + { + switch(TPropertyValue::ensureEnum($value,'TDbNullConversionMode')) + { + case TDbNullConversionMode::Preserved: + $value=PDO::NULL_NATURAL; + break; + case TDbNullConversionMode::EmptyStringToNull: + $value=PDO::NULL_EMPTY_STRING; + break; + case TDbNullConversionMode::NullToEmptyString: + $value=PDO::NULL_TO_STRING; + break; + } + $this->setAttribute(PDO::ATTR_ORACLE_NULLS,$value); + } + + /** + * @return boolean whether creating or updating a DB record will be automatically committed. + * Some DBMS (such as sqlite) may not support this feature. + */ + public function getAutoCommit() + { + return $this->getAttribute(PDO::ATTR_AUTOCOMMIT); + } + + /** + * @param boolean whether creating or updating a DB record will be automatically committed. + * Some DBMS (such as sqlite) may not support this feature. + */ + public function setAutoCommit($value) + { + $this->setAttribute(PDO::ATTR_AUTOCOMMIT,TPropertyValue::ensureBoolean($value)); + } + + /** + * @return boolean whether the connection is persistent or not + * Some DBMS (such as sqlite) may not support this feature. + */ + public function getPersistent() + { + return $this->getAttribute(PDO::ATTR_PERSISTENT); + } + + /** + * @param boolean whether the connection is persistent or not + * Some DBMS (such as sqlite) may not support this feature. + */ + public function setPersistent($value) + { + return $this->setAttribute(PDO::ATTR_PERSISTENT,TPropertyValue::ensureBoolean($value)); + } + + /** + * @return string name of the DB driver + */ + public function getDriverName() + { + return $this->getAttribute(PDO::ATTR_DRIVER_NAME); + } + + /** + * @return string the version information of the DB driver + */ + public function getClientVersion() + { + return $this->getAttribute(PDO::ATTR_CLIENT_VERSION); + } + + /** + * @return string the status of the connection + * Some DBMS (such as sqlite) may not support this feature. + */ + public function getConnectionStatus() + { + return $this->getAttribute(PDO::ATTR_CONNECTION_STATUS); + } + + /** + * @return boolean whether the connection performs data prefetching + */ + public function getPrefetch() + { + return $this->getAttribute(PDO::ATTR_PREFETCH); + } + + /** + * @return string the information of DBMS server + */ + public function getServerInfo() + { + return $this->getAttribute(PDO::ATTR_SERVER_INFO); + } + + /** + * @return string the version information of DBMS server + */ + public function getServerVersion() + { + return $this->getAttribute(PDO::ATTR_SERVER_VERSION); + } + + /** + * @return int timeout settings for the connection + */ + public function getTimeout() + { + return $this->getAttribute(PDO::ATTR_TIMEOUT); + } + + /** + * Obtains a specific DB connection attribute information. + * @param int the attribute to be queried + * @return mixed the corresponding attribute information + * @see http://www.php.net/manual/en/function.PDO-getAttribute.php + */ + public function getAttribute($name) + { + if($this->getActive()) + return $this->_pdo->getAttribute($name); + else + throw new TDbException('dbconnection_connection_inactive'); + } + + /** + * Sets an attribute on the database connection. + * @param int the attribute to be set + * @param mixed the attribute value + * @see http://www.php.net/manual/en/function.PDO-setAttribute.php + */ + public function setAttribute($name,$value) + { + if($this->_pdo instanceof PDO) + $this->_pdo->setAttribute($name,$value); + else + $this->_attributes[$name]=$value; + } +} + +/** + * TDbColumnCaseMode + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id$ + * @package System.Data + * @since 3.0 + */ +class TDbColumnCaseMode extends TEnumerable +{ + /** + * Column name cases are kept as is from the database + */ + const Preserved='Preserved'; + /** + * Column names are converted to lower case + */ + const LowerCase='LowerCase'; + /** + * Column names are converted to upper case + */ + const UpperCase='UpperCase'; +} + +/** + * TDbNullConversionMode + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id$ + * @package System.Data + * @since 3.0 + */ +class TDbNullConversionMode extends TEnumerable +{ + /** + * No conversion is performed for null and empty values. + */ + const Preserved='Preserved'; + /** + * NULL is converted to empty string + */ + const NullToEmptyString='NullToEmptyString'; + /** + * Empty string is converted to NULL + */ + const EmptyStringToNull='EmptyStringToNull'; +} + +?> diff --git a/framework/Data/TDbDataReader.php b/framework/Data/TDbDataReader.php index 879fd49f..d191e336 100644 --- a/framework/Data/TDbDataReader.php +++ b/framework/Data/TDbDataReader.php @@ -1,225 +1,225 @@ -<?php
-/**
- * TDbDataReader class file
- *
- * @author Qiang Xue <qiang.xue@gmail.com>
- * @link http://www.pradosoft.com/
+<?php +/** + * TDbDataReader class file + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data
- */
-
-/**
- * TDbDataReader class.
- *
- * TDbDataReader represents a forward-only stream of rows from a query result set.
- *
- * To read the current row of data, call {@link read}. The method {@link readAll}
- * returns all the rows in a single array.
- *
- * One can also retrieve the rows of data in TDbDataReader by using foreach:
- * <code>
- * foreach($reader as $row)
- * // $row represents a row of data
- * </code>
- * Since TDbDataReader is a forward-only stream, you can only traverse it once.
- *
- * It is possible to use a specific mode of data fetching by setting
- * {@link setFetchMode FetchMode}. See {@link http://www.php.net/manual/en/function.PDOStatement-setFetchMode.php}
- * for more details.
- *
- * @author Qiang Xue <qiang.xue@gmail.com>
- * @version $Id$
- * @package System.Data
- * @since 3.0
- */
-class TDbDataReader extends TComponent implements Iterator
-{
- private $_statement;
- private $_closed=false;
- private $_row;
- private $_index=-1;
-
- /**
- * Constructor.
- * @param TDbCommand the command generating the query result
- */
- public function __construct(TDbCommand $command)
- {
- $this->_statement=$command->getPdoStatement();
- $this->_statement->setFetchMode(PDO::FETCH_ASSOC);
- }
-
- /**
- * Binds a column to a PHP variable.
- * When rows of data are being fetched, the corresponding column value
- * will be set in the variable. Note, the fetch mode must include PDO::FETCH_BOUND.
- * @param mixed Number of the column (1-indexed) or name of the column
- * in the result set. If using the column name, be aware that the name
- * should match the case of the column, as returned by the driver.
- * @param mixed Name of the PHP variable to which the column will be bound.
- * @param int Data type of the parameter
- * @see http://www.php.net/manual/en/function.PDOStatement-bindColumn.php
- */
- public function bindColumn($column, &$value, $dataType=null)
- {
- if($dataType===null)
- $this->_statement->bindColumn($column,$value);
- else
- $this->_statement->bindColumn($column,$value,$dataType);
- }
-
- /**
- * @see http://www.php.net/manual/en/function.PDOStatement-setFetchMode.php
- */
- public function setFetchMode($mode)
- {
- $params=func_get_args();
- call_user_func_array(array($this->_statement,'setFetchMode'),$params);
- }
-
- /**
- * Advances the reader to the next row in a result set.
- * @return array|false the current row, false if no more row available
- */
- public function read()
- {
- return $this->_statement->fetch();
- }
-
- /**
- * Returns a single column from the next row of a result set.
- * @param int zero-based column index
- * @return mixed|false the column of the current row, false if no more row available
- */
- public function readColumn($columnIndex)
- {
- return $this->_statement->fetchColumn($columnIndex);
- }
-
- /**
- * Returns a single column from the next row of a result set.
- * @param string class name of the object to be created and populated
- * @param array list of column names whose values are to be passed as parameters in the constructor of the class being created
- * @return mixed|false the populated object, false if no more row of data available
- */
- public function readObject($className,$fields)
- {
- return $this->_statement->fetchObject($className,$fields);
- }
-
- /**
- * Reads the whole result set into an array.
- * @return array the result set (each array element represents a row of data).
- * An empty array will be returned if the result contains no row.
- */
- public function readAll()
- {
- return $this->_statement->fetchAll();
- }
-
- /**
- * Advances the reader to the next result when reading the results of a batch of statements.
- * This method is only useful when there are multiple result sets
- * returned by the query. Not all DBMS support this feature.
- */
- public function nextResult()
- {
- return $this->_statement->nextRowset();
- }
-
- /**
- * Closes the reader.
- * Any further data reading will result in an exception.
- */
- public function close()
- {
- $this->_statement->closeCursor();
- $this->_closed=true;
- }
-
- /**
- * @return boolean whether the reader is closed or not.
- */
- public function getIsClosed()
- {
- return $this->_closed;
- }
-
- /**
- * @return int number of rows contained in the result.
- * Note, most DBMS may not give a meaningful count.
- * In this case, use "SELECT COUNT(*) FROM tableName" to obtain the number of rows.
- */
- public function getRowCount()
- {
- return $this->_statement->rowCount();
- }
-
- /**
- * @return int the number of columns in the result set.
- * Note, even there's no row in the reader, this still gives correct column number.
- */
- public function getColumnCount()
- {
- return $this->_statement->columnCount();
- }
-
- /**
- * Resets the iterator to the initial state.
- * This method is required by the interface Iterator.
- * @throws TDbException if this method is invoked twice
- */
- public function rewind()
- {
- if($this->_index<0)
- {
- $this->_row=$this->_statement->fetch();
- $this->_index=0;
- }
- else
- throw new TDbException('dbdatareader_rewind_invalid');
- }
-
- /**
- * Returns the index of the current row.
- * This method is required by the interface Iterator.
- * @return integer the index of the current row.
- */
- public function key()
- {
- return $this->_index;
- }
-
- /**
- * Returns the current row.
- * This method is required by the interface Iterator.
- * @return mixed the current row.
- */
- public function current()
- {
- return $this->_row;
- }
-
- /**
- * Moves the internal pointer to the next row.
- * This method is required by the interface Iterator.
- */
- public function next()
- {
- $this->_row=$this->_statement->fetch();
- $this->_index++;
- }
-
- /**
- * Returns whether there is a row of data at current position.
- * This method is required by the interface Iterator.
- * @return boolean whether there is a row of data at current position.
- */
- public function valid()
- {
- return $this->_row!==false;
- }
-}
-
+ * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data + */ + +/** + * TDbDataReader class. + * + * TDbDataReader represents a forward-only stream of rows from a query result set. + * + * To read the current row of data, call {@link read}. The method {@link readAll} + * returns all the rows in a single array. + * + * One can also retrieve the rows of data in TDbDataReader by using foreach: + * <code> + * foreach($reader as $row) + * // $row represents a row of data + * </code> + * Since TDbDataReader is a forward-only stream, you can only traverse it once. + * + * It is possible to use a specific mode of data fetching by setting + * {@link setFetchMode FetchMode}. See {@link http://www.php.net/manual/en/function.PDOStatement-setFetchMode.php} + * for more details. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id$ + * @package System.Data + * @since 3.0 + */ +class TDbDataReader extends TComponent implements Iterator +{ + private $_statement; + private $_closed=false; + private $_row; + private $_index=-1; + + /** + * Constructor. + * @param TDbCommand the command generating the query result + */ + public function __construct(TDbCommand $command) + { + $this->_statement=$command->getPdoStatement(); + $this->_statement->setFetchMode(PDO::FETCH_ASSOC); + } + + /** + * Binds a column to a PHP variable. + * When rows of data are being fetched, the corresponding column value + * will be set in the variable. Note, the fetch mode must include PDO::FETCH_BOUND. + * @param mixed Number of the column (1-indexed) or name of the column + * in the result set. If using the column name, be aware that the name + * should match the case of the column, as returned by the driver. + * @param mixed Name of the PHP variable to which the column will be bound. + * @param int Data type of the parameter + * @see http://www.php.net/manual/en/function.PDOStatement-bindColumn.php + */ + public function bindColumn($column, &$value, $dataType=null) + { + if($dataType===null) + $this->_statement->bindColumn($column,$value); + else + $this->_statement->bindColumn($column,$value,$dataType); + } + + /** + * @see http://www.php.net/manual/en/function.PDOStatement-setFetchMode.php + */ + public function setFetchMode($mode) + { + $params=func_get_args(); + call_user_func_array(array($this->_statement,'setFetchMode'),$params); + } + + /** + * Advances the reader to the next row in a result set. + * @return array|false the current row, false if no more row available + */ + public function read() + { + return $this->_statement->fetch(); + } + + /** + * Returns a single column from the next row of a result set. + * @param int zero-based column index + * @return mixed|false the column of the current row, false if no more row available + */ + public function readColumn($columnIndex) + { + return $this->_statement->fetchColumn($columnIndex); + } + + /** + * Returns a single column from the next row of a result set. + * @param string class name of the object to be created and populated + * @param array list of column names whose values are to be passed as parameters in the constructor of the class being created + * @return mixed|false the populated object, false if no more row of data available + */ + public function readObject($className,$fields) + { + return $this->_statement->fetchObject($className,$fields); + } + + /** + * Reads the whole result set into an array. + * @return array the result set (each array element represents a row of data). + * An empty array will be returned if the result contains no row. + */ + public function readAll() + { + return $this->_statement->fetchAll(); + } + + /** + * Advances the reader to the next result when reading the results of a batch of statements. + * This method is only useful when there are multiple result sets + * returned by the query. Not all DBMS support this feature. + */ + public function nextResult() + { + return $this->_statement->nextRowset(); + } + + /** + * Closes the reader. + * Any further data reading will result in an exception. + */ + public function close() + { + $this->_statement->closeCursor(); + $this->_closed=true; + } + + /** + * @return boolean whether the reader is closed or not. + */ + public function getIsClosed() + { + return $this->_closed; + } + + /** + * @return int number of rows contained in the result. + * Note, most DBMS may not give a meaningful count. + * In this case, use "SELECT COUNT(*) FROM tableName" to obtain the number of rows. + */ + public function getRowCount() + { + return $this->_statement->rowCount(); + } + + /** + * @return int the number of columns in the result set. + * Note, even there's no row in the reader, this still gives correct column number. + */ + public function getColumnCount() + { + return $this->_statement->columnCount(); + } + + /** + * Resets the iterator to the initial state. + * This method is required by the interface Iterator. + * @throws TDbException if this method is invoked twice + */ + public function rewind() + { + if($this->_index<0) + { + $this->_row=$this->_statement->fetch(); + $this->_index=0; + } + else + throw new TDbException('dbdatareader_rewind_invalid'); + } + + /** + * Returns the index of the current row. + * This method is required by the interface Iterator. + * @return integer the index of the current row. + */ + public function key() + { + return $this->_index; + } + + /** + * Returns the current row. + * This method is required by the interface Iterator. + * @return mixed the current row. + */ + public function current() + { + return $this->_row; + } + + /** + * Moves the internal pointer to the next row. + * This method is required by the interface Iterator. + */ + public function next() + { + $this->_row=$this->_statement->fetch(); + $this->_index++; + } + + /** + * Returns whether there is a row of data at current position. + * This method is required by the interface Iterator. + * @return boolean whether there is a row of data at current position. + */ + public function valid() + { + return $this->_row!==false; + } +} + diff --git a/framework/Data/TDbTransaction.php b/framework/Data/TDbTransaction.php index c60deec5..618ec79e 100644 --- a/framework/Data/TDbTransaction.php +++ b/framework/Data/TDbTransaction.php @@ -1,112 +1,112 @@ -<?php
-/**
- * TDbTransaction class file
- *
- * @author Qiang Xue <qiang.xue@gmail.com>
- * @link http://www.pradosoft.com/
+<?php +/** + * TDbTransaction class file + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data
- */
-
-Prado::using('System.Data.TDbDataReader');
-
-/**
- * TDbTransaction class.
- *
- * TDbTransaction represents a DB transaction.
- * It is usually created by calling {@link TDbConnection::beginTransaction}.
- *
- * The following code is a common scenario of using transactions:
- * <code>
- * try
- * {
- * $transaction=$connection->beginTransaction();
- * $connection->createCommand($sql1)->execute();
- * $connection->createCommand($sql2)->execute();
- * //.... other SQL executions
- * $transaction->commit();
- * }
- * catch(Exception $e)
- * {
- * $transaction->rollBack();
- * }
- * </code>
- *
- * @author Qiang Xue <qiang.xue@gmail.com>
- * @version $Id$
- * @package System.Data
- * @since 3.0
- */
-class TDbTransaction extends TComponent
-{
- private $_connection=null;
- private $_active;
-
- /**
- * Constructor.
- * @param TDbConnection the connection associated with this transaction
- * @see TDbConnection::beginTransaction
- */
- public function __construct(TDbConnection $connection)
- {
- $this->_connection=$connection;
- $this->setActive(true);
- }
-
- /**
- * Commits a transaction.
- * @throws TDbException if the transaction or the DB connection is not active.
- */
- public function commit()
- {
- if($this->_active && $this->_connection->getActive())
- {
- $this->_connection->getPdoInstance()->commit();
- $this->_active=false;
- }
- else
- throw new TDbException('dbtransaction_transaction_inactive');
- }
-
- /**
- * Rolls back a transaction.
- * @throws TDbException if the transaction or the DB connection is not active.
- */
- public function rollback()
- {
- if($this->_active && $this->_connection->getActive())
- {
- $this->_connection->getPdoInstance()->rollBack();
- $this->_active=false;
- }
- else
- throw new TDbException('dbtransaction_transaction_inactive');
- }
-
- /**
- * @return TDbConnection the DB connection for this transaction
- */
- public function getConnection()
- {
- return $this->_connection;
- }
-
- /**
- * @return boolean whether this transaction is active
- */
- public function getActive()
- {
- return $this->_active;
- }
-
- /**
- * @param boolean whether this transaction is active
- */
- protected function setActive($value)
- {
- $this->_active=TPropertyValue::ensureBoolean($value);
- }
-}
-
+ * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data + */ + +Prado::using('System.Data.TDbDataReader'); + +/** + * TDbTransaction class. + * + * TDbTransaction represents a DB transaction. + * It is usually created by calling {@link TDbConnection::beginTransaction}. + * + * The following code is a common scenario of using transactions: + * <code> + * try + * { + * $transaction=$connection->beginTransaction(); + * $connection->createCommand($sql1)->execute(); + * $connection->createCommand($sql2)->execute(); + * //.... other SQL executions + * $transaction->commit(); + * } + * catch(Exception $e) + * { + * $transaction->rollBack(); + * } + * </code> + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id$ + * @package System.Data + * @since 3.0 + */ +class TDbTransaction extends TComponent +{ + private $_connection=null; + private $_active; + + /** + * Constructor. + * @param TDbConnection the connection associated with this transaction + * @see TDbConnection::beginTransaction + */ + public function __construct(TDbConnection $connection) + { + $this->_connection=$connection; + $this->setActive(true); + } + + /** + * Commits a transaction. + * @throws TDbException if the transaction or the DB connection is not active. + */ + public function commit() + { + if($this->_active && $this->_connection->getActive()) + { + $this->_connection->getPdoInstance()->commit(); + $this->_active=false; + } + else + throw new TDbException('dbtransaction_transaction_inactive'); + } + + /** + * Rolls back a transaction. + * @throws TDbException if the transaction or the DB connection is not active. + */ + public function rollback() + { + if($this->_active && $this->_connection->getActive()) + { + $this->_connection->getPdoInstance()->rollBack(); + $this->_active=false; + } + else + throw new TDbException('dbtransaction_transaction_inactive'); + } + + /** + * @return TDbConnection the DB connection for this transaction + */ + public function getConnection() + { + return $this->_connection; + } + + /** + * @return boolean whether this transaction is active + */ + public function getActive() + { + return $this->_active; + } + + /** + * @param boolean whether this transaction is active + */ + protected function setActive($value) + { + $this->_active=TPropertyValue::ensureBoolean($value); + } +} + |