From 03f362a40a8dd39f8c8b4bf816334922b7b264e4 Mon Sep 17 00:00:00 2001 From: wei <> Date: Tue, 9 Jan 2007 10:42:06 +0000 Subject: add TActiveRecord::findAllByPks() --- .../Data/ActiveRecord/Exceptions/messages.txt | 4 +- framework/Data/ActiveRecord/TActiveRecord.php | 33 +++++++- .../Data/ActiveRecord/TActiveRecordGateway.php | 15 ++++ framework/Data/ActiveRecord/Vendor/TDbMetaData.php | 92 +++++++++------------- .../Data/ActiveRecord/Vendor/TDbMetaDataCommon.php | 26 ++++-- 5 files changed, 105 insertions(+), 65 deletions(-) (limited to 'framework/Data') diff --git a/framework/Data/ActiveRecord/Exceptions/messages.txt b/framework/Data/ActiveRecord/Exceptions/messages.txt index 92bdb30f..774c0275 100644 --- a/framework/Data/ActiveRecord/Exceptions/messages.txt +++ b/framework/Data/ActiveRecord/Exceptions/messages.txt @@ -10,4 +10,6 @@ ar_primary_key_is_scalar = Primary key '{1}' in table '{0}' is NOT a composi ar_invalid_db_connection = Missing or invalid default database connection for ActiveRecord class '{0}', default connection is set by the DbConnection property of TActiveRecordManager. ar_mismatch_args_exception = ActiveRecord finder method '{0}' expects {1} parameters but found only {2} parameters instead. ar_invalid_tablename_property = ActiveRecord tablename property '{0}::${1}' must be static and not null. -ar_value_must_not_be_null = Property '{0}::${2}' must not be null as defined by column '{2}' in table '{1}'. \ No newline at end of file +ar_value_must_not_be_null = Property '{0}::${2}' must not be null as defined by column '{2}' in table '{1}'. +ar_missing_pk_values = Missing primary key values in forming IN(key1, key2, ...) for table '{0}'. +ar_pk_value_count_mismatch = Composite key value count mismatch in forming IN( (key1, key2, ..), (key3, key4, ..)) for table '{0}'. \ No newline at end of file diff --git a/framework/Data/ActiveRecord/TActiveRecord.php b/framework/Data/ActiveRecord/TActiveRecord.php index 68d63a23..d3e25dcf 100644 --- a/framework/Data/ActiveRecord/TActiveRecord.php +++ b/framework/Data/ActiveRecord/TActiveRecord.php @@ -38,7 +38,7 @@ Prado::using('System.Data.ActiveRecord.TActiveRecordCriteria'); * public $username; //corresponds to the fieldname in the table * public $email; * - * private static final $_tablename='users'; //optional table name. + * public static final $_tablename='users'; //optional table name. * * //returns active record finder instance * public static function finder() @@ -332,13 +332,42 @@ abstract class TActiveRecord extends TComponent */ public function findByPk($keys) { - if(func_num_args() > 1 && !is_array($keys)) + if(func_num_args() > 1) $keys = func_get_args(); $gateway = $this->getRecordManager()->getRecordGateway(); $data = $gateway->findRecordByPK($this,$keys); return $this->populateObject(get_class($this), $data); } + /** + * Find multiple records matching a list of primary or composite keys. + * + * For scalar primary keys: + * + * $finder->findAllByPk($key1, $key2, ...); + * $finder->findAllByPk(array($key1, $key2, ...)); + * + * + * For composite keys: + * + * $finder->findAllByPk(array($key1, $key2), array($key3, $key4), ...); + * $finder->findAllByPk(array(array($key1, $key2), array($key3, $key4), ...)); + * + * @param mixed primary keys + * @return array matching ActiveRecords + */ + public function findAllByPks($keys) + { + if(func_num_args() > 1) + $keys = func_get_args(); + $gateway = $this->getRecordManager()->getRecordGateway(); + $results = array(); + $class = get_class($this); + foreach($gateway->findRecordsByPks($this,(array)$keys) as $data) + $results[] = $this->populateObject($class,$data); + return $results; + } + /** * Find records using full SQL, returns corresponding record object. * @param string select SQL diff --git a/framework/Data/ActiveRecord/TActiveRecordGateway.php b/framework/Data/ActiveRecord/TActiveRecordGateway.php index 1cb1c79f..7bcd0eb2 100644 --- a/framework/Data/ActiveRecord/TActiveRecordGateway.php +++ b/framework/Data/ActiveRecord/TActiveRecordGateway.php @@ -146,6 +146,21 @@ class TActiveRecordGateway extends TComponent return $meta->postQueryRow($command->queryRow()); } + /** + * Returns records matching the list of given primary keys. + * @param TActiveRecord active record instance. + * @param array list of primary name value pairs + * @return array matching data. + */ + public function findRecordsByPks(TActiveRecord $record, $keys) + { + $meta = $this->getMetaData($record); + $command = $meta->getFindInPksCommand($record->getDbConnection(), $keys); + $this->raiseCommandEvent(TActiveRecordStatementType::Select,$command,$record,$keys); + return $meta->postQuery($command->query()); + } + + /** * Returns record data matching the given critera. If $iterator is true, it will * return multiple rows as TDbDataReader otherwise it returns the first row data. diff --git a/framework/Data/ActiveRecord/Vendor/TDbMetaData.php b/framework/Data/ActiveRecord/Vendor/TDbMetaData.php index efb7c467..38a82aef 100644 --- a/framework/Data/ActiveRecord/Vendor/TDbMetaData.php +++ b/framework/Data/ActiveRecord/Vendor/TDbMetaData.php @@ -136,6 +136,43 @@ abstract class TDbMetaData extends TComponent return implode(' AND ', $criteria); } + /** + * Construct a "pk IN ('key1', 'key2', ...)" criteria. + * @param TDbConnection database connection. + * @param array values for IN predicate + * @param string SQL string for primary keys IN a list. + */ + protected function getCompositeKeysCriteria($conn, $values) + { + $count = count($this->getPrimaryKeys()); + if($count===0) + throw new TActiveRecordException('ar_no_primary_key_found',$this->getTableName()); + if(!is_array($values) || count($values) === 0) + throw new TActiveRecordException('ar_missing_pk_values', $this->getTableName()); + if($count>1 && !is_array($values[0])) + $values = array($values); + if($count > 1 && count($values[0]) !== $count) + throw new TActiveRecordException('ar_pk_value_count_mismatch', $this->getTableName()); + + $columns = array(); + foreach($this->getPrimaryKeys() as $key) + $columns[] = $this->getColumn($key)->getName(); + return '('.implode(', ',$columns).') IN '.$this->quoteTuple($conn, $values); + } + + /** + * @param TDbConnection database connection. + * @param array values + * @return string quoted recursive tuple values, e.g. "('val1', 'val2')". + */ + protected function quoteTuple($conn, $array) + { + $data = array(); + foreach($array as $k=>$v) + $data[] = is_array($v) ? $this->quoteTuple($conn, $v) : $conn->quoteString($v); + return '('.implode(', ', $data).')'; + } + /** * Bind a list of variables in the command. The named parameters is taken * from the values of the $keys parameter. The bind value is taken from the @@ -308,60 +345,5 @@ x * @param array name value pairs of columns for update. return implode(', ', $fields); } - /** - * @param TDbConnection database connection - * @param array primary key values. - * @return string delete criteria for multiple scalar primary keys. - */ - protected function getDeleteInPkCriteria($conn, $keys) - { - $pk = $this->getPrimaryKeys(); - $column = $this->getColumn($pk[0])->getName(); - $values = array(); - foreach($keys as $key) - { - if(is_array($key)) - { - throw new TActiveRecordException('ar_primary_key_is_scalar', - $this->getTableName(),$column,'array('.implode(', ',$key).')'); - } - $values[] = $conn->quoteString($key); - } - $pks = implode(', ', $values); - return "$column IN ($pks)"; - } - - /** - * @param TDbConnection database connection - * @param array primary key values. - * @return string delete criteria for multiple composite primary keys. - */ - protected function getDeleteMultiplePkCriteria($conn,$pks) - { - //check for 1 set composite keys - if(count($pks)>0 && !is_array($pks[0])) - $pks = array($pks); - $conditions=array(); - foreach($pks as $keys) - $conditions[] = $this->getDeleteCompositeKeyCondition($conn,$keys); - return implode(' OR ', $conditions); - } - - /** - * @return string delete criteria for 1 composite key. - */ - protected function getDeleteCompositeKeyCondition($conn,$keys) - { - $condition=array(); - $index = 0; - foreach($this->getPrimarykeys() as $pk) - { - $name = $this->getColumn($pk)->getName(); - $value = isset($keys[$pk]) ? $keys[$pk] : $keys[$index]; - $condition[] = "$name = ".$conn->quoteString($value); - $index++; - } - return '('.implode(' AND ', $condition).')'; - } } ?> \ No newline at end of file diff --git a/framework/Data/ActiveRecord/Vendor/TDbMetaDataCommon.php b/framework/Data/ActiveRecord/Vendor/TDbMetaDataCommon.php index fffdb6fb..7f7dad8b 100644 --- a/framework/Data/ActiveRecord/Vendor/TDbMetaDataCommon.php +++ b/framework/Data/ActiveRecord/Vendor/TDbMetaDataCommon.php @@ -40,6 +40,24 @@ abstract class TDbMetaDataCommon extends TDbMetaData return $command; } + /** + * SQL database command for finding records by a list of primary keys. + * @param TDbConnection database connection. + * @param array list of primary keys to match. + * @return TDbCommand find by list of primary keys command. + */ + public function getFindInPksCommand($conn, $keys) + { + $conn->setActive(true); + $columns = $this->getSelectionColumns(); + $table = $this->getTableName(); + $criteria = $this->getCompositeKeysCriteria($conn,$keys); + $sql = "SELECT {$columns} FROM {$table} WHERE {$criteria}"; + $command = $conn->createCommand($sql); + $command->prepare(); + return $command; + } + /** * SQL database command for finding records using a criteria object. * @param TDbConnection database connection. @@ -160,14 +178,8 @@ abstract class TDbMetaDataCommon extends TDbMetaData public function getDeleteByPkCommand($conn,$keys) { $conn->setActive(true); - $numKeys = count($this->getPrimaryKeys()); $table = $this->getTableName(); - if($numKeys===0) - throw new TActiveRecordException('ar_no_primary_key_found',$table); - if($numKeys===1) - $criteria = $this->getDeleteInPkCriteria($conn,$keys); - else - $criteria = $this->getDeleteMultiplePkCriteria($conn,$keys); + $criteria = $this->getCompositeKeysCriteria($conn, $keys); $sql = "DELETE FROM {$table} WHERE {$criteria}"; $command = $conn->createCommand($sql); $command->prepare(); -- cgit v1.2.3