Blame | Letzte Änderung | Log anzeigen | RSS feed
<?php/*** TDataGatewayCommand, TDataGatewayEventParameter and TDataGatewayResultEventParameter class file.** @author Wei Zhuo <weizhuo[at]gmail[dot]com>* @link http://www.pradosoft.com/* @copyright Copyright © 2005-2008 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();$command = $this->getBuilder()->createFindCommand($where,$parameters,$ordering,$limit,$offset);$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 && !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] = $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;}}?>