From 3d3f8d3832921f99daf8ce1953304763c2e76c62 Mon Sep 17 00:00:00 2001 From: wei <> Date: Fri, 14 Apr 2006 06:22:09 +0000 Subject: Importing SQLMap + sample + docs. --- .../SQLMap/Statements/IMappedStatement.php | 71 ++ .../SQLMap/Statements/TCachingStatement.php | 102 +++ .../SQLMap/Statements/TDeleteMappedStatement.php | 7 + .../SQLMap/Statements/TInsertMappedStatement.php | 32 + .../SQLMap/Statements/TMappedStatement.php | 988 +++++++++++++++++++++ .../SQLMap/Statements/TPreparedCommand.php | 60 ++ .../SQLMap/Statements/TPreparedStatement.php | 26 + .../Statements/TPreparedStatementFactory.php | 47 + .../SQLMap/Statements/TSelectMappedStatement.php | 19 + .../DataAccess/SQLMap/Statements/TStaticSql.php | 19 + .../SQLMap/Statements/TUpdateMappedStatement.php | 32 + 11 files changed, 1403 insertions(+) create mode 100644 framework/DataAccess/SQLMap/Statements/IMappedStatement.php create mode 100644 framework/DataAccess/SQLMap/Statements/TCachingStatement.php create mode 100644 framework/DataAccess/SQLMap/Statements/TDeleteMappedStatement.php create mode 100644 framework/DataAccess/SQLMap/Statements/TInsertMappedStatement.php create mode 100644 framework/DataAccess/SQLMap/Statements/TMappedStatement.php create mode 100644 framework/DataAccess/SQLMap/Statements/TPreparedCommand.php create mode 100644 framework/DataAccess/SQLMap/Statements/TPreparedStatement.php create mode 100644 framework/DataAccess/SQLMap/Statements/TPreparedStatementFactory.php create mode 100644 framework/DataAccess/SQLMap/Statements/TSelectMappedStatement.php create mode 100644 framework/DataAccess/SQLMap/Statements/TStaticSql.php create mode 100644 framework/DataAccess/SQLMap/Statements/TUpdateMappedStatement.php (limited to 'framework/DataAccess/SQLMap/Statements') diff --git a/framework/DataAccess/SQLMap/Statements/IMappedStatement.php b/framework/DataAccess/SQLMap/Statements/IMappedStatement.php new file mode 100644 index 00000000..6a4d76db --- /dev/null +++ b/framework/DataAccess/SQLMap/Statements/IMappedStatement.php @@ -0,0 +1,71 @@ +$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 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 $keyProperty. + */ + 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, $skip=-1, $max=-1); + + + /** + * Executes an SQL statement that returns a single row as an object + * of the type of the $result 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); +} + +?> \ No newline at end of file diff --git a/framework/DataAccess/SQLMap/Statements/TCachingStatement.php b/framework/DataAccess/SQLMap/Statements/TCachingStatement.php new file mode 100644 index 00000000..8e4e2a3d --- /dev/null +++ b/framework/DataAccess/SQLMap/Statements/TCachingStatement.php @@ -0,0 +1,102 @@ + + * @version $Revision: $ $Date: $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TCachingStatement 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 getSqlMap() + { + return $this->_mappedStatement->getSqlMap(); + } + + public function executeQueryForMap($connection, $parameter, + $keyProperty, $valueProperty=null, $delegate=null) + { + $sql = $this->createCommand($connection, $parameter); + $key = $this->getCacheKey(array($sql, $keyProperty, $valueProperty)); + $map = $this->getStatement()->getCache()->get($key); + if(is_null($map)) + { + $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, $skip=-1, $max=-1, $delegate=null) + { + $sql = $this->createCommand($connection, $parameter); + $key = $this->getCacheKey(array($sql, $skip, $max)); + $list = $this->getStatement()->getCache()->get($key); + if(is_null($list)) + { + $list = $this->_mappedStatement->runQueryForList( + $connection, $parameter, $sql, $result, $skip, $max, $delegate); + $this->getStatement()->getCache()->set($key, $list); + } + return $list; + } + + public function executeQueryForObject($connection, $parameter, $result) + { + $sql = $this->createCommand($connection, $parameter); + $key = $this->getCacheKey($sql); + $object = $this->getStatement()->getCache()->get($key); + if(is_null($object)) + { + $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) + { + return $this->_mappedStatement->getCommand()->create( + $connection, $this->getStatement(), $parameter); + } +} + +?> \ No newline at end of file diff --git a/framework/DataAccess/SQLMap/Statements/TDeleteMappedStatement.php b/framework/DataAccess/SQLMap/Statements/TDeleteMappedStatement.php new file mode 100644 index 00000000..9a1c8fae --- /dev/null +++ b/framework/DataAccess/SQLMap/Statements/TDeleteMappedStatement.php @@ -0,0 +1,7 @@ + \ No newline at end of file diff --git a/framework/DataAccess/SQLMap/Statements/TInsertMappedStatement.php b/framework/DataAccess/SQLMap/Statements/TInsertMappedStatement.php new file mode 100644 index 00000000..7a444c89 --- /dev/null +++ b/framework/DataAccess/SQLMap/Statements/TInsertMappedStatement.php @@ -0,0 +1,32 @@ + \ No newline at end of file diff --git a/framework/DataAccess/SQLMap/Statements/TMappedStatement.php b/framework/DataAccess/SQLMap/Statements/TMappedStatement.php new file mode 100644 index 00000000..483a315a --- /dev/null +++ b/framework/DataAccess/SQLMap/Statements/TMappedStatement.php @@ -0,0 +1,988 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Revision: $ $Date: $ + * @package System.DataAccess.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 + * @version $Revision: $ $Date: $ + * @package System.DataAccess.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 $_sqlMap; + + /** + * @var TPostSelectBinding[] post select statement queue. + */ + private $_selectQueque=array(); + + /** + * @var boolean true when data is mapped to a particular row. + */ + private $_IsRowDataFound = false; + + /** + * @var array group by result data cache. + */ + private $_groupBy=array(); + + /** + * @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 getSqlMap() + { + return $this->_sqlMap; + } + + /** + * @return TPreparedCommand command to prepare SQL statements. + */ + public function getCommand() + { + return $this->_command; + } + + /** + * @return TResultMapGroupBy[] list of results obtained from group by result maps. + */ + protected function getGroupByResults() + { + return $this->_groupBy; + } + + /** + * Empty the group by results cache. + */ + protected function clearGroupByResults() + { + $this->_groupBy = array(); + } + + /** + * Creates a new mapped statement. + * @param TSqlMapper an sqlmap. + * @param TSqlMapStatement An SQL statement. + */ + public function __construct(TSqlMapper $sqlMap, TSqlMapStatement $statement) + { + $this->_sqlMap = $sqlMap; + $this->_statement = $statement; + $this->_command = new TPreparedCommand(); + } + + /** + * 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, $sql, $max, $skip) + { + 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, $skip=-1, $max=-1, $delegate=null) + { + $sql = $this->_command->create($connection, $this->_statement, $parameter); + return $this->runQueryForList($connection, $parameter, $sql, $result, $skip, $max, $delegate); + } + + /** + * Executes the SQL and retuns a List of result objects. + * + * This method should only be called by internal developers, consider using + * executeQueryForList() 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, $skip=-1, $max=-1, $delegate=null) + { + $list = $result instanceof ArrayAccess ? $result : + $this->_statement->createInstanceOfListClass(); + $recordSet = $this->executeSQLQueryLimit($connection, $sql, $max, $skip); + + if(!is_null($delegate)) + { + while($row = $recordSet->fetchRow()) + { + $obj = $this->applyResultMap($row); + $param = new TResultSetListItemParameter($obj, $parameter, $list); + $this->raiseRowDelegate($delegate, $param); + } + } + else + { + while($row = $recordSet->fetchRow()) + { + $obj = $this->applyResultMap($row); + if(!is_null($obj)) + $list[] = $obj; + } + } + + //get the groupings + foreach($this->getGroupbyResults() as $group) + $list[] = $group->updateProperties(); + $this->clearGroupByResults(); + + $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, $delegate=null) + { + $sql = $this->_command->create($connection, $this->_statement, $parameter); + 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 + * executeQueryForMap() 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, $sql, $keyProperty, $valueProperty=null, $delegate=null) + { + $map = array(); + $recordSet = $this->executeSQLQuery($connection, $sql); + if(!is_null($delegate)) + { + while($row = $recordSet->fetchRow()) + { + $obj = $this->applyResultMap($row); + $key = TPropertyAccess::get($obj, $keyProperty); + $value = is_null($valueProperty) ? $obj : + TPropertyAccess::get($obj, $valueProperty); + $param = new TResultSetMapItemParameter($key, $value, $parameter, $map); + $this->raiseRowDelegate($delegate, $param); + } + } + else + { + while($row = $recordSet->fetchRow()) + { + $obj = $this->applyResultMap($row); + $key = TPropertyAccess::get($obj, $keyProperty); + $map[$key] = is_null($valueProperty) ? $obj : + TPropertyAccess::get($obj, $valueProperty); + } + } + $this->onExecuteQuery($sql); + 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 $result 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) + { + $sql = $this->_command->create($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 $result passed in as a parameter. + * + * This method should only be called by internal developers, consider using + * executeQueryForObject() 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, $sql, $result) + { + $recordSet = $this->executeSQLQuery($connection, $sql); + $object = $this->applyResultMap($recordSet->fetchRow(), $result); + + //get group by result + foreach($this->getGroupbyResults() as $group) + $object = $group->updateProperties(); + $this->clearGroupByResults(); + + $this->executePostSelect($connection); + $this->onExecuteQuery($sql); + 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); + + $sql = $this->_command->create($connection, $this->_statement, $parameter); + + $this->executeSQLQuery($connection, $sql); + + if(is_null($generatedKey)) + $generatedKey = $this->getPostGeneratedSelectKey($connection, $parameter); + + $this->executePostSelect($connection); + $this->onExecuteQuery($sql); + 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(!is_null($selectKey) && !$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(!is_null($selectKey) && $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->sqlMap->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($connection, $this->_statement, $parameter); + $this->executeSQLQuery($connection, $sql); + $this->executePostSelect($connection); + $this->onExecuteQuery($sql); + return $connection->Affected_Rows(); + } + + /** + * Process 'select' result properties + * @param IDbConnection database connection + */ + protected function executePostSelect($connection) + { + + while(count($this->_selectQueque)) + { + $postSelect = array_shift($this->_selectQueque); + $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(); + + if($this->_sqlMap->getResultMaps()->contains($resultMapName)) + return $this->fillResultMap($resultMapName, $row, $resultObject); + else if(strlen($resultClass) > 0) + return $this->fillResultClass($resultClass, $row, $resultObject); + else + return $this->fillDefaultResultMap(null, $row, $resultObject); + } + + /** + * 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(is_null($resultObject)) + $resultObject = $this->_statement->createInstanceOfResultClass(); + + 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; + foreach($row as $k=>$v) + { + $property = new TResultProperty; + $property->initialize($this->_sqlMap); + if(is_string($k) && strlen($k) > 0) + $property->setColumn($k); + $property->setColumnIndex(++$index); + $type = gettype(TPropertyAccess::get($resultObject,$k)); + $property->setType($type); + $value = $property->getDatabaseValue($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, $resultObject) + { + $resultMap = $this->_sqlMap->getResultMap($resultMapName); + $resultMap = $resultMap->resolveSubMap($row); + + if(is_null($resultObject)) + $resultObject = $resultMap->createInstanceOfResult(); + + if(is_object($resultObject)) + { + if(strlen($resultMap->getGroupBy()) > 0) + return $this->addResultMapGroupBy($resultMap, $row, $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 results temporarily, retrieve it later using + * TMappedStatement::getGroupByResults() and TResultMapGroupBy::updateProperties() + * @param TResultMap result mapping details. + * @param array a result set row retrieved from the database + * @param object the result object + * @return null, always returns null, use getGroupByResults() to obtain the result object list. + * @see getGroupByResults() + * @see runQueryForList() + */ + protected function addResultMapGroupBy($resultMap, $row, $resultObject) + { + $group = $this->getResultMapGroupKey($resultMap, $row, $resultObject); + foreach($resultMap->getColumns() as $property) + { + $this->setObjectProperty($resultMap, $property, $row, $resultObject); + if(strlen($property->getResultMapping()) > 0) + { + $key = $property->getProperty(); + $value = TPropertyAccess::get($resultObject, $key); + $this->_groupBy[$group]->addValue($key, $value); + } + } + return null; + } + + /** + * 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 + * @param object the result object + * @return string groupping key. + * @throws TSqlMapExecutionException if unable to find grouping key. + */ + protected function getResultMapGroupKey($resultMap, $row, $resultObject) + { + $groupBy = $resultMap->getGroupBy(); + if(isset($row[$groupBy])) + { + $group = $row[$groupBy]; + if(!isset($this->_groupBy[$group])) + $this->_groupBy[$group] = new TResultMapGroupBy($group, $resultObject); + return $group; + } + else + { + throw new TSqlMapExecutionException('sqlmap_unable_to_find_groupby', + $groupBy, $resultMap->getID()); + } + } + + /** + * Fill the result map using default settings. If $resultMap is null + * the result object returned will be guessed from $resultObject. + * @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(is_null($resultObject)) + $resultObject=''; + + if(!is_null($resultMap)) + $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(); + foreach($resultMap->getColumns() as $column) + { + if(is_null($column->getType()) + && !is_null($resultObject) && !is_object($resultObject)) + $column->setType(gettype($resultObject)); + $result[$column->getProperty()] = $column->getDatabaseValue($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(); + + if(strlen($select) == 0 && is_null($nested)) + { + $value = $property->getDatabaseValue($row); + TPropertyAccess::set($resultObject, $key, $value); + $this->_IsRowDataFound = $this->_IsRowDataFound || ($value != null); + } + else if(!is_null($nested)) + { + $obj = $nested->createInstanceOfResult(); + 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->_sqlMap->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->isListType($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->isArrayType($resultObject)) + $postSelect->setMethod(self::QUERY_FOR_ARRAY); + else + $postSelect->setMethod(self::QUERY_FOR_OBJECT); + + if(!$property->getLazyLoad()) + array_push($this->_selectQueque, $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 + { + return $property->getOrdinalValue($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; + } +} + +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; } +} + +class TResultMapGroupBy +{ + private $_ID=''; + private $_object=''; + private $_values = array(); + + public function __construct($id, $object) + { + $this->setID($id); + $this->setObject($object); + } + + public function getID(){ return $this->_ID; } + public function setID($value){ $this->_ID = $value; } + + public function getObject(){ return $this->_object; } + public function setObject($value){ $this->_object = $value; } + + public function addValue($property, $value) + { + $this->_values[$property][] = $value; + } + + public function getProperties() + { + return array_keys($this->_values); + } + + public function updateProperties() + { + foreach($this->_values as $property => $value) + { + TPropertyAccess::set($this->getObject(), $property, $value); + } + return $this->getObject(); + } +} + +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; + } +} + +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; + } +} + +?> \ No newline at end of file diff --git a/framework/DataAccess/SQLMap/Statements/TPreparedCommand.php b/framework/DataAccess/SQLMap/Statements/TPreparedCommand.php new file mode 100644 index 00000000..1c0e5ad7 --- /dev/null +++ b/framework/DataAccess/SQLMap/Statements/TPreparedCommand.php @@ -0,0 +1,60 @@ +getSQL()->getPreparedStatement(); + $parameters = $this->applyParameterMap($connection, + $prepared, $statement, $parameterObject); + return array('sql'=>$prepared->getPreparedSql(), + 'parameters'=>$parameters); + } + + protected function applyParameterMap($connection, + $prepared, $statement, $parameterObject) + { + $properties = $prepared->getParameterNames(); + $parameters = $prepared->getParameterValues(); + $values = array(); + for($i = 0, $k=$properties->getCount(); $i<$k; $i++) + { + $property = $statement->parameterMap()->getProperty($i); + $values[] = $statement->parameterMap()->getParameter( + $property, $parameterObject, $statement); + } + return count($values) > 0 ? $values : false; + } + +/* protected function applyParameterClass($connection, $statement, $parameter) + { + $type=$statement->getParameterClass(); + if(strlen($type) < 1) return; + $prepared = $statement->getSql()->getPreparedStatement(); + $names = $prepared->getParameterNames(); + $values = $prepared->getParameterValues(); + switch (strtolower($type)) + { + case 'integer': + case 'int': + $values[$names[0]] = $connection->quote(intval($parameter)); + break; + case 'array': + foreach($names as $name) + { + $key = substr(substr($name,0,-1),1); + if(isset($parameter[$key])) + $values->add($name,$connection->quote($parameter[$key])); + else + throw new TDataMapperException('unable_to_find_parameter', $key); + } + break; + default: + var_dump("todo for other parameter classes"); + } + } +*/ +} + +?> \ No newline at end of file diff --git a/framework/DataAccess/SQLMap/Statements/TPreparedStatement.php b/framework/DataAccess/SQLMap/Statements/TPreparedStatement.php new file mode 100644 index 00000000..e534f532 --- /dev/null +++ b/framework/DataAccess/SQLMap/Statements/TPreparedStatement.php @@ -0,0 +1,26 @@ +_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; } + +} + +?> \ No newline at end of file diff --git a/framework/DataAccess/SQLMap/Statements/TPreparedStatementFactory.php b/framework/DataAccess/SQLMap/Statements/TPreparedStatementFactory.php new file mode 100644 index 00000000..41059934 --- /dev/null +++ b/framework/DataAccess/SQLMap/Statements/TPreparedStatementFactory.php @@ -0,0 +1,47 @@ +_statement = $statement; + $this->_commandText = $sqlString; + // $this->_statement = new TPreparedStatement(); +// $this->_statement->setSqlString($sqlString); + } + + public function prepare() + { + //$this->createParametersFromTextCommand(); + //return $this->_statement; + $this->_preparedStatement = new TPreparedStatement(); + $this->_preparedStatement->setPreparedSql($this->_commandText); + if(!is_null($this->_statement->parameterMap())) + { + $this->createParametersForTextCommand(); + //$this->evaluateParameterMap(); + } + //var_dump($this->_preparedStatement); + return $this->_preparedStatement; + } + + protected function createParametersForTextCommand() + { + /*$matches = array(); + $string = $this->_statement->getSqlString(); + preg_match_all('/#([a-zA-Z0-9._]+)#/', $string, $matches); + $this->_statement->getParameterNames()->copyFrom($matches[0]);*/ + //var_dump($this->_statement); + foreach($this->_statement->ParameterMap()->getProperties() as $prop) + { + $this->_preparedStatement->getParameterNames()->add($prop->getProperty()); + } + } +} + +?> \ No newline at end of file diff --git a/framework/DataAccess/SQLMap/Statements/TSelectMappedStatement.php b/framework/DataAccess/SQLMap/Statements/TSelectMappedStatement.php new file mode 100644 index 00000000..1171e28f --- /dev/null +++ b/framework/DataAccess/SQLMap/Statements/TSelectMappedStatement.php @@ -0,0 +1,19 @@ + \ No newline at end of file diff --git a/framework/DataAccess/SQLMap/Statements/TStaticSql.php b/framework/DataAccess/SQLMap/Statements/TStaticSql.php new file mode 100644 index 00000000..bf6e4a18 --- /dev/null +++ b/framework/DataAccess/SQLMap/Statements/TStaticSql.php @@ -0,0 +1,19 @@ +_preparedStatement = $factory->prepare(); + } + + public function getPreparedStatement() + { + return $this->_preparedStatement; + } +} + +?> \ No newline at end of file diff --git a/framework/DataAccess/SQLMap/Statements/TUpdateMappedStatement.php b/framework/DataAccess/SQLMap/Statements/TUpdateMappedStatement.php new file mode 100644 index 00000000..cf16ee52 --- /dev/null +++ b/framework/DataAccess/SQLMap/Statements/TUpdateMappedStatement.php @@ -0,0 +1,32 @@ + \ No newline at end of file -- cgit v1.2.3