diff options
author | wei <> | 2006-04-14 06:22:09 +0000 |
---|---|---|
committer | wei <> | 2006-04-14 06:22:09 +0000 |
commit | 3d3f8d3832921f99daf8ce1953304763c2e76c62 (patch) | |
tree | e1b0a9bc3a13fccd253770fb452ac96cc6315121 /framework/DataAccess/SQLMap/Statements | |
parent | 373d8acc503b94ea09823f49e2ab5e395eccc584 (diff) |
Importing SQLMap + sample + docs.
Diffstat (limited to 'framework/DataAccess/SQLMap/Statements')
11 files changed, 1403 insertions, 0 deletions
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 @@ +<?php
+
+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 getSqlMap();
+
+ /**
+ * 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, $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);
+}
+
+?>
\ 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 @@ +<?php
+
+/**
+ *
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @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 @@ +<?php
+
+class TDeleteMappedStatement extends TUpdateMappedStatement
+{
+}
+
+?>
\ 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 @@ +<?php
+
+class TInsertMappedStatement extends TMappedStatement
+{
+ public function executeQueryForMap($connection, $parameter,
+ $keyProperty, $valueProperty=null)
+ {
+ throw new TSqlMapExecutionException(
+ 'sqlmap_cannot_execute_query_for_map', get_class($this));
+ }
+
+ public function executeUpdate($connection, $parameter)
+ {
+ throw new TSqlMapExecutionException(
+ 'sqlmap_cannot_execute_update', get_class($this));
+ }
+
+ public function executeQueryForList($connection, $parameter, $result,
+ $skip=-1, $max=-1)
+ {
+ throw new TSqlMapExecutionException(
+ 'sqlmap_cannot_execute_query_for_list', get_class($this));
+ }
+
+ public function executeQueryForObject($connection, $parameter, $result)
+ {
+ throw new TSqlMapExecutionException(
+ 'sqlmap_cannot_execute_query_for_object', get_class($this));
+ }
+}
+
+?>
\ 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 @@ +<?php
+/**
+ * TMappedStatement and related classes.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @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 <weizhuo[at]gmail[dot]com>
+ * @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
+ * <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, $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
+ * <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, $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 <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)
+ {
+ $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 <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, $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
+ * <tt>TMappedStatement::getGroupByResults()</tt> and <tt>TResultMapGroupBy::updateProperties()</tt>
+ * @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 <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(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 @@ +<?php
+
+class TPreparedCommand
+{
+
+ public function create($connection, $statement, $parameterObject)
+ {
+ $prepared = $statement->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 @@ +<?php
+
+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; }
+
+}
+
+?>
\ 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 @@ +<?php
+
+class TPreparedStatementFactory
+{
+ private $_statement;
+ private $_preparedStatement;
+ private $_parameterPrefix = 'param';
+ private $_commandText;
+
+ public function __construct($statement, $sqlString)
+ {
+ $this->_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 @@ +<?php
+
+class TSelectMappedStatement extends TMappedStatement
+{
+ public function executeInsert($connection, $parameter)
+ {
+ throw new TSqlMapExecutionException(
+ 'sqlmap_cannot_execute_insert', get_class($this));
+ }
+
+ public function executeUpdate($connection, $parameter)
+ {
+ throw new TSqlMapExecutionException(
+ 'sqlmap_cannot_execute_update', get_class($this));
+ }
+
+}
+
+?>
\ 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 @@ +<?php
+
+class TStaticSql extends TComponent
+{
+ private $_preparedStatement;
+
+ public function buildPreparedStatement($statement, $sqlString)
+ {
+ $factory = new TPreparedStatementFactory($statement, $sqlString);
+ $this->_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 @@ +<?php
+
+class TUpdateMappedStatement extends TMappedStatement
+{
+ public function executeInsert($connection, $parameter)
+ {
+ throw new TSqlMapExecutionException(
+ 'sqlmap_cannot_execute_insert', get_class($this));
+ }
+
+ public function executeQueryForMap($connection, $parameter, $keyProperty,
+ $valueProperty=null)
+ {
+ throw new TSqlMapExecutionException(
+ 'sqlmap_cannot_execute_query_for_map', get_class($this));
+ }
+
+ public function executeQueryForList($connection, $parameter, $result,
+ $skip=-1, $max=-1)
+ {
+ throw new TSqlMapExecutionException(
+ 'sqlmap_cannot_execute_query_for_list', get_class($this));
+ }
+
+ public function executeQueryForObject($connection, $parameter, $result)
+ {
+ throw new TSqlMapExecutionException(
+ 'sqlmap_cannot_execute_query_for_object', get_class($this));
+ }
+}
+
+?>
\ No newline at end of file |