From ee46dba332d2f4244885a64f2c91b651ddbd3cbe Mon Sep 17 00:00:00 2001 From: romanb Date: Sun, 12 Apr 2009 19:02:12 +0000 Subject: [PATCH] [2.0] Moved code between Query and AbstractQuery. Added first NativeQuery implementation. Hydration work and code movements for discriminator column usage. Started implementing Single Table Inheritance. --- .../Common/Collections/Collection.php | 3 +- lib/Doctrine/DBAL/Driver/Statement.php | 6 +- lib/Doctrine/DBAL/Types/Type.php | 3 + lib/Doctrine/ORM/AbstractQuery.php | 1214 +++++------------ lib/Doctrine/ORM/EntityManager.php | 14 +- .../Internal/Hydration/AbstractHydrator.php | 76 +- .../ORM/Internal/Hydration/ArrayHydrator.php | 9 +- .../ORM/Internal/Hydration/ObjectHydrator.php | 94 +- .../ORM/Internal/Hydration/ScalarHydrator.php | 21 +- lib/Doctrine/ORM/Mapping/ClassMetadata.php | 29 +- .../ORM/Mapping/ClassMetadataFactory.php | 2 +- .../ORM/Mapping/Driver/AnnotationDriver.php | 7 +- .../Mapping/Driver/DoctrineAnnotations.php | 1 + lib/Doctrine/ORM/NativeQuery.php | 104 +- .../Persisters/AbstractEntityPersister.php | 8 - .../ORM/Persisters/SingleTablePersister.php | 4 +- lib/Doctrine/ORM/Query.php | 1094 ++++++++++----- lib/Doctrine/ORM/Query/AbstractResult.php | 25 - .../ORM/Query/Exec/SingleSelectExecutor.php | 2 +- lib/Doctrine/ORM/Query/Parser.php | 30 - lib/Doctrine/ORM/Query/ResultSetMapping.php | 63 +- lib/Doctrine/ORM/Query/SqlWalker.php | 34 +- lib/Doctrine/ORM/UnitOfWork.php | 19 +- .../Doctrine/Tests/Models/Forum/ForumUser.php | 6 - .../Tests/ORM/EntityPersisterTest.php | 3 - .../Tests/ORM/Functional/AllTests.php | 2 + .../Tests/ORM/Functional/NativeQueryTest.php | 45 + .../Functional/SingleTableInheritanceTest.php | 103 ++ .../Tests/ORM/Hydration/ArrayHydratorTest.php | 18 +- .../ORM/Hydration/ObjectHydratorTest.php | 20 +- .../ORM/Hydration/ScalarHydratorTest.php | 2 +- .../Hydration/SingleScalarHydratorTest.php | 6 +- 32 files changed, 1562 insertions(+), 1505 deletions(-) create mode 100644 tests/Doctrine/Tests/ORM/Functional/NativeQueryTest.php create mode 100644 tests/Doctrine/Tests/ORM/Functional/SingleTableInheritanceTest.php diff --git a/lib/Doctrine/Common/Collections/Collection.php b/lib/Doctrine/Common/Collections/Collection.php index 1882b4d87..f38866067 100644 --- a/lib/Doctrine/Common/Collections/Collection.php +++ b/lib/Doctrine/Common/Collections/Collection.php @@ -31,8 +31,9 @@ use \ArrayIterator; * A Collection is a thin wrapper around a php array. Think of it as an OO version * of a plain array. * - * @author Roman S. Borschel + * @author Roman S. Borschel * @since 2.0 + * @todo Consider extending ArrayObject */ class Collection implements Countable, IteratorAggregate, ArrayAccess { diff --git a/lib/Doctrine/DBAL/Driver/Statement.php b/lib/Doctrine/DBAL/Driver/Statement.php index c60746a0d..b3f933557 100644 --- a/lib/Doctrine/DBAL/Driver/Statement.php +++ b/lib/Doctrine/DBAL/Driver/Statement.php @@ -49,9 +49,8 @@ interface Statement public function bindColumn($column, &$param, $type = null); /** - * bindValue - * Binds a value to a corresponding named or question mark - * placeholder in the SQL statement that was use to prepare the statement. + * Binds a value to a corresponding named or positional + * placeholder in the SQL statement that was used to prepare the statement. * * @param mixed $param Parameter identifier. For a prepared statement using named placeholders, * this will be a parameter name of the form :name. For a prepared statement @@ -65,7 +64,6 @@ interface Statement public function bindValue($param, $value, $type = null); /** - * bindParam * Binds a PHP variable to a corresponding named or question mark placeholder in the * SQL statement that was use to prepare the statement. Unlike PDOStatement->bindValue(), * the variable is bound as a reference and will only be evaluated at the time diff --git a/lib/Doctrine/DBAL/Types/Type.php b/lib/Doctrine/DBAL/Types/Type.php index 127285751..ee98b4723 100644 --- a/lib/Doctrine/DBAL/Types/Type.php +++ b/lib/Doctrine/DBAL/Types/Type.php @@ -36,7 +36,10 @@ abstract class Type } abstract public function getSqlDeclaration(array $fieldDeclaration, \Doctrine\DBAL\Platforms\AbstractPlatform $platform); + abstract public function getName(); + + //abstract public function getTypeCode(); /** * Factory method to create type instances. diff --git a/lib/Doctrine/ORM/AbstractQuery.php b/lib/Doctrine/ORM/AbstractQuery.php index 9b3e20f91..99608412e 100644 --- a/lib/Doctrine/ORM/AbstractQuery.php +++ b/lib/Doctrine/ORM/AbstractQuery.php @@ -1,5 +1,4 @@ AbstractQuery. + * + * @param Doctrine\ORM\EntityManager $entityManager + */ + public function __construct(EntityManager $entityManager) + { + $this->_em = $entityManager; + $this->free(); + } + + /** + * Retrieves the associated EntityManager of this Query instance. + * + * @return Doctrine\ORM\EntityManager + */ + public function getEntityManager() + { + return $this->_em; + } /** * Frees the resources used by the query object. It especially breaks a @@ -141,668 +139,10 @@ abstract class AbstractQuery */ public function free() { - /** - * @todo [TODO] What about "forUpdate" support? Remove it? - */ - $this->_dqlParts = array( - 'select' => array(), - 'distinct' => false, - 'forUpdate' => false, - 'from' => array(), - 'join' => array(), - 'set' => array(), - 'where' => array(), - 'groupby' => array(), - 'having' => array(), - 'orderby' => array(), - 'limit' => array(), - 'offset' => array(), - ); - - $this->_params = array( - 'join' => array(), - 'set' => array(), - 'where' => array(), - 'having' => array() - ); - + $this->_params = array(); $this->_enumParams = array(); - - $this->_dql = null; - $this->_state = self::STATE_CLEAN; } - - /** - * Defines a complete DQL - * - * @param string $dqlQuery DQL Query - */ - public function setDql($dqlQuery) - { - $this->free(); - - if ($dqlQuery !== null) { - $this->_dql = $dqlQuery; - - $this->_state = self::STATE_DIRTY; - } - } - - - /** - * Returns the DQL query that is represented by this query object. - * - * @return string DQL query - */ - public function getDql() - { - if ($this->_dql !== null) { - return $this->_dql; - } - - $dql = ''; - - switch ($this->_type) { - case self::DELETE: - $dql = $this->_getDqlForDelete(); - break; - - case self::UPDATE: - $dql = $this->_getDqlForUpdate(); - break; - - /** - * @todo [TODO] Remove these ones (INSERT and CREATE)? - */ - /* - case self::INSERT: - break; - - case self::CREATE: - break; - */ - - case self::SELECT: - default: - $dql = $this->_getDqlForSelect(); - break; - } - - return $dql; - } - - - /** - * Builds the DQL of DELETE - */ - protected function _getDqlForDelete() - { - /* - * BNF: - * - * DeleteStatement = DeleteClause [WhereClause] [OrderByClause] [LimitClause] [OffsetClause] - * DeleteClause = "DELETE" "FROM" RangeVariableDeclaration - * WhereClause = "WHERE" ConditionalExpression - * OrderByClause = "ORDER" "BY" OrderByItem {"," OrderByItem} - * LimitClause = "LIMIT" integer - * OffsetClause = "OFFSET" integer - * - */ - return 'DELETE' - . $this->_getReducedDqlQueryPart('from', array('pre' => ' FROM ', 'separator' => ' ')) - . $this->_getReducedDqlQueryPart('where', array('pre' => ' WHERE ', 'separator' => ' ')) - . $this->_getReducedDqlQueryPart('orderby', array('pre' => ' ORDER BY ', 'separator' => ', ')) - . $this->_getReducedDqlQueryPart('limit', array('pre' => ' LIMIT ', 'separator' => ' ')) - . $this->_getReducedDqlQueryPart('offset', array('pre' => ' OFFSET ', 'separator' => ' ')); - } - - - /** - * Builds the DQL of UPDATE - */ - protected function _getDqlForUpdate() - { - /* - * BNF: - * - * UpdateStatement = UpdateClause [WhereClause] [OrderByClause] [LimitClause] [OffsetClause] - * UpdateClause = "UPDATE" RangeVariableDeclaration "SET" UpdateItem {"," UpdateItem} - * WhereClause = "WHERE" ConditionalExpression - * OrderByClause = "ORDER" "BY" OrderByItem {"," OrderByItem} - * LimitClause = "LIMIT" integer - * OffsetClause = "OFFSET" integer - * - */ - return 'UPDATE' - . $this->_getReducedDqlQueryPart('from', array('pre' => ' FROM ', 'separator' => ' ')) - . $this->_getReducedDqlQueryPart('where', array('pre' => ' SET ', 'separator' => ', ')) - . $this->_getReducedDqlQueryPart('where', array('pre' => ' WHERE ', 'separator' => ' ')) - . $this->_getReducedDqlQueryPart('orderby', array('pre' => ' ORDER BY ', 'separator' => ', ')) - . $this->_getReducedDqlQueryPart('limit', array('pre' => ' LIMIT ', 'separator' => ' ')) - . $this->_getReducedDqlQueryPart('offset', array('pre' => ' OFFSET ', 'separator' => ' ')); - } - - - /** - * Builds the DQL of SELECT - */ - protected function _getDqlForSelect() - { - /* - * BNF: - * - * SelectStatement = [SelectClause] FromClause [WhereClause] [GroupByClause] [HavingClause] [OrderByClause] [LimitClause] [OffsetClause] - * SelectClause = "SELECT" ["ALL" | "DISTINCT"] SelectExpression {"," SelectExpression} - * FromClause = "FROM" IdentificationVariableDeclaration {"," IdentificationVariableDeclaration} - * WhereClause = "WHERE" ConditionalExpression - * GroupByClause = "GROUP" "BY" GroupByItem {"," GroupByItem} - * HavingClause = "HAVING" ConditionalExpression - * OrderByClause = "ORDER" "BY" OrderByItem {"," OrderByItem} - * LimitClause = "LIMIT" integer - * OffsetClause = "OFFSET" integer - * - */ - /** - * @todo [TODO] What about "ALL" support? - */ - return 'SELECT' - . (($this->getDqlQueryPart('distinct') === true) ? ' DISTINCT' : '') - . $this->_getReducedDqlQueryPart('select', array('pre' => ' ', 'separator' => ', ', 'empty' => ' *')) - . $this->_getReducedDqlQueryPart('from', array('pre' => ' FROM ', 'separator' => ' ')) - . $this->_getReducedDqlQueryPart('where', array('pre' => ' WHERE ', 'separator' => ' ')) - . $this->_getReducedDqlQueryPart('groupby', array('pre' => ' GROUP BY ', 'separator' => ', ')) - . $this->_getReducedDqlQueryPart('having', array('pre' => ' HAVING ', 'separator' => ' ')) - . $this->_getReducedDqlQueryPart('orderby', array('pre' => ' ORDER BY ', 'separator' => ', ')) - . $this->_getReducedDqlQueryPart('limit', array('pre' => ' LIMIT ', 'separator' => ' ')) - . $this->_getReducedDqlQueryPart('offset', array('pre' => ' OFFSET ', 'separator' => ' ')); - } - - - /** - * @nodoc - */ - protected function _getReducedDqlQueryPart($queryPartName, $options = array()) - { - if (empty($this->_dqlParts[$queryPartName])) { - return (isset($options['empty']) ? $options['empty'] : ''); - } - - $str = (isset($options['pre']) ? $options['pre'] : ''); - $str .= implode($options['separator'], $this->getDqlQueryPart($queryPartName)); - $str .= (isset($options['post']) ? $options['post'] : ''); - - return $str; - } - - /** - * Returns the type of this query object - * By default the type is Doctrine_ORM_Query_Abstract::SELECT but if update() or delete() - * are being called the type is Doctrine_ORM_Query_Abstract::UPDATE and Doctrine_ORM_Query_Abstract::DELETE, - * respectively. - * - * @see Doctrine_ORM_Query_Abstract::SELECT - * @see Doctrine_ORM_Query_Abstract::UPDATE - * @see Doctrine_ORM_Query_Abstract::DELETE - * - * @return integer Return the query type - */ - public function getType() - { - return $this->_type; - } - - - /** - * Returns the state of this query object - * By default the type is Doctrine_ORM_Query_Abstract::STATE_CLEAN but if it appears any unprocessed DQL - * part, it is switched to Doctrine_ORM_Query_Abstract::STATE_DIRTY. - * - * @see Doctrine_ORM_Query_Abstract::STATE_CLEAN - * @see Doctrine_ORM_Query_Abstract::STATE_DIRTY - * - * @return integer Return the query state - */ - public function getState() - { - return $this->_state; - } - - - /** - * Adds fields to the SELECT part of the query - * - * @param string $select Query SELECT part - * @return Doctrine_ORM_Query - */ - public function select($select = '', $override = false) - { - if ($select === '') { - return $this; - } - - return $this->_addDqlQueryPart('select', $select, ! $override); - } - - - /** - * Makes the query SELECT DISTINCT. - * - * @param bool $flag Whether or not the SELECT is DISTINCT (default true). - * @return Doctrine_ORM_Query - */ - public function distinct($flag = true) - { - $this->_dqlParts['distinct'] = (bool) $flag; - return $this; - } - - - /** - * Makes the query SELECT FOR UPDATE. - * - * @param bool $flag Whether or not the SELECT is FOR UPDATE (default true). - * @return Doctrine_ORM_Query - * - * @todo [TODO] What about "forUpdate" support? Remove it? - */ - public function forUpdate($flag = true) - { - return $this->_addDqlQueryPart('forUpdate', (bool) $flag); - } - - - /** - * Sets the query type to DELETE - * - * @return Doctrine_ORM_Query - */ - public function delete() - { - $this->_type = self::DELETE; - return $this; - } - - - /** - * Sets the UPDATE part of the query - * - * @param string $update Query UPDATE part - * @return Doctrine_ORM_Query - */ - public function update($update) - { - $this->_type = self::UPDATE; - return $this->_addDqlQueryPart('from', $update); - } - - - /** - * Sets the SET part of the query - * - * @param mixed $key UPDATE keys. Accepts either a string (requiring then $value or $params to be defined) - * or an array of $key => $value pairs. - * @param string $value UPDATE key value. Optional argument, but required if $key is a string. - * @return Doctrine_ORM_Query - */ - public function set($key, $value = null, $params = null) - { - if (is_array($key)) { - foreach ($key as $k => $v) { - $this->set($k, '?', array($v)); - } - - return $this; - } else { - if ($params !== null) { - if (is_array($params)) { - $this->_params['set'] = array_merge($this->_params['set'], $params); - } else { - $this->_params['set'][] = $params; - } - } - - if ($value === null) { - throw \Doctrine\Common\DoctrineException::updateMe( 'Cannot try to set \''.$key.'\' without a value.' ); - } - - return $this->_addDqlQueryPart('set', $key . ' = ' . $value, true); - } - } - - /** - * Adds fields to the FROM part of the query - * - * @param string $from Query FROM part - * @return Doctrine_ORM_Query - */ - public function from($from, $override = false) - { - return $this->_addDqlQueryPart('from', $from, ! $override); - } - - - /** - * Appends an INNER JOIN to the FROM part of the query - * - * @param string $join Query INNER JOIN - * @param mixed $params Optional JOIN params (array of parameters or a simple scalar) - * @return Doctrine_ORM_Query - */ - public function innerJoin($join, $params = array()) - { - if (is_array($params)) { - $this->_params['join'] = array_merge($this->_params['join'], $params); - } else { - $this->_params['join'][] = $params; - } - - return $this->_addDqlQueryPart('from', 'INNER JOIN ' . $join, true); - } - - - /** - * Appends an INNER JOIN to the FROM part of the query - * - * @param string $join Query INNER JOIN - * @param mixed $params Optional JOIN params (array of parameters or a simple scalar) - * @return Doctrine_ORM_Query - */ - public function join($join, $params = array()) - { - return $this->innerJoin($join, $params); - } - - - /** - * Appends a LEFT JOIN to the FROM part of the query - * - * @param string $join Query LEFT JOIN - * @param mixed $params Optional JOIN params (array of parameters or a simple scalar) - * @return Doctrine_ORM_Query - */ - public function leftJoin($join, $params = array()) - { - if (is_array($params)) { - $this->_params['join'] = array_merge($this->_params['join'], $params); - } else { - $this->_params['join'][] = $params; - } - - return $this->_addDqlQueryPart('from', 'LEFT JOIN ' . $join, true); - } - - - /** - * Adds conditions to the WHERE part of the query - * - * @param string $where Query WHERE part - * @param mixed $params An array of parameters or a simple scalar - * @return Doctrine_ORM_Query - */ - public function where($where, $params = array(), $override = false) - { - if ($override) { - $this->_params['where'] = array(); - } - - if (is_array($params)) { - $this->_params['where'] = array_merge($this->_params['where'], $params); - } else { - $this->_params['where'][] = $params; - } - - return $this->_addDqlQueryPart('where', $where, ! $override); - } - - - /** - * Adds conditions to the WHERE part of the query - * - * @param string $where Query WHERE part - * @param mixed $params An array of parameters or a simple scalar - * @return Doctrine_ORM_Query - */ - public function andWhere($where, $params = array(), $override = false) - { - if (count($this->getDqlQueryPart('where')) > 0) { - $this->_addDqlQueryPart('where', 'AND', true); - } - - return $this->where($where, $params, $override); - } - - - /** - * Adds conditions to the WHERE part of the query - * - * @param string $where Query WHERE part - * @param mixed $params An array of parameters or a simple scalar - * @return Doctrine_ORM_Query - */ - public function orWhere($where, $params = array(), $override = false) - { - if (count($this->getDqlQueryPart('where')) > 0) { - $this->_addDqlQueryPart('where', 'OR', true); - } - - return $this->where($where, $params, $override); - } - - - /** - * Adds IN condition to the query WHERE part - * - * @param string $expr The operand of the IN - * @param mixed $params An array of parameters or a simple scalar - * @param boolean $not Whether or not to use NOT in front of IN - * @return Doctrine_ORM_Query - */ - public function whereIn($expr, $params = array(), $override = false, $not = false) - { - $params = (array) $params; - - // Must have at least one param, otherwise we'll get an empty IN () => invalid SQL - if ( ! count($params)) { - return $this; - } - - list($sqlPart, $params) = $this->_processWhereInParams($params); - - $where = $expr . ($not === true ? ' NOT' : '') . ' IN (' . $sqlPart . ')'; - - return $this->_returnWhereIn($where, $params, $override); - } - - - /** - * Adds NOT IN condition to the query WHERE part - * - * @param string $expr The operand of the NOT IN - * @param mixed $params An array of parameters or a simple scalar - * @return Doctrine_ORM_Query - */ - public function whereNotIn($expr, $params = array(), $override = false) - { - return $this->whereIn($expr, $params, $override, true); - } - - - /** - * Adds IN condition to the query WHERE part - * - * @param string $expr The operand of the IN - * @param mixed $params An array of parameters or a simple scalar - * @param boolean $not Whether or not to use NOT in front of IN - * @return Doctrine_ORM_Query - */ - public function andWhereIn($expr, $params = array(), $override = false) - { - if (count($this->getDqlQueryPart('where')) > 0) { - $this->_addDqlQueryPart('where', 'AND', true); - } - - return $this->whereIn($expr, $params, $override); - } - - - /** - * Adds NOT IN condition to the query WHERE part - * - * @param string $expr The operand of the NOT IN - * @param mixed $params An array of parameters or a simple scalar - * @return Doctrine_ORM_Query - */ - public function andWhereNotIn($expr, $params = array(), $override = false) - { - if (count($this->getDqlQueryPart('where')) > 0) { - $this->_addDqlQueryPart('where', 'AND', true); - } - - return $this->whereIn($expr, $params, $override, true); - } - - - /** - * Adds IN condition to the query WHERE part - * - * @param string $expr The operand of the IN - * @param mixed $params An array of parameters or a simple scalar - * @param boolean $not Whether or not to use NOT in front of IN - * @return Doctrine_ORM_Query - */ - public function orWhereIn($expr, $params = array(), $override = false) - { - if (count($this->getDqlQueryPart('where')) > 0) { - $this->_addDqlQueryPart('where', 'OR', true); - } - - return $this->whereIn($expr, $params, $override); - } - - - /** - * Adds NOT IN condition to the query WHERE part - * - * @param string $expr The operand of the NOT IN - * @param mixed $params An array of parameters or a simple scalar - * @return Doctrine_ORM_Query - */ - public function orWhereNotIn($expr, $params = array(), $override = false) - { - if (count($this->getDqlQueryPart('where')) > 0) { - $this->_addDqlQueryPart('where', 'OR', true); - } - - return $this->whereIn($expr, $params, $override, true); - } - - - /** - * Adds fields to the GROUP BY part of the query - * - * @param string $groupby Query GROUP BY part - * @return Doctrine_ORM_Query - */ - public function groupBy($groupby, $override = false) - { - return $this->_addDqlQueryPart('groupby', $groupby, ! $override); - } - - - /** - * Adds conditions to the HAVING part of the query - * - * @param string $having Query HAVING part - * @param mixed $params An array of parameters or a simple scalar - * @return Doctrine_ORM_Query - */ - public function having($having, $params = array(), $override = false) - { - if ($override) { - $this->_params['having'] = array(); - } - - if (is_array($params)) { - $this->_params['having'] = array_merge($this->_params['having'], $params); - } else { - $this->_params['having'][] = $params; - } - - return $this->_addDqlQueryPart('having', $having, true); - } - - - /** - * Adds conditions to the HAVING part of the query - * - * @param string $having Query HAVING part - * @param mixed $params An array of parameters or a simple scalar - * @return Doctrine_ORM_Query - */ - public function andHaving($having, $params = array(), $override = false) - { - if (count($this->getDqlQueryPart('having')) > 0) { - $this->_addDqlQueryPart('having', 'AND', true); - } - - return $this->having($having, $params, $override); - } - - - /** - * Adds conditions to the HAVING part of the query - * - * @param string $having Query HAVING part - * @param mixed $params An array of parameters or a simple scalar - * @return Doctrine_ORM_Query - */ - public function orHaving($having, $params = array(), $override = false) - { - if (count($this->getDqlQueryPart('having')) > 0) { - $this->_addDqlQueryPart('having', 'OR', true); - } - - return $this->having($having, $params, $override); - } - - - /** - * Adds fields to the ORDER BY part of the query - * - * @param string $orderby Query ORDER BY part - * @return Doctrine_ORM_Query - */ - public function orderBy($orderby, $override = false) - { - return $this->_addDqlQueryPart('orderby', $orderby, ! $override); - } - - - /** - * Sets the Query query limit - * - * @param integer $limit Limit to be used for limiting the query results - * @return Doctrine_ORM_Query - */ - public function limit($limit) - { - return $this->_addDqlQueryPart('limit', $limit); - } - - - /** - * Sets the Query query offset - * - * @param integer $offset Offset to be used for paginating the query - * @return Doctrine_ORM_Query - */ - public function offset($offset) - { - return $this->_addDqlQueryPart('offset', $offset); - } - - /** * Set enumerated parameters * @@ -813,7 +153,6 @@ abstract class AbstractQuery $this->_enumParams = $enumParams; } - /** * Get all enumerated parameters * @@ -824,7 +163,6 @@ abstract class AbstractQuery return $this->_enumParams; } - /** * Convert ENUM parameters to their integer equivalents * @@ -842,7 +180,6 @@ abstract class AbstractQuery return $params; } - /** * Get all defined parameters * @@ -850,16 +187,9 @@ abstract class AbstractQuery */ public function getParams($params = array()) { - return array_merge( - $this->_params['join'], - $this->_params['set'], - $this->_params['where'], - $this->_params['having'], - $params - ); + return array_merge($this->_params, $params); } - /** * setParams * @@ -869,116 +199,6 @@ abstract class AbstractQuery $this->_params = $params; } - - /** - * Method to check if a arbitrary piece of DQL exists - * - * @param string $dql Arbitrary piece of DQL to check for - * @return boolean - */ - public function contains($dql) - { - return stripos($this->getDql(), $dql) === false ? false : true; - } - - - /** - * Retrieve a DQL part for internal purposes - * - * @param string $queryPartName The name of the query part. - * @return mixed Array related to query part or simple scalar - */ - public function getDqlQueryPart($queryPartName) - { - if ( ! isset($this->_dqlParts[$queryPartName])) { - throw \Doctrine\Common\DoctrineException::updateMe('Unknown DQL query part \'' . $queryPartName . '\''); - } - - return $this->_dqlParts[$queryPartName]; - } - - - /** - * Adds a DQL part to the internal parts collection. - * - * @param string $queryPartName The name of the query part. - * @param string $queryPart The actual query part to add. - * @param boolean $append Whether to append $queryPart to already existing - * parts under the same $queryPartName. Defaults to FALSE - * (previously added parts with the same name get overridden). - * @return Doctrine_ORM_Query - */ - protected function _addDqlQueryPart($queryPartName, $queryPart, $append = false) - { - if ($append) { - $this->_dqlParts[$queryPartName][] = $queryPart; - } else { - $this->_dqlParts[$queryPartName] = array($queryPart); - } - - $this->_state = Doctrine_ORM_Query::STATE_DIRTY; - return $this; - } - - - /** - * Processes the WHERE IN () parameters and return an indexed array containing - * the sqlPart to be placed in SQL statement and the new parameters (that will be - * bound in SQL execution) - * - * @param array $params Parameters to be processed - * @return array - */ - protected function _processWhereInParams($params = array()) - { - return array( - // [0] => sqlPart - implode(', ', array_map(array(&$this, '_processWhereInSqlPart'), $params)), - // [1] => params - array_filter($params, array(&$this, '_processWhereInParamItem')), - ); - } - - - /** - * @nodoc - */ - protected function _processWhereInSqlPart($value) - { - // [TODO] Add support to imbricated query (must deliver the hardest effort to Parser) - return ($value instanceof Doctrine_Expression) ? $value->getSql() : '?'; - } - - - /** - * @nodoc - */ - protected function _processWhereInParamItem($value) - { - // [TODO] Add support to imbricated query (must deliver the hardest effort to Parser) - return ( ! ($value instanceof Doctrine_Expression)); - } - - - /** - * Processes a WHERE IN () and build defined stuff to add in DQL - * - * @param string $where The WHERE clause to be added - * @param array $params WHERE clause parameters - * @param mixed $appender Where this clause may be not be appended, or appended - * (two possible values: AND or OR) - * @return Doctrine_ORM_Query - */ - protected function _returnWhereIn($where, $params = array(), $override = false) - { - // Parameters inclusion - $this->_params['where'] = $override ? $params : array_merge($this->_params['where'], $params); - - // WHERE clause definition - return $this->_addDqlQueryPart('where', $where, ! $override); - } - - /** * Gets the SQL query that corresponds to this query object. * The returned SQL syntax depends on the connection driver that is used @@ -1011,4 +231,302 @@ abstract class AbstractQuery } } + /** + * Sets the ResultSetMapping that should be used for hydration. + * + * @param ResultSetMapping $rsm + */ + public function setResultSetMapping($rsm) + { + $this->_resultSetMapping = $rsm; + } + + /** + * Defines a cache driver to be used for caching result sets. + * + * @param Doctrine\ORM\Cache\Cache $driver Cache driver + * @return Doctrine\ORM\Query + */ + public function setResultCache($resultCache = null) + { + if ($resultCache !== null && ! ($resultCache instanceof \Doctrine\ORM\Cache\Cache)) { + throw DoctrineException::updateMe( + 'Method setResultCache() accepts only an instance of Doctrine_Cache_Interface or null.' + ); + } + $this->_resultCache = $resultCache; + return $this; + } + + /** + * Returns the cache driver used for caching result sets. + * + * @return Doctrine_Cache_Interface Cache driver + */ + public function getResultCache() + { + if ($this->_resultCache instanceof \Doctrine\ORM\Cache\Cache) { + return $this->_resultCache; + } else { + return $this->_em->getConfiguration()->getResultCacheImpl(); + } + } + + /** + * Defines how long the result cache will be active before expire. + * + * @param integer $timeToLive How long the cache entry is valid + * @return Doctrine\ORM\Query + */ + public function setResultCacheLifetime($timeToLive) + { + if ($timeToLive !== null) { + $timeToLive = (int) $timeToLive; + } + + $this->_resultCacheTTL = $timeToLive; + + return $this; + } + + /** + * Retrieves the lifetime of resultset cache. + * + * @return int + */ + public function getResultCacheLifetime() + { + return $this->_resultCacheTTL; + } + + /** + * Defines if the resultset cache is active or not. + * + * @param boolean $expire Whether or not to force resultset cache expiration. + * @return Doctrine_ORM_Query + */ + public function setExpireResultCache($expire = true) + { + $this->_expireResultCache = (bool) $expire; + + return $this; + } + + /** + * Retrieves if the resultset cache is active or not. + * + * @return bool + */ + public function getExpireResultCache() + { + return $this->_expireResultCache; + } + + /** + * Defines the processing mode to be used during hydration process. + * + * @param integer $hydrationMode Doctrine processing mode to be used during hydration process. + * One of the Query::HYDRATE_* constants. + * @return Doctrine\ORM\Query + */ + public function setHydrationMode($hydrationMode) + { + $this->_hydrationMode = $hydrationMode; + return $this; + } + + /** + * Gets the hydration mode currently used by the query. + * + * @return integer + */ + public function getHydrationMode() + { + return $this->_hydrationMode; + } + + /** + * Gets the list of results for the query. + * + * Alias for execute(array(), HYDRATE_OBJECT). + * + * @return Collection + */ + public function getResultList() + { + return $this->execute(array(), self::HYDRATE_OBJECT); + } + + /** + * Gets the array of results for the query. + * + * Alias for execute(array(), HYDRATE_ARRAY). + * + * @return array + */ + public function getResultArray() + { + return $this->execute(array(), self::HYDRATE_ARRAY); + } + + /** + * Gets the scalar results for the query. + * + * Alias for execute(array(), HYDRATE_SCALAR). + * + * @return array + */ + public function getScalarResult() + { + return $this->execute(array(), self::HYDRATE_SCALAR); + } + + /** + * Gets the single result of the query. + * Enforces the uniqueness of the result. If the result is not unique, + * a QueryException is thrown. + * + * @param integer $hydrationMode + * @return mixed + * @throws QueryException If the query result is not unique. + */ + public function getSingleResult($hydrationMode = null) + { + $result = $this->execute(array(), $hydrationMode); + if (is_array($result)) { + if (count($result) > 1) { + throw QueryException::nonUniqueResult(); + } + return array_shift($result); + } else if (is_object($result)) { + if (count($result) > 1) { + throw QueryException::nonUniqueResult(); + } + return $result->getFirst(); + } + return $result; + } + + /** + * Gets the single scalar result of the query. + * + * Alias for getSingleResult(HYDRATE_SINGLE_SCALAR). + * + * @return mixed + * @throws QueryException If the query result is not unique. + */ + public function getSingleScalarResult() + { + return $this->getSingleResult(self::HYDRATE_SINGLE_SCALAR); + } + + /** + * Sets an implementation-specific hint. If the hint name is not recognized, + * it is silently ignored. + * + * @param string $name The name of the hint. + * @param mixed $value The value of the hint. + */ + public function setHint($name, $value) + { + $this->_hints[$name] = $value; + } + + /** + * Gets an implementation-specific hint. If the hint name is not recognized, + * FALSE is returned. + * + * @param string $name The name of the hint. + * @return mixed The value of the hint or FALSe, if the hint name is not recognized. + */ + public function getHint($name) + { + return isset($this->_hints[$name]) ? $this->_hints[$name] : false; + } + + /** + * Executes the query and returns an IterableResult that can be used to incrementally + * iterated over the result. + * + * @param array $params The query parameters. + * @param integer $hydrationMode The hydratio mode to use. + * @return IterableResult + */ + public function iterate(array $params = array(), $hydrationMode = self::HYDRATE_OBJECT) + { + return $this->_em->getHydrator($this->_hydrationMode)->iterate( + $this->_execute($params, $hydrationMode), $this->_resultSetMapping + ); + } + + /** + * Executes the query. + * + * @param string $params Parameters to be sent to query. + * @param integer $hydrationMode Doctrine processing mode to be used during hydration process. + * One of the Query::HYDRATE_* constants. + * @return mixed + */ + public function execute($params = array(), $hydrationMode = null) + { + if ($this->_em->getUnitOfWork()->hasPendingInsertions()) { + $this->_em->flush(); + } + + if ($hydrationMode !== null) { + $this->_hydrationMode = $hydrationMode; + } + + $params = $this->getParams($params); + + // Check result cache (Only for SELECT queries) + if ($this->_resultCache && $this->_type === self::SELECT) { + $cacheDriver = $this->getResultCacheDriver(); + + // Calculate hash for DQL query. + $hash = md5($this->getDql() . var_export($params, true)); + $cached = ($this->_expireResultCache) ? false : $cacheDriver->fetch($hash); + + if ($cached === false) { + // Cache miss. + $result = $this->_doExecute($params); + $queryResult = CacheHandler::fromResultSet($this, $result); + $cacheDriver->save($hash, $queryResult->toCachedForm(), $this->_resultCacheTTL); + + return $result; + } else { + // Cache hit. + $queryResult = CacheHandler::fromCachedResult($this, $cached); + + return $queryResult->getResultSet(); + } + } + + $stmt = $this->_doExecute($params); + + if (is_integer($stmt)) { + return $stmt; + } + + return $this->_em->getHydrator($this->_hydrationMode)->hydrateAll($stmt, $this->_resultSetMapping); + } + + /** + * @nodoc + */ + protected function _prepareParams(array $params) + { + // Convert boolean params + $params = $this->_em->getConnection()->getDatabasePlatform()->convertBooleans($params); + + // Convert enum params + return $this->convertEnums($params); + } + + /** + * Executes the query and returns a reference to the resulting Statement object. + * + * @param $params + */ + abstract protected function _doExecute(array $params); } diff --git a/lib/Doctrine/ORM/EntityManager.php b/lib/Doctrine/ORM/EntityManager.php index cfce0eef1..2e58792b1 100644 --- a/lib/Doctrine/ORM/EntityManager.php +++ b/lib/Doctrine/ORM/EntityManager.php @@ -223,7 +223,7 @@ class EntityManager } /** - * Creates a query with the specified name. + * Creates a DQL query with the specified name. * * @todo Implementation. * @throws DoctrineException If there is no query registered with the given name. @@ -234,11 +234,17 @@ class EntityManager } /** - * @todo Implementation. + * Creates a native SQL query. + * + * @param string $sql + * @return Query */ - public function createNativeQuery($sql = "") + public function createNativeQuery($sql, \Doctrine\ORM\Query\ResultSetMapping $rsm) { - //... + $query = new NativeQuery($this); + $query->setSql($sql); + $query->setResultSetMapping($rsm); + return $query; } /** diff --git a/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php b/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php index 272ef3a2a..55fa31206 100644 --- a/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php +++ b/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php @@ -24,7 +24,8 @@ namespace Doctrine\ORM\Internal\Hydration; use \PDO; /** - * Base class for all hydrators (ok, we got only 1 currently). + * Base class for all hydrators. A hydrator is a class that provides some form + * of transformation of an SQL result set into another structure. * * @license http://www.opensource.org/licenses/lgpl-license.php LGPL * @link www.doctrine-project.org @@ -50,11 +51,8 @@ abstract class AbstractHydrator /** @var Statement The statement that provides the data to hydrate. */ protected $_stmt; - /** @var object The ParserResult instance that holds the necessary information for hydration. */ - protected $_parserResult; - /** - * Initializes a new instance of a class derived from AbstractHydrator. + * Initializes a new instance of a class derived from AbstractHydrator. * * @param Doctrine\ORM\EntityManager $em The EntityManager to use. */ @@ -68,13 +66,14 @@ abstract class AbstractHydrator * Initiates a row-by-row hydration. * * @param object $stmt - * @param object $parserResult + * @param object $resultSetMapping * @return IterableResult */ - public function iterate($stmt, $parserResult) + public function iterate($stmt, $resultSetMapping) { $this->_stmt = $stmt; - $this->_prepare($parserResult); + $this->_resultSetMapping = $resultSetMapping; + $this->_prepare(); return new IterableResult($this); } @@ -82,13 +81,14 @@ abstract class AbstractHydrator * Hydrates all rows returned by the passed statement instance at once. * * @param object $stmt - * @param object $parserResult + * @param object $resultSetMapping * @return mixed */ - public function hydrateAll($stmt, $parserResult) + public function hydrateAll($stmt, $resultSetMapping) { $this->_stmt = $stmt; - $this->_prepare($parserResult); + $this->_resultSetMapping = $resultSetMapping; + $this->_prepare(); $result = $this->_hydrateAll(); $this->_cleanup(); return $result; @@ -115,14 +115,9 @@ abstract class AbstractHydrator /** * Excutes one-time preparation tasks once each time hydration is started * through {@link hydrateAll} or {@link iterate()}. - * - * @param object $parserResult */ - protected function _prepare($parserResult) - { - $this->_resultSetMapping = $parserResult->getResultSetMapping(); - $this->_parserResult = $parserResult; - } + protected function _prepare() + {} /** * Excutes one-time cleanup tasks at the end of a hydration that was initiated @@ -131,7 +126,6 @@ abstract class AbstractHydrator protected function _cleanup() { $this->_resultSetMapping = null; - $this->_parserResult = null; $this->_stmt->closeCursor(); $this->_stmt = null; } @@ -152,8 +146,6 @@ abstract class AbstractHydrator /** * Hydrates all rows from the current statement instance at once. - * - * @param object $parserResult */ abstract protected function _hydrateAll(); @@ -180,21 +172,26 @@ abstract class AbstractHydrator foreach ($data as $key => $value) { // Parse each column name only once. Cache the results. if ( ! isset($cache[$key])) { - if ($this->_isIgnoredName($key)) continue; - - if ($this->_resultSetMapping->isScalarResult($key)) { + if ($this->_resultSetMapping->isIgnoredColumn($key)) { + $cache[$key] = false; + } else if ($this->_resultSetMapping->isScalarResult($key)) { $cache[$key]['fieldName'] = $this->_resultSetMapping->getScalarAlias($key); $cache[$key]['isScalar'] = true; - } else { + } else if ($this->_resultSetMapping->isFieldResult($key)) { $classMetadata = $this->_resultSetMapping->getOwningClass($key); $fieldName = $this->_resultSetMapping->getFieldName($key); $classMetadata = $this->_lookupDeclaringClass($classMetadata, $fieldName); - //$fieldName = $classMetadata->getFieldNameForLowerColumnName($columnName); $cache[$key]['fieldName'] = $fieldName; $cache[$key]['isScalar'] = false; $cache[$key]['type'] = $classMetadata->getTypeOfField($fieldName); $cache[$key]['isIdentifier'] = $classMetadata->isIdentifier($fieldName); $cache[$key]['dqlAlias'] = $this->_resultSetMapping->getEntityAlias($key); + } else { + // Discriminator column + $cache[$key]['isDiscriminator'] = true; + $cache[$key]['isScalar'] = false; + $cache[$key]['fieldName'] = $key; + $cache[$key]['dqlAlias'] = $this->_resultSetMapping->getEntityAlias($key); } } @@ -207,6 +204,11 @@ abstract class AbstractHydrator $dqlAlias = $cache[$key]['dqlAlias']; + if (isset($cache[$key]['isDiscriminator'])) { + $rowData[$dqlAlias][$fieldName] = $value; + continue; + } + if ($cache[$key]['isIdentifier']) { $id[$dqlAlias] .= '|' . $value; } @@ -243,9 +245,10 @@ abstract class AbstractHydrator foreach ($data as $key => $value) { // Parse each column name only once. Cache the results. if ( ! isset($cache[$key])) { - if ($this->_isIgnoredName($key)) continue; - - if ($this->_resultSetMapping->isScalarResult($key)) { + if ($this->_resultSetMapping->isIgnoredColumn($key)) { + $cache[$key] = false; + continue; + } else if ($this->_resultSetMapping->isScalarResult($key)) { $cache[$key]['fieldName'] = $this->_resultSetMapping->getScalarAlias($key); $cache[$key]['isScalar'] = true; } else { @@ -285,19 +288,6 @@ abstract class AbstractHydrator $this->_resultSetMapping->getIndexByField($alias) : null; } - /** - * Checks whether a name is ignored. Used during result set parsing to skip - * certain elements in the result set that do not have any meaning for the result. - * (I.e. ORACLE limit/offset emulation adds doctrine_rownum to the result set). - * - * @param string $name - * @return boolean - */ - private function _isIgnoredName($name) - { - return $name == 'doctrine_rownum'; - } - /** * Looks up the field name for a (lowercased) column name. * @@ -319,14 +309,12 @@ abstract class AbstractHydrator private function _lookupDeclaringClass($class, $fieldName) { if ($class->hasField($fieldName)) { - //return $class->getFieldNameForLowerColumnName($lcColumnName); return $class; } foreach ($class->getSubclasses() as $subClass) { $subClassMetadata = $this->_em->getClassMetadata($subClass); if ($subClassMetadata->hasField($fieldName)) { - //return $subClassMetadata->getFieldNameForLowerColumnName($lcColumnName); return $subClassMetadata; } } diff --git a/lib/Doctrine/ORM/Internal/Hydration/ArrayHydrator.php b/lib/Doctrine/ORM/Internal/Hydration/ArrayHydrator.php index 2d2a655dc..19f5fef7a 100644 --- a/lib/Doctrine/ORM/Internal/Hydration/ArrayHydrator.php +++ b/lib/Doctrine/ORM/Internal/Hydration/ArrayHydrator.php @@ -38,9 +38,8 @@ class ArrayHydrator extends AbstractHydrator private $_resultCounter = 0; /** @override */ - protected function _prepare($parserResult) + protected function _prepare() { - parent::_prepare($parserResult); $this->_isSimpleQuery = $this->_resultSetMapping->getEntityResultCount() <= 1; $this->_identifierMap = array(); $this->_resultPointers = array(); @@ -98,7 +97,7 @@ class ArrayHydrator extends AbstractHydrator // Get a reference to the right element in the result tree. // This element will get the associated element attached. - if ($this->_parserResult->isMixedQuery() && isset($this->_rootAliases[$parent])) { + if ($this->_resultSetMapping->isMixedResult() && isset($this->_rootAliases[$parent])) { $key = key(reset($this->_resultPointers)); // TODO: Exception if $key === null ? $baseElement =& $this->_resultPointers[$parent][$key]; @@ -155,14 +154,14 @@ class ArrayHydrator extends AbstractHydrator if ($this->_isSimpleQuery || ! isset($this->_identifierMap[$dqlAlias][$id[$dqlAlias]])) { $element = $rowData[$dqlAlias]; if ($field = $this->_getCustomIndexField($dqlAlias)) { - if ($this->_parserResult->isMixedQuery()) { + if ($this->_resultSetMapping->isMixedResult()) { $result[] = array($element[$field] => $element); ++$this->_resultCounter; } else { $result[$element[$field]] = $element; } } else { - if ($this->_parserResult->isMixedQuery()) { + if ($this->_resultSetMapping->isMixedResult()) { $result[] = array($element); ++$this->_resultCounter; } else { diff --git a/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php b/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php index 72e71f0cf..2826dec92 100644 --- a/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php +++ b/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php @@ -33,22 +33,24 @@ use Doctrine\Common\Collections\Collection; */ class ObjectHydrator extends AbstractHydrator { + /* TOOD: Consider unifying _collections and _initializedRelations */ /** Collections initialized by the hydrator */ private $_collections = array(); /** Memory for initialized relations */ private $_initializedRelations = array(); - private $_metadataMap = array(); + + private $_classMetadatas = array(); private $_rootAliases = array(); private $_isSimpleQuery = false; private $_identifierMap = array(); private $_resultPointers = array(); private $_idTemplate = array(); private $_resultCounter = 0; + private $_discriminatorMap = array(); /** @override */ - protected function _prepare($parserResult) + protected function _prepare() { - parent::_prepare($parserResult); $this->_isSimpleQuery = $this->_resultSetMapping->getEntityResultCount() <= 1; $this->_identifierMap = array(); $this->_resultPointers = array(); @@ -58,6 +60,14 @@ class ObjectHydrator extends AbstractHydrator $this->_identifierMap[$dqlAlias] = array(); $this->_resultPointers[$dqlAlias] = array(); $this->_idTemplate[$dqlAlias] = ''; + $this->_classMetadatas[$class->getClassName()] = $class; + if ($class->isInheritanceTypeSingleTable() || $class->isInheritanceTypeJoined()) { + $this->_discriminatorMap[$class->getClassName()][$class->getDiscriminatorValue()] = $class->getClassName(); + foreach (array_merge($class->getParentClasses(), $class->getSubclasses()) as $className) { + $value = $this->_em->getClassMetadata($className)->getDiscriminatorValue(); + $this->_discriminatorMap[$class->getClassName()][$value] = $className; + } + } } } @@ -70,7 +80,7 @@ class ObjectHydrator extends AbstractHydrator { $s = microtime(true); - if ($this->_parserResult->isMixedQuery()) { + if ($this->_resultSetMapping->isMixedResult()) { $result = array(); } else { $result = new Collection; @@ -90,7 +100,7 @@ class ObjectHydrator extends AbstractHydrator // Clean up $this->_collections = array(); $this->_initializedRelations = array(); - $this->_metadataMap = array(); + $this->_classMetadatas = array(); $e = microtime(true); echo 'Hydration took: ' . ($e - $s) . PHP_EOL; @@ -108,7 +118,7 @@ class ObjectHydrator extends AbstractHydrator * @param string $dqlAlias * @param boolean $oneToOne Whether it is a single-valued association or not. */ - private function updateResultPointer(&$coll, $index, $dqlAlias, $oneToOne) + private function updateResultPointer(&$coll, $index, $dqlAlias/*, $oneToOne*/) { if ($coll === null) { unset($this->_resultPointers[$dqlAlias]); // Ticket #1228 @@ -132,6 +142,12 @@ class ObjectHydrator extends AbstractHydrator } } + /** + * + * @param $component + * @return + * @todo Consider inlining this method. + */ private function getCollection($component) { $coll = new PersistentCollection($this->_em, $component); @@ -139,10 +155,16 @@ class ObjectHydrator extends AbstractHydrator return $coll; } + /** + * + * @param $entity + * @param $name + * @todo Consider inlining this method. + */ private function initRelatedCollection($entity, $name) { $oid = spl_object_hash($entity); - $classMetadata = $this->_metadataMap[$oid]; + $classMetadata = $this->_classMetadatas[get_class($entity)]; if ( ! isset($this->_initializedRelations[$oid][$name])) { $relation = $classMetadata->getAssociationMapping($name); $relatedClass = $this->_em->getClassMetadata($relation->getTargetEntityName()); @@ -157,7 +179,7 @@ class ObjectHydrator extends AbstractHydrator private function isIndexKeyInUse($entity, $assocField, $indexField) { - return $this->_metadataMap[spl_object_hash($entity)] + return $this->_classMetadatas[get_class($entity)] ->getReflectionProperty($assocField) ->getValue($entity) ->containsKey($indexField); @@ -178,9 +200,11 @@ class ObjectHydrator extends AbstractHydrator private function getEntity(array $data, $className) { + if ($discrColumn = $this->_resultSetMapping->getDiscriminatorColumn($className)) { + $className = $this->_discriminatorMap[$className][$data[$discrColumn]]; + unset($data[$discrColumn]); + } $entity = $this->_uow->createEntity($className, $data); - $oid = spl_object_hash($entity); - $this->_metadataMap[$oid] = $this->_em->getClassMetadata($className); return $entity; } @@ -191,11 +215,13 @@ class ObjectHydrator extends AbstractHydrator * @param $property * @param $entity2 * @param $indexField + * @todo Consider inlining this method. It's called only once and inlining can + * remove the need for the 2 get_class calls. */ private function addRelatedIndexedEntity($entity1, $property, $entity2, $indexField) { - $classMetadata1 = $this->_metadataMap[spl_object_hash($entity1)]; - $classMetadata2 = $this->_metadataMap[spl_object_hash($entity2)]; + $classMetadata1 = $this->_classMetadatas[get_class($entity1)]; + $classMetadata2 = $this->_classMetadatas[get_class($entity2)]; $indexValue = $classMetadata2->getReflectionProperty($indexField)->getValue($entity2); $classMetadata1->getReflectionProperty($property)->getValue($entity1)->set($indexValue, $entity2); } @@ -209,8 +235,10 @@ class ObjectHydrator extends AbstractHydrator */ private function addRelatedEntity($entity1, $property, $entity2) { - $classMetadata1 = $this->_metadataMap[spl_object_hash($entity1)]; - $classMetadata1->getReflectionProperty($property)->getValue($entity1)->add($entity2); + $this->_classMetadatas[get_class($entity1)] + ->getReflectionProperty($property) + ->getValue($entity1) + ->add($entity2); } /** @@ -222,7 +250,7 @@ class ObjectHydrator extends AbstractHydrator */ private function isFieldSet($entity, $field) { - return $this->_metadataMap[spl_object_hash($entity)] + return $this->_classMetadatas[get_class($entity)] ->getReflectionProperty($field) ->getValue($entity) !== null; } @@ -237,12 +265,13 @@ class ObjectHydrator extends AbstractHydrator private function setRelatedElement($entity1, $property, $entity2) { $oid = spl_object_hash($entity1); - $classMetadata1 = $this->_metadataMap[$oid]; + $classMetadata1 = $this->_classMetadatas[get_class($entity1)]; $classMetadata1->getReflectionProperty($property)->setValue($entity1, $entity2); $this->_uow->setOriginalEntityProperty($oid, $property, $entity2); $relation = $classMetadata1->getAssociationMapping($property); if ($relation->isOneToOne()) { - $targetClass = $this->_em->getClassMetadata($relation->getTargetEntityName()); + //$targetClass = $this->_em->getClassMetadata($relation->getTargetEntityName()); + $targetClass = $this->_classMetadatas[$relation->getTargetEntityName()]; if ($relation->isOwningSide()) { // If there is an inverse mapping on the target class its bidirectional if ($targetClass->hasInverseAssociationMapping($property)) { @@ -291,7 +320,7 @@ class ObjectHydrator extends AbstractHydrator // Get a reference to the right element in the result tree. // This element will get the associated element attached. - if ($this->_parserResult->isMixedQuery() && isset($this->_rootAliases[$parent])) { + if ($this->_resultSetMapping->isMixedResult() && isset($this->_rootAliases[$parent])) { $key = key(reset($this->_resultPointers)); // TODO: Exception if $key === null ? $baseElement =& $this->_resultPointers[$parent][$key]; @@ -302,11 +331,11 @@ class ObjectHydrator extends AbstractHydrator continue; } - $oid = spl_object_hash($baseElement); + $parentClass = get_class($baseElement); // Check the type of the relation (many or single-valued) if ( ! $relation->isOneToOne()) { - $oneToOne = false; + //$oneToOne = false; if (isset($nonemptyComponents[$dqlAlias])) { $this->initRelatedCollection($baseElement, $relationAlias); $indexExists = isset($this->_identifierMap[$path][$id[$parent]][$id[$dqlAlias]]); @@ -320,9 +349,10 @@ class ObjectHydrator extends AbstractHydrator $this->addRelatedEntity($baseElement, $relationAlias, $element); } $this->_identifierMap[$path][$id[$parent]][$id[$dqlAlias]] = $this->getLastKey( - $this->_metadataMap[$oid] + $this->_classMetadatas[$parentClass] ->getReflectionProperty($relationAlias) - ->getValue($baseElement)); + ->getValue($baseElement) + ); } } else if ( ! $this->isFieldSet($baseElement, $relationAlias)) { $coll = new PersistentCollection($this->_em, $entityName); @@ -330,7 +360,7 @@ class ObjectHydrator extends AbstractHydrator $this->setRelatedElement($baseElement, $relationAlias, $coll); } } else { - $oneToOne = true; + //$oneToOne = true; if ( ! isset($nonemptyComponents[$dqlAlias]) && ! $this->isFieldSet($baseElement, $relationAlias)) { $this->setRelatedElement($baseElement, $relationAlias, null); @@ -340,12 +370,12 @@ class ObjectHydrator extends AbstractHydrator } } - $coll = $this->_metadataMap[$oid] + $coll = $this->_classMetadatas[$parentClass] ->getReflectionProperty($relationAlias) ->getValue($baseElement); if ($coll !== null) { - $this->updateResultPointer($coll, $index, $dqlAlias, $oneToOne); + $this->updateResultPointer($coll, $index, $dqlAlias/*, $oneToOne*/); } } else { // Its a root result element @@ -353,24 +383,22 @@ class ObjectHydrator extends AbstractHydrator $this->_rootAliases[$dqlAlias] = true; // Mark as root alias if ($this->_isSimpleQuery || ! isset($this->_identifierMap[$dqlAlias][$id[$dqlAlias]])) { - $element = $this->_uow->createEntity($entityName, $rowData[$dqlAlias]); - $oid = spl_object_hash($element); - $this->_metadataMap[$oid] = $this->_em->getClassMetadata($entityName); + $element = $this->getEntity($rowData[$dqlAlias], $entityName); if ($field = $this->_getCustomIndexField($dqlAlias)) { - if ($this->_parserResult->isMixedQuery()) { + if ($this->_resultSetMapping->isMixedResult()) { $result[] = array( - $this->_metadataMap[$oid] + $this->_classMetadatas[$entityName] ->getReflectionProperty($field) ->getValue($element) => $element ); ++$this->_resultCounter; } else { - $result->set($element, $this->_metadataMap[$oid] + $result->set($element, $this->_classMetadatas[$entityName] ->getReflectionProperty($field) ->getValue($element)); } } else { - if ($this->_parserResult->isMixedQuery()) { + if ($this->_resultSetMapping->isMixedResult()) { $result[] = array($element); ++$this->_resultCounter; } else { @@ -381,7 +409,7 @@ class ObjectHydrator extends AbstractHydrator } else { $index = $this->_identifierMap[$dqlAlias][$id[$dqlAlias]]; } - $this->updateResultPointer($result, $index, $dqlAlias, false); + $this->updateResultPointer($result, $index, $dqlAlias/*, false*/); //unset($rowData[$dqlAlias]); } } diff --git a/lib/Doctrine/ORM/Internal/Hydration/ScalarHydrator.php b/lib/Doctrine/ORM/Internal/Hydration/ScalarHydrator.php index a8a271492..4c8953346 100644 --- a/lib/Doctrine/ORM/Internal/Hydration/ScalarHydrator.php +++ b/lib/Doctrine/ORM/Internal/Hydration/ScalarHydrator.php @@ -1,4 +1,23 @@ . + */ namespace Doctrine\ORM\Internal\Hydration; @@ -9,7 +28,7 @@ use \PDO; * The created result is almost the same as a regular SQL result set, except * that column names are mapped to field names and data type conversions. * - * @author robo + * @author Roman Borschel * @since 2.0 */ class ScalarHydrator extends AbstractHydrator diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadata.php b/lib/Doctrine/ORM/Mapping/ClassMetadata.php index 8246397a5..4f4ee006c 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadata.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadata.php @@ -273,7 +273,8 @@ final class ClassMetadata * @var array * @see _discriminatorColumn */ - private $_discriminatorMap = array(); + //private $_discriminatorMap = array(); + private $_discriminatorValue; /** * The definition of the descriminator column used in JOINED and SINGLE_TABLE @@ -825,6 +826,20 @@ final class ClassMetadata { return isset($this->_columnNames[$fieldName]); } + + public function setValue($entity, $field, $value) + { + if (isset($this->_reflectionProperties[$field])) { + $this->_reflectionProperties[$field]->setValue($entity, $value); + } + } + + public function setValueIfChanged($entity, $field, $value) + { + if (isset($this->_reflectionProperties[$field])) { + $this->_reflectionProperties[$field]->setValue($entity, $value); + } + } /** * Gets all field mappings. @@ -1500,25 +1515,25 @@ final class ClassMetadata } /** - * Sets the dsicriminator map used for mapping discriminator values to class names. + * Sets the dsicriminator value used by this class. * Used for JOINED and SINGLE_TABLE inheritance mapping strategies. * * @param array $map */ - public function setDiscriminatorMap(array $map) + public function setDiscriminatorValue($value) { - $this->_discriminatorMap = $map; + $this->_discriminatorValue = $value; } /** - * Gets the discriminator map that maps discriminator values to class names. + * Gets the discriminator value of this class. * Used for JOINED and SINGLE_TABLE inheritance mapping strategies. * * @return array */ - public function getDiscriminatorMap() + public function getDiscriminatorValue() { - return $this->_discriminatorMap; + return $this->_discriminatorValue; } /** diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php b/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php index d807ff1ed..55de5d03c 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php @@ -126,7 +126,7 @@ class ClassMetadataFactory $class = $this->_newClassMetadataInstance($className); if ($parent) { $class->setInheritanceType($parent->getInheritanceType()); - $class->setDiscriminatorMap($parent->getDiscriminatorMap()); + //$class->setDiscriminatorMap($parent->getDiscriminatorMap()); $class->setDiscriminatorColumn($parent->getDiscriminatorColumn()); $class->setIdGeneratorType($parent->getIdGeneratorType()); $this->_addInheritedFields($class, $parent); diff --git a/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php b/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php index 7af2d46bd..e2c46ddc6 100644 --- a/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php +++ b/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php @@ -75,11 +75,16 @@ class AnnotationDriver 'length' => $discrColumnAnnot->length )); } - +/* // Evaluate DoctrineDiscriminatorMap annotation if ($discrMapAnnot = $annotClass->getAnnotation('DoctrineDiscriminatorMap')) { $metadata->setDiscriminatorMap((array)$discrMapAnnot->value); } +*/ + // Evaluate DoctrineDiscriminatorMap annotation + if ($discrValueAnnot = $annotClass->getAnnotation('DoctrineDiscriminatorValue')) { + $metadata->setDiscriminatorValue($discrValueAnnot->value); + } // Evaluate DoctrineSubClasses annotation if ($subClassesAnnot = $annotClass->getAnnotation('DoctrineSubClasses')) { diff --git a/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php b/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php index b3ade22ab..cbc88070a 100644 --- a/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php +++ b/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php @@ -31,6 +31,7 @@ final class DoctrineDiscriminatorColumn extends \Addendum\Annotation { public $length; } final class DoctrineDiscriminatorMap extends \Addendum\Annotation {} +final class DoctrineDiscriminatorValue extends \Addendum\Annotation {} final class DoctrineSubClasses extends \Addendum\Annotation {} final class DoctrineId extends \Addendum\Annotation {} final class DoctrineGeneratedValue extends \Addendum\Annotation { diff --git a/lib/Doctrine/ORM/NativeQuery.php b/lib/Doctrine/ORM/NativeQuery.php index 9dd0c1738..19a5cca36 100644 --- a/lib/Doctrine/ORM/NativeQuery.php +++ b/lib/Doctrine/ORM/NativeQuery.php @@ -1,4 +1,23 @@ . + */ namespace Doctrine\ORM; @@ -6,51 +25,60 @@ namespace Doctrine\ORM; * Represents a native SQL query. * * @since 2.0 + * @author Roman Borschel */ -class NativeQuery +class NativeQuery extends AbstractQuery { private $_sql; - private $_conn; - private $_params = array(); - - public function __construct($sql, Connection $conn) - { + + /** + * Initializes a new instance of the NativeQuery class that is bound + * to the given EntityManager. + * + * @param EntityManager $em + */ + public function __construct(EntityManager $em) + { + parent::__construct($em); + } + + /** + * Sets the SQL of the query. + * + * @param string $sql + */ + public function setSql($sql) + { $this->_sql = $sql; - $this->_conn = $conn; } - - /*public function addScalar() + + /** + * Gets the SQL query/queries that correspond to this DQL query. + * + * @return mixed The built sql query or an array of all sql queries. + * @override + */ + public function getSql() { - - }*/ - - public function addEntity($alias, $className) - { - $this->_entities[$alias] = $className; + return $this->_sql; } - - public function addJoin($join) + + /** + * Executed the query. + * + * @param array $params + * @return Statement The Statement handle. + * @override + */ + protected function _doExecute(array $params) { - - } - - public function setParameter($key, $value) - { - $this->_params[$key] = $value; - } - - - public function execute(array $params) - { - if ($this->_entities) { - //... - } else { - return $this->_conn->execute($this->_sql, array_merge($this->_params, $params)); - } - } - - public function executeUpdate(array $params) - { - return $this->_conn->exec($this->_sql, array_merge($this->_params, $params)); + // Assignments for Enums + //$this->_setEnumParams($this->_parserResult->getEnumParams()); + + // Converting parameters + $params = $this->_prepareParams($params); + + // Executing the query and returning statement + return $this->_em->getConnection()->execute($this->_sql, $params); } } \ No newline at end of file diff --git a/lib/Doctrine/ORM/Persisters/AbstractEntityPersister.php b/lib/Doctrine/ORM/Persisters/AbstractEntityPersister.php index 1719af4ae..88daea510 100644 --- a/lib/Doctrine/ORM/Persisters/AbstractEntityPersister.php +++ b/lib/Doctrine/ORM/Persisters/AbstractEntityPersister.php @@ -244,13 +244,5 @@ abstract class AbstractEntityPersister $result[$columnName] = $type->convertToDatabaseValue($newVal, $this->_conn->getDatabasePlatform()); } } - /* - // Populate the discriminator column on insert in JOINED & SINGLE_TABLE inheritance - if ($isInsert && ($this->_classMetadata->isInheritanceTypeJoined() || - $this->_classMetadata->isInheritanceTypeSingleTable())) { - $discColumn = $this->_classMetadata->getDiscriminatorColumn(); - $discMap = $this->_classMetadata->getDiscriminatorMap(); - $result[$discColumn['name']] = array_search($this->_entityName, $discMap); - }*/ } } \ No newline at end of file diff --git a/lib/Doctrine/ORM/Persisters/SingleTablePersister.php b/lib/Doctrine/ORM/Persisters/SingleTablePersister.php index 4ac28aa3d..6376909ff 100644 --- a/lib/Doctrine/ORM/Persisters/SingleTablePersister.php +++ b/lib/Doctrine/ORM/Persisters/SingleTablePersister.php @@ -18,8 +18,8 @@ class SingleTablePersister extends AbstractEntityPersister // Populate the discriminator column if ($isInsert) { $discColumn = $this->_classMetadata->getDiscriminatorColumn(); - $discMap = $this->_classMetadata->getDiscriminatorMap(); - $result[$discColumn['name']] = array_search($this->_entityName, $discMap); + //$discMap = $this->_classMetadata->getDiscriminatorMap(); + $result[$discColumn['name']] = $this->_classMetadata->getDiscriminatorValue(); //array_search($this->_entityName, $discMap); } } diff --git a/lib/Doctrine/ORM/Query.php b/lib/Doctrine/ORM/Query.php index 13ec757cc..e5834c1c0 100644 --- a/lib/Doctrine/ORM/Query.php +++ b/lib/Doctrine/ORM/Query.php @@ -38,161 +38,97 @@ use Doctrine\ORM\Query\QueryException; */ class Query extends AbstractQuery { - /* Hydration mode constants */ /** - * Hydrates an object graph. This is the default behavior. + * QUERY TYPE CONSTANTS */ - const HYDRATE_OBJECT = 1; - /** - * Hydrates an array graph. - */ - const HYDRATE_ARRAY = 2; - /** - * Hydrates a flat, rectangular result set with scalar values. - */ - const HYDRATE_SCALAR = 3; - /** - * Hydrates a single scalar value. - */ - const HYDRATE_SINGLE_SCALAR = 4; - /** - * Hydrates nothing. - */ - const HYDRATE_NONE = 5; - - /** - * @var Doctrine\ORM\EntityManager The entity manager used by this query object. - */ - protected $_em; /** - * @var integer The hydration mode. + * Constant for SELECT queries. */ - protected $_hydrationMode = self::HYDRATE_OBJECT; + const SELECT = 0; + + /** + * Constant for DELETE queries. + */ + const DELETE = 1; + + /** + * Constant for UPDATE queries. + */ + const UPDATE = 2; + + /** + * A query object is in CLEAN state when it has NO unparsed/unprocessed DQL parts. + */ + const STATE_CLEAN = 1; + + /** + * A query object is in state DIRTY when it has DQL parts that have not yet been + * parsed/processed. This is automatically defined as DIRTY when addDqlQueryPart + * is called. + */ + const STATE_DIRTY = 2; + + /** + * @var integer $type Query type. + * + * @see Query::* constants + */ + protected $_type = self::SELECT; + + /** + * @var integer $_state The current state of this query. + */ + protected $_state = self::STATE_CLEAN; + + /** + * @var array $_dqlParts An array containing all DQL query parts. + * @see Query::free that initializes this property + */ + protected $_dqlParts = array(); + + /** + * @var string $_dql Cached DQL query. + */ + protected $_dql = null; /** * @var Doctrine\ORM\Query\ParserResult The parser result that holds DQL => SQL information. */ protected $_parserResult; - /** - * A set of query hints. - * - * @var array - */ - protected $_hints = array(); - - - // Caching Stuff - - /** - * @var Doctrine_Cache_Interface The cache driver used for caching result sets. - */ - protected $_resultCache; - - /** - * @var boolean Boolean value that indicates whether or not expire the result cache. - */ - protected $_expireResultCache = false; - - /** - * @var int Result Cache lifetime. - */ - protected $_resultCacheTTL; - - /** * @var Doctrine_Cache_Interface The cache driver used for caching queries. */ - protected $_queryCache; + //protected $_queryCache; /** * @var boolean Boolean value that indicates whether or not expire the query cache. */ - protected $_expireQueryCache = false; + //protected $_expireQueryCache = false; /** * @var int Query Cache lifetime. */ - protected $_queryCacheTTL; + //protected $_queryCacheTTL; // End of Caching Stuff /** - * Initializes a new instance of the Query class. + * Initializes a new Query instance. * * @param Doctrine\ORM\EntityManager $entityManager */ public function __construct(EntityManager $entityManager) { - $this->_em = $entityManager; - $this->free(); - } - - /** - * Retrieves the associated EntityManager of this Query instance. - * - * @return Doctrine\ORM\EntityManager - */ - public function getEntityManager() - { - return $this->_em; - } - - /** - * Convenience method to execute using array fetching as hydration mode. - * - * @param string $params - * @return array - */ - public function fetchArray($params = array()) - { - return $this->execute($params, self::HYDRATE_ARRAY); - } - - /** - * Convenience method to execute the query and return the first item - * of the collection. - * - * @param string $params Parameters - * @param int $hydrationMode Hydration mode - * @return mixed Array or Doctrine\Common\Collection or false if no result. - */ - public function fetchOne($params = array(), $hydrationMode = null) - { - $collection = $this->limit(1)->execute($params, $hydrationMode); - - if (count($collection) === 0) { - return false; - } - - if ($collection instanceof Collection) { - return $collection->getFirst(); - } else if (is_array($collection)) { - return array_shift($collection); - } - - return false; - } - - /** - * Query the database with DQL (Doctrine Query Language). - * - * @param string $query The DQL query. - * @param array $params The query parameters. - * @param int $hydrationMode - * @return mixed - */ - public function query($query, $params = array(), $hydrationMode = null) - { - $this->setDql($query); - return $this->execute($params, $hydrationMode); + parent::__construct($entityManager); } /** * Gets the SQL query/queries that correspond to this DQL query. * * @return mixed The built sql query or an array of all sql queries. + * @override */ public function getSql() { @@ -213,88 +149,31 @@ class Query extends AbstractQuery $this->_parserResult = $parser->parse(); $this->_state = self::STATE_CLEAN; } - return $this->_parserResult; } - /** - * Executes the query. - * - * @param string $params Parameters to be sent to query. - * @param integer $hydrationMode Doctrine processing mode to be used during hydration process. - * One of the Query::HYDRATE_* constants. - * @return mixed - */ - public function execute($params = array(), $hydrationMode = null) - { - if ($this->_em->getUnitOfWork()->hasPendingInsertions()) { - $this->_em->flush(); - } - - if ($hydrationMode !== null) { - $this->_hydrationMode = $hydrationMode; - } - - $params = $this->getParams($params); - - // Check result cache - if ($this->_resultCache && $this->_type === self::SELECT) { // Only executes if "SELECT" - $cacheDriver = $this->getResultCacheDriver(); - - // Calculate hash for dql query. - $hash = md5($this->getDql() . var_export($params, true)); - $cached = ($this->_expireResultCache) ? false : $cacheDriver->fetch($hash); - - if ($cached === false) { - // Cache does not exist, we have to create it. - $result = $this->_execute($params, self::HYDRATE_ARRAY); - $queryResult = CacheHandler::fromResultSet($this, $result); - $cacheDriver->save($hash, $queryResult->toCachedForm(), $this->_resultCacheTTL); - - return $result; - } else { - // Cache exists, recover it and return the results. - $queryResult = CacheHandler::fromCachedResult($this, $cached); - - return $queryResult->getResultSet(); - } - } - - $stmt = $this->_execute($params); - - if (is_integer($stmt)) { - return $stmt; - } - - return $this->_em->getHydrator($this->_hydrationMode)->hydrateAll($stmt, $this->_parserResult); - } - /** * _execute * * @param array $params * @return PDOStatement The executed PDOStatement. + * @override */ - protected function _execute(array $params) + protected function _doExecute(array $params) { // If there is a CacheDriver associated to cache queries... - if ($this->_queryCache || $this->_em->getConfiguration()->getQueryCacheImpl()) { - $queryCacheDriver = $this->getQueryCacheDriver(); - + if ($queryCache = $this->_em->getConfiguration()->getQueryCacheImpl()) { // Calculate hash for dql query. $hash = md5($this->getDql() . 'DOCTRINE_QUERY_CACHE_SALT'); - $cached = ($this->_expireQueryCache) ? false : $queryCacheDriver->fetch($hash); + $cached = ($this->_expireQueryCache) ? false : $queryCache->fetch($hash); if ($cached === false) { - // Cache does not exist, we have to create it. + // Cache miss. $executor = $this->parse()->getSqlExecutor(); - - // To-be cached item is parserResult - $cacheDriver->save($hash, $this->_parserResult->toCachedForm(), $this->_queryCacheTTL); + $queryCache->save($hash, $this->_parserResult->toCachedForm(), null); } else { - // Cache exists, recover it and return the results. + // Cache hit. $this->_parserResult = CacheHandler::fromCachedQuery($this, $cached); - $executor = $this->_parserResult->getSqlExecutor(); } } else { @@ -307,111 +186,21 @@ class Query extends AbstractQuery // Converting parameters $params = $this->_prepareParams($params); + if ( ! $this->_resultSetMapping) { + $this->_resultSetMapping = $this->_parserResult->getResultSetMapping(); + } + // Executing the query and returning statement return $executor->execute($this->_em->getConnection(), $params); } - /** - * @nodoc - */ - protected function _prepareParams(array $params) - { - // Convert boolean params - $params = $this->_em->getConnection()->getDatabasePlatform()->convertBooleans($params); - - // Convert enum params - return $this->convertEnums($params); - } - - /** - * Defines a cache driver to be used for caching result sets. - * - * @param Doctrine\ORM\Cache\Cache $driver Cache driver - * @return Doctrine\ORM\Query - */ - public function setResultCache($resultCache) - { - if ($resultCache !== null && ! ($resultCache instanceof \Doctrine\ORM\Cache\Cache)) { - throw DoctrineException::updateMe( - 'Method setResultCache() accepts only an instance of Doctrine_Cache_Interface or null.' - ); - } - $this->_resultCache = $resultCache; - - return $this; - } - - /** - * Returns the cache driver used for caching result sets. - * - * @return Doctrine_Cache_Interface Cache driver - */ - public function getResultCache() - { - if ($this->_resultCache instanceof \Doctrine\ORM\Cache\Cache) { - return $this->_resultCache; - } else { - return $this->_em->getConnection()->getResultCacheDriver(); - } - } - - /** - * Defines how long the result cache will be active before expire. - * - * @param integer $timeToLive How long the cache entry is valid - * @return Doctrine\ORM\Query - */ - public function setResultCacheLifetime($timeToLive) - { - if ($timeToLive !== null) { - $timeToLive = (int) $timeToLive; - } - - $this->_resultCacheTTL = $timeToLive; - - return $this; - } - - /** - * Retrieves the lifetime of resultset cache. - * - * @return int - */ - public function getResultCacheLifetime() - { - return $this->_resultCacheTTL; - } - - /** - * Defines if the resultset cache is active or not. - * - * @param boolean $expire Whether or not to force resultset cache expiration. - * @return Doctrine_ORM_Query - */ - public function setExpireResultCache($expire = true) - { - $this->_expireResultCache = (bool) $expire; - - return $this; - } - - /** - * Retrieves if the resultset cache is active or not. - * - * @return bool - */ - public function getExpireResultCache() - { - return $this->_expireResultCache; - } - /** * Defines a cache driver to be used for caching queries. * * @param Doctrine_Cache_Interface|null $driver Cache driver * @return Doctrine_ORM_Query */ - public function setQueryCache($queryCache) + /*public function setQueryCache($queryCache) { if ($queryCache !== null && ! ($queryCache instanceof \Doctrine\ORM\Cache\Cache)) { throw DoctrineException::updateMe( @@ -422,21 +211,21 @@ class Query extends AbstractQuery $this->_queryCache = $queryCache; return $this; - } + }*/ /** * Returns the cache driver used for caching queries. * * @return Doctrine_Cache_Interface Cache driver */ - public function getQueryCache() + /*public function getQueryCache() { if ($this->_queryCache instanceof \Doctrine\ORM\Cache\Cache) { return $this->_queryCache; } else { return $this->_em->getConnection()->getQueryCacheDriver(); } - } + }*/ /** * Defines how long the query cache will be active before expire. @@ -444,7 +233,7 @@ class Query extends AbstractQuery * @param integer $timeToLive How long the cache entry is valid * @return Doctrine_ORM_Query */ - public function setQueryCacheLifetime($timeToLive) + /*public function setQueryCacheLifetime($timeToLive) { if ($timeToLive !== null) { $timeToLive = (int) $timeToLive; @@ -453,17 +242,17 @@ class Query extends AbstractQuery $this->_queryCacheTTL = $timeToLive; return $this; - } + }*/ /** * Retrieves the lifetime of resultset cache. * * @return int */ - public function getQueryCacheLifetime() + /*public function getQueryCacheLifetime() { return $this->_queryCacheTTL; - } + }*/ /** * Defines if the query cache is active or not. @@ -471,156 +260,721 @@ class Query extends AbstractQuery * @param boolean $expire Whether or not to force query cache expiration. * @return Doctrine_ORM_Query */ - public function setExpireQueryCache($expire = true) + /*public function setExpireQueryCache($expire = true) { $this->_expireQueryCache = (bool) $expire; return $this; - } + }*/ /** * Retrieves if the query cache is active or not. * * @return bool */ - public function getExpireQueryCache() + /*public function getExpireQueryCache() { return $this->_expireQueryCache; + }*/ + + /** + * @override + */ + public function free() + { + parent::free(); + $this->_dqlParts = array( + 'select' => array(), + 'distinct' => false, + 'from' => array(), + 'join' => array(), + 'set' => array(), + 'where' => array(), + 'groupby' => array(), + 'having' => array(), + 'orderby' => array(), + 'limit' => array(), + 'offset' => array(), + ); + $this->_dql = null; + $this->_state = self::STATE_CLEAN; } /** - * Defines the processing mode to be used during hydration process. + * Sets a DQL query string. * - * @param integer $hydrationMode Doctrine processing mode to be used during hydration process. - * One of the Query::HYDRATE_* constants. - * @return Doctrine\ORM\Query + * @param string $dqlQuery DQL Query */ - public function setHydrationMode($hydrationMode) + public function setDql($dqlQuery) { - $this->_hydrationMode = $hydrationMode; + $this->free(); + if ($dqlQuery !== null) { + $this->_dql = $dqlQuery; + $this->_state = self::STATE_DIRTY; + } + } + + /** + * Returns the DQL query that is represented by this query object. + * + * @return string DQL query + */ + public function getDql() + { + if ($this->_dql !== null) { + return $this->_dql; + } + + $dql = ''; + + switch ($this->_type) { + case self::DELETE: + $dql = $this->_getDqlForDelete(); + break; + + case self::UPDATE: + $dql = $this->_getDqlForUpdate(); + break; + + case self::SELECT: + default: + $dql = $this->_getDqlForSelect(); + break; + } + + return $dql; + } + + /** + * Builds the DQL of DELETE + */ + protected function _getDqlForDelete() + { + /* + * BNF: + * + * DeleteStatement = DeleteClause [WhereClause] [OrderByClause] [LimitClause] [OffsetClause] + * DeleteClause = "DELETE" "FROM" RangeVariableDeclaration + * WhereClause = "WHERE" ConditionalExpression + * OrderByClause = "ORDER" "BY" OrderByItem {"," OrderByItem} + * LimitClause = "LIMIT" integer + * OffsetClause = "OFFSET" integer + * + */ + return 'DELETE' + . $this->_getReducedDqlQueryPart('from', array('pre' => ' FROM ', 'separator' => ' ')) + . $this->_getReducedDqlQueryPart('where', array('pre' => ' WHERE ', 'separator' => ' ')) + . $this->_getReducedDqlQueryPart('orderby', array('pre' => ' ORDER BY ', 'separator' => ', ')) + . $this->_getReducedDqlQueryPart('limit', array('pre' => ' LIMIT ', 'separator' => ' ')) + . $this->_getReducedDqlQueryPart('offset', array('pre' => ' OFFSET ', 'separator' => ' ')); + } + + + /** + * Builds the DQL of UPDATE + */ + protected function _getDqlForUpdate() + { + /* + * BNF: + * + * UpdateStatement = UpdateClause [WhereClause] [OrderByClause] [LimitClause] [OffsetClause] + * UpdateClause = "UPDATE" RangeVariableDeclaration "SET" UpdateItem {"," UpdateItem} + * WhereClause = "WHERE" ConditionalExpression + * OrderByClause = "ORDER" "BY" OrderByItem {"," OrderByItem} + * LimitClause = "LIMIT" integer + * OffsetClause = "OFFSET" integer + * + */ + return 'UPDATE' + . $this->_getReducedDqlQueryPart('from', array('pre' => ' FROM ', 'separator' => ' ')) + . $this->_getReducedDqlQueryPart('where', array('pre' => ' SET ', 'separator' => ', ')) + . $this->_getReducedDqlQueryPart('where', array('pre' => ' WHERE ', 'separator' => ' ')) + . $this->_getReducedDqlQueryPart('orderby', array('pre' => ' ORDER BY ', 'separator' => ', ')) + . $this->_getReducedDqlQueryPart('limit', array('pre' => ' LIMIT ', 'separator' => ' ')) + . $this->_getReducedDqlQueryPart('offset', array('pre' => ' OFFSET ', 'separator' => ' ')); + } + + + /** + * Builds the DQL of SELECT + */ + protected function _getDqlForSelect() + { + /* + * BNF: + * + * SelectStatement = [SelectClause] FromClause [WhereClause] [GroupByClause] [HavingClause] [OrderByClause] [LimitClause] [OffsetClause] + * SelectClause = "SELECT" ["ALL" | "DISTINCT"] SelectExpression {"," SelectExpression} + * FromClause = "FROM" IdentificationVariableDeclaration {"," IdentificationVariableDeclaration} + * WhereClause = "WHERE" ConditionalExpression + * GroupByClause = "GROUP" "BY" GroupByItem {"," GroupByItem} + * HavingClause = "HAVING" ConditionalExpression + * OrderByClause = "ORDER" "BY" OrderByItem {"," OrderByItem} + * LimitClause = "LIMIT" integer + * OffsetClause = "OFFSET" integer + * + */ + /** + * @todo [TODO] What about "ALL" support? + */ + return 'SELECT' + . (($this->getDqlQueryPart('distinct') === true) ? ' DISTINCT' : '') + . $this->_getReducedDqlQueryPart('select', array('pre' => ' ', 'separator' => ', ', 'empty' => ' *')) + . $this->_getReducedDqlQueryPart('from', array('pre' => ' FROM ', 'separator' => ' ')) + . $this->_getReducedDqlQueryPart('where', array('pre' => ' WHERE ', 'separator' => ' ')) + . $this->_getReducedDqlQueryPart('groupby', array('pre' => ' GROUP BY ', 'separator' => ', ')) + . $this->_getReducedDqlQueryPart('having', array('pre' => ' HAVING ', 'separator' => ' ')) + . $this->_getReducedDqlQueryPart('orderby', array('pre' => ' ORDER BY ', 'separator' => ', ')) + . $this->_getReducedDqlQueryPart('limit', array('pre' => ' LIMIT ', 'separator' => ' ')) + . $this->_getReducedDqlQueryPart('offset', array('pre' => ' OFFSET ', 'separator' => ' ')); + } + + + /** + * @nodoc + */ + protected function _getReducedDqlQueryPart($queryPartName, $options = array()) + { + if (empty($this->_dqlParts[$queryPartName])) { + return (isset($options['empty']) ? $options['empty'] : ''); + } + + $str = (isset($options['pre']) ? $options['pre'] : ''); + $str .= implode($options['separator'], $this->getDqlQueryPart($queryPartName)); + $str .= (isset($options['post']) ? $options['post'] : ''); + + return $str; + } + + /** + * Returns the type of this query object + * By default the type is Doctrine_ORM_Query_Abstract::SELECT but if update() or delete() + * are being called the type is Doctrine_ORM_Query_Abstract::UPDATE and Doctrine_ORM_Query_Abstract::DELETE, + * respectively. + * + * @see Doctrine_ORM_Query_Abstract::SELECT + * @see Doctrine_ORM_Query_Abstract::UPDATE + * @see Doctrine_ORM_Query_Abstract::DELETE + * + * @return integer Return the query type + */ + public function getType() + { + return $this->_type; + } + + /** + * Returns the state of this query object + * By default the type is Doctrine_ORM_Query_Abstract::STATE_CLEAN but if it appears any unprocessed DQL + * part, it is switched to Doctrine_ORM_Query_Abstract::STATE_DIRTY. + * + * @see AbstractQuery::STATE_CLEAN + * @see AbstractQuery::STATE_DIRTY + * + * @return integer Return the query state + */ + public function getState() + { + return $this->_state; + } + + /** + * Adds fields to the SELECT part of the query + * + * @param string $select Query SELECT part + * @return Doctrine_ORM_Query + */ + public function select($select = '', $override = false) + { + if ($select === '') { + return $this; + } + + return $this->_addDqlQueryPart('select', $select, ! $override); + } + + /** + * Makes the query SELECT DISTINCT. + * + * @param bool $flag Whether or not the SELECT is DISTINCT (default true). + * @return Doctrine_ORM_Query + */ + public function distinct($flag = true) + { + $this->_dqlParts['distinct'] = (bool) $flag; return $this; } /** - * Gets the hydration mode currently used by the query. + * Sets the query type to DELETE * - * @return integer + * @return Doctrine_ORM_Query */ - public function getHydrationMode() + public function delete() { - return $this->_hydrationMode; - } - - /** - * Gets the list of results for the query. - * - * Alias for execute(array(), HYDRATE_OBJECT). - * - * @return Collection - */ - public function getResultList() - { - return $this->execute(array(), self::HYDRATE_OBJECT); + $this->_type = self::DELETE; + return $this; } /** - * Gets the array of results for the query. + * Sets the UPDATE part of the query * - * Alias for execute(array(), HYDRATE_ARRAY). - * - * @return array + * @param string $update Query UPDATE part + * @return Doctrine_ORM_Query */ - public function getResultArray() + public function update($update) { - return $this->execute(array(), self::HYDRATE_ARRAY); + $this->_type = self::UPDATE; + return $this->_addDqlQueryPart('from', $update); } /** - * Gets the scalar results for the query. + * Sets the SET part of the query * - * Alias for execute(array(), HYDRATE_SCALAR). - * - * @return array + * @param mixed $key UPDATE keys. Accepts either a string (requiring then $value or $params to be defined) + * or an array of $key => $value pairs. + * @param string $value UPDATE key value. Optional argument, but required if $key is a string. + * @return Doctrine_ORM_Query */ - public function getScalarResult() + public function set($key, $value = null, $params = null) { - return $this->execute(array(), self::HYDRATE_SCALAR); - } - - /** - * Gets the single result of the query. - * Enforces the uniqueness of the result. If the result is not unique, - * a QueryException is thrown. - * - * @param integer $hydrationMode - * @return mixed - * @throws QueryException If the query result is not unique. - */ - public function getSingleResult($hydrationMode = null) - { - $result = $this->execute(array(), $hydrationMode); - if (is_array($result)) { - if (count($result) > 1) { - throw QueryException::nonUniqueResult(); + if (is_array($key)) { + foreach ($key as $k => $v) { + $this->set($k, '?', array($v)); } - return array_shift($result); - } else if (is_object($result)) { - if (count($result) > 1) { - throw QueryException::nonUniqueResult(); + + return $this; + } else { + if ($params !== null) { + if (is_array($params)) { + $this->_params['set'] = array_merge($this->_params['set'], $params); + } else { + $this->_params['set'][] = $params; + } } - return $result->getFirst(); + + if ($value === null) { + throw \Doctrine\Common\DoctrineException::updateMe( 'Cannot try to set \''.$key.'\' without a value.' ); + } + + return $this->_addDqlQueryPart('set', $key . ' = ' . $value, true); } - return $result; } /** - * Gets the single scalar result of the query. + * Adds fields to the FROM part of the query * - * Alias for getSingleResult(HYDRATE_SINGLE_SCALAR). - * - * @return mixed - * @throws QueryException If the query result is not unique. + * @param string $from Query FROM part + * @return Doctrine_ORM_Query */ - public function getSingleScalarResult() + public function from($from, $override = false) { - return $this->getSingleResult(self::HYDRATE_SINGLE_SCALAR); + return $this->_addDqlQueryPart('from', $from, ! $override); } /** - * Sets an implementation-specific hint. If the hint name is not recognized, - * it is silently ignored. + * Appends an INNER JOIN to the FROM part of the query * - * @param string $name The name of the hint. - * @param mixed $value The value of the hint. + * @param string $join Query INNER JOIN + * @param mixed $params Optional JOIN params (array of parameters or a simple scalar) + * @return Doctrine_ORM_Query */ - public function setHint($name, $value) + public function innerJoin($join, $params = array()) { - $this->_hints[$name] = $value; + if (is_array($params)) { + $this->_params['join'] = array_merge($this->_params['join'], $params); + } else { + $this->_params['join'][] = $params; + } + + return $this->_addDqlQueryPart('from', 'INNER JOIN ' . $join, true); } /** - * Gets an implementation-specific hint. If the hint name is not recognized, - * FALSE is returned. + * Appends an INNER JOIN to the FROM part of the query * - * @param string $name The name of the hint. + * @param string $join Query INNER JOIN + * @param mixed $params Optional JOIN params (array of parameters or a simple scalar) + * @return Doctrine_ORM_Query */ - public function getHint($name) + public function join($join, $params = array()) { - return isset($this->_hints[$name]) ? $this->_hints[$name] : false; + return $this->innerJoin($join, $params); } /** - * Executes the query and returns an IterableResult that can be used to incrementally - * iterated over the result. + * Appends a LEFT JOIN to the FROM part of the query * - * @param array $params The query parameters. - * @param integer $hydrationMode The hydratio mode to use. - * @return IterableResult + * @param string $join Query LEFT JOIN + * @param mixed $params Optional JOIN params (array of parameters or a simple scalar) + * @return Doctrine_ORM_Query */ - public function iterate(array $params = array(), $hydrationMode = self::HYDRATE_OBJECT) + public function leftJoin($join, $params = array()) { - return $this->_em->getHydrator($this->_hydrationMode)->iterate( - $this->_execute($params, $hydrationMode), $this->_parserResult + if (is_array($params)) { + $this->_params['join'] = array_merge($this->_params['join'], $params); + } else { + $this->_params['join'][] = $params; + } + + return $this->_addDqlQueryPart('from', 'LEFT JOIN ' . $join, true); + } + + /** + * Adds conditions to the WHERE part of the query + * + * @param string $where Query WHERE part + * @param mixed $params An array of parameters or a simple scalar + * @return Doctrine_ORM_Query + */ + public function where($where, $params = array(), $override = false) + { + if ($override) { + $this->_params['where'] = array(); + } + + if (is_array($params)) { + $this->_params['where'] = array_merge($this->_params['where'], $params); + } else { + $this->_params['where'][] = $params; + } + + return $this->_addDqlQueryPart('where', $where, ! $override); + } + + /** + * Adds conditions to the WHERE part of the query + * + * @param string $where Query WHERE part + * @param mixed $params An array of parameters or a simple scalar + * @return Doctrine_ORM_Query + */ + public function andWhere($where, $params = array(), $override = false) + { + if (count($this->getDqlQueryPart('where')) > 0) { + $this->_addDqlQueryPart('where', 'AND', true); + } + + return $this->where($where, $params, $override); + } + + /** + * Adds conditions to the WHERE part of the query + * + * @param string $where Query WHERE part + * @param mixed $params An array of parameters or a simple scalar + * @return Doctrine_ORM_Query + */ + public function orWhere($where, $params = array(), $override = false) + { + if (count($this->getDqlQueryPart('where')) > 0) { + $this->_addDqlQueryPart('where', 'OR', true); + } + + return $this->where($where, $params, $override); + } + + /** + * Adds IN condition to the query WHERE part + * + * @param string $expr The operand of the IN + * @param mixed $params An array of parameters or a simple scalar + * @param boolean $not Whether or not to use NOT in front of IN + * @return Doctrine_ORM_Query + */ + public function whereIn($expr, $params = array(), $override = false, $not = false) + { + $params = (array) $params; + + // Must have at least one param, otherwise we'll get an empty IN () => invalid SQL + if ( ! count($params)) { + return $this; + } + + list($sqlPart, $params) = $this->_processWhereInParams($params); + + $where = $expr . ($not === true ? ' NOT' : '') . ' IN (' . $sqlPart . ')'; + + return $this->_returnWhereIn($where, $params, $override); + } + + /** + * Adds NOT IN condition to the query WHERE part + * + * @param string $expr The operand of the NOT IN + * @param mixed $params An array of parameters or a simple scalar + * @return Doctrine_ORM_Query + */ + public function whereNotIn($expr, $params = array(), $override = false) + { + return $this->whereIn($expr, $params, $override, true); + } + + /** + * Adds IN condition to the query WHERE part + * + * @param string $expr The operand of the IN + * @param mixed $params An array of parameters or a simple scalar + * @param boolean $not Whether or not to use NOT in front of IN + * @return Doctrine_ORM_Query + */ + public function andWhereIn($expr, $params = array(), $override = false) + { + if (count($this->getDqlQueryPart('where')) > 0) { + $this->_addDqlQueryPart('where', 'AND', true); + } + + return $this->whereIn($expr, $params, $override); + } + + /** + * Adds NOT IN condition to the query WHERE part + * + * @param string $expr The operand of the NOT IN + * @param mixed $params An array of parameters or a simple scalar + * @return Doctrine_ORM_Query + */ + public function andWhereNotIn($expr, $params = array(), $override = false) + { + if (count($this->getDqlQueryPart('where')) > 0) { + $this->_addDqlQueryPart('where', 'AND', true); + } + + return $this->whereIn($expr, $params, $override, true); + } + + /** + * Adds IN condition to the query WHERE part + * + * @param string $expr The operand of the IN + * @param mixed $params An array of parameters or a simple scalar + * @param boolean $not Whether or not to use NOT in front of IN + * @return Doctrine_ORM_Query + */ + public function orWhereIn($expr, $params = array(), $override = false) + { + if (count($this->getDqlQueryPart('where')) > 0) { + $this->_addDqlQueryPart('where', 'OR', true); + } + + return $this->whereIn($expr, $params, $override); + } + + /** + * Adds NOT IN condition to the query WHERE part + * + * @param string $expr The operand of the NOT IN + * @param mixed $params An array of parameters or a simple scalar + * @return Doctrine_ORM_Query + */ + public function orWhereNotIn($expr, $params = array(), $override = false) + { + if (count($this->getDqlQueryPart('where')) > 0) { + $this->_addDqlQueryPart('where', 'OR', true); + } + + return $this->whereIn($expr, $params, $override, true); + } + + /** + * Adds fields to the GROUP BY part of the query + * + * @param string $groupby Query GROUP BY part + * @return Doctrine_ORM_Query + */ + public function groupBy($groupby, $override = false) + { + return $this->_addDqlQueryPart('groupby', $groupby, ! $override); + } + + /** + * Adds conditions to the HAVING part of the query + * + * @param string $having Query HAVING part + * @param mixed $params An array of parameters or a simple scalar + * @return Doctrine_ORM_Query + */ + public function having($having, $params = array(), $override = false) + { + if ($override) { + $this->_params['having'] = array(); + } + + if (is_array($params)) { + $this->_params['having'] = array_merge($this->_params['having'], $params); + } else { + $this->_params['having'][] = $params; + } + + return $this->_addDqlQueryPart('having', $having, true); + } + + /** + * Adds conditions to the HAVING part of the query + * + * @param string $having Query HAVING part + * @param mixed $params An array of parameters or a simple scalar + * @return Doctrine_ORM_Query + */ + public function andHaving($having, $params = array(), $override = false) + { + if (count($this->getDqlQueryPart('having')) > 0) { + $this->_addDqlQueryPart('having', 'AND', true); + } + + return $this->having($having, $params, $override); + } + + /** + * Adds conditions to the HAVING part of the query + * + * @param string $having Query HAVING part + * @param mixed $params An array of parameters or a simple scalar + * @return Doctrine_ORM_Query + */ + public function orHaving($having, $params = array(), $override = false) + { + if (count($this->getDqlQueryPart('having')) > 0) { + $this->_addDqlQueryPart('having', 'OR', true); + } + + return $this->having($having, $params, $override); + } + + /** + * Adds fields to the ORDER BY part of the query + * + * @param string $orderby Query ORDER BY part + * @return Doctrine_ORM_Query + */ + public function orderBy($orderby, $override = false) + { + return $this->_addDqlQueryPart('orderby', $orderby, ! $override); + } + + /** + * Sets the Query query limit + * + * @param integer $limit Limit to be used for limiting the query results + * @return Doctrine_ORM_Query + */ + public function limit($limit) + { + return $this->_addDqlQueryPart('limit', $limit); + } + + /** + * Sets the Query query offset + * + * @param integer $offset Offset to be used for paginating the query + * @return Doctrine_ORM_Query + */ + public function offset($offset) + { + return $this->_addDqlQueryPart('offset', $offset); + } + + /** + * Method to check if a arbitrary piece of DQL exists + * + * @param string $dql Arbitrary piece of DQL to check for + * @return boolean + */ + public function contains($dql) + { + return stripos($this->getDql(), $dql) === false ? false : true; + } + + /** + * Retrieve a DQL part for internal purposes + * + * @param string $queryPartName The name of the query part. + * @return mixed Array related to query part or simple scalar + */ + public function getDqlQueryPart($queryPartName) + { + if ( ! isset($this->_dqlParts[$queryPartName])) { + throw \Doctrine\Common\DoctrineException::updateMe('Unknown DQL query part \'' . $queryPartName . '\''); + } + + return $this->_dqlParts[$queryPartName]; + } + + /** + * Adds a DQL part to the internal parts collection. + * + * @param string $queryPartName The name of the query part. + * @param string $queryPart The actual query part to add. + * @param boolean $append Whether to append $queryPart to already existing + * parts under the same $queryPartName. Defaults to FALSE + * (previously added parts with the same name get overridden). + * @return Doctrine_ORM_Query + */ + protected function _addDqlQueryPart($queryPartName, $queryPart, $append = false) + { + if ($append) { + $this->_dqlParts[$queryPartName][] = $queryPart; + } else { + $this->_dqlParts[$queryPartName] = array($queryPart); + } + + $this->_state = Doctrine_ORM_Query::STATE_DIRTY; + return $this; + } + + + /** + * Processes the WHERE IN () parameters and return an indexed array containing + * the sqlPart to be placed in SQL statement and the new parameters (that will be + * bound in SQL execution) + * + * @param array $params Parameters to be processed + * @return array + */ + protected function _processWhereInParams($params = array()) + { + return array( + // [0] => sqlPart + implode(', ', array_map(array(&$this, '_processWhereInSqlPart'), $params)), + // [1] => params + array_filter($params, array(&$this, '_processWhereInParamItem')), ); } + + /** + * @nodoc + */ + protected function _processWhereInSqlPart($value) + { + // [TODO] Add support to imbricated query (must deliver the hardest effort to Parser) + return ($value instanceof Doctrine_Expression) ? $value->getSql() : '?'; + } + + /** + * @nodoc + */ + protected function _processWhereInParamItem($value) + { + // [TODO] Add support to imbricated query (must deliver the hardest effort to Parser) + return ( ! ($value instanceof Doctrine_Expression)); + } + + /** + * Processes a WHERE IN () and build defined stuff to add in DQL + * + * @param string $where The WHERE clause to be added + * @param array $params WHERE clause parameters + * @param mixed $appender Where this clause may be not be appended, or appended + * (two possible values: AND or OR) + * @return Doctrine_ORM_Query + */ + protected function _returnWhereIn($where, $params = array(), $override = false) + { + // Parameters inclusion + $this->_params['where'] = $override ? $params : array_merge($this->_params['where'], $params); + + // WHERE clause definition + return $this->_addDqlQueryPart('where', $where, ! $override); + } } \ No newline at end of file diff --git a/lib/Doctrine/ORM/Query/AbstractResult.php b/lib/Doctrine/ORM/Query/AbstractResult.php index 563f144b4..781298638 100644 --- a/lib/Doctrine/ORM/Query/AbstractResult.php +++ b/lib/Doctrine/ORM/Query/AbstractResult.php @@ -46,31 +46,6 @@ abstract class AbstractResult */ protected $_enumParams = array(); - /** - * @var boolean - */ - protected $_isMixedQuery = false; - - /** - * Gets whether the parsed query selects objects/arrays and scalar values - * at the same time. - * - * @return boolean - */ - public function isMixedQuery() - { - return $this->_isMixedQuery; - } - - /** - * Sets whether the parsed query selects objects/arrays and scalar values - * at the same time. - */ - public function setMixedQuery($bool) - { - $this->_isMixedQuery = $bool; - } - /** * Returns the enum parameters. * diff --git a/lib/Doctrine/ORM/Query/Exec/SingleSelectExecutor.php b/lib/Doctrine/ORM/Query/Exec/SingleSelectExecutor.php index e3e7e120c..967ba4541 100644 --- a/lib/Doctrine/ORM/Query/Exec/SingleSelectExecutor.php +++ b/lib/Doctrine/ORM/Query/Exec/SingleSelectExecutor.php @@ -16,7 +16,7 @@ * * This software consists of voluntary contributions made by many individuals * and is licensed under the LGPL. For more information, see - * . + * . */ namespace Doctrine\ORM\Query\Exec; diff --git a/lib/Doctrine/ORM/Query/Parser.php b/lib/Doctrine/ORM/Query/Parser.php index 6d7bbdeca..6401342c6 100644 --- a/lib/Doctrine/ORM/Query/Parser.php +++ b/lib/Doctrine/ORM/Query/Parser.php @@ -112,30 +112,12 @@ class Parser */ private $_query; - /** - * Whether the query is a SELECT query and contains scalar values in the result list - * as defined by the SelectExpressions. - * - * @var boolean - */ - private $_resultContainsScalars = false; - - /** - * Whether the query is a SELECT query and contains properties in the result list - * as defined by the SelectExpressions. - * - * @var boolean - */ - private $_resultContainsProperties = false; - /** * Map of declared classes in the parsed query. * Maps the declared DQL alias (key) to the class name (value). * * @var array */ - //private $_declaredClasses = array(); - private $_queryComponents = array(); /** @@ -656,10 +638,6 @@ class Parser $peek = $this->_lexer->glimpse(); // First we recognize for an IdentificationVariable (DQL class alias) if ($peek['value'] != '.' && $peek['value'] != '(' && $this->_lexer->lookahead['type'] === Lexer::T_IDENTIFIER) { - $this->_resultContainsProperties = true; - if ($this->_resultContainsScalars) { - $this->_parserResult->setMixedQuery(true); - } $expression = $this->_IdentificationVariable(); } else if (($isFunction = $this->_isFunction()) !== false || $this->_isSubselect()) { if ($isFunction) { @@ -680,15 +658,7 @@ class Parser $this->match(Lexer::T_IDENTIFIER); $fieldIdentificationVariable = $this->_lexer->token['value']; } - $this->_resultContainsScalars = true; - if ($this->_resultContainsProperties) { - $this->_parserResult->setMixedQuery(true); - } } else { - $this->_resultContainsProperties = true; - if ($this->_resultContainsScalars) { - $this->_parserResult->setMixedQuery(true); - } //TODO: If hydration mode is OBJECT throw an exception ("partial object dangerous...") // unless the doctrine.forcePartialLoad query hint is set $expression = $this->_StateFieldPathExpression(); diff --git a/lib/Doctrine/ORM/Query/ResultSetMapping.php b/lib/Doctrine/ORM/Query/ResultSetMapping.php index 4f8178447..2f801c6b3 100644 --- a/lib/Doctrine/ORM/Query/ResultSetMapping.php +++ b/lib/Doctrine/ORM/Query/ResultSetMapping.php @@ -29,6 +29,8 @@ namespace Doctrine\ORM\Query; */ class ResultSetMapping { + /** Whether the result is mixed (contains scalar values together with field values). */ + private $_isMixed = false; /** Maps alias names to ClassMetadata descriptors. */ private $_aliasMap = array(); /** Maps alias names to related association mappings. */ @@ -41,10 +43,12 @@ class ResultSetMapping private $_scalarMappings = array(); /** Maps column names in the result set to the alias they belong to. */ private $_columnOwnerMap = array(); - /** Maps discriminator columns in the result set to the class they represent. */ - private $_discriminatorMap = array(); + /** List of columns in the result set that are used as discriminator columns. */ + private $_discriminatorColumns = array(); /** Maps alias names to field names that should be used for indexing. */ private $_indexByMap = array(); + /** A list of columns that should be ignored/skipped during hydration. */ + private $_ignoredColumns = array(); /** * @@ -52,12 +56,21 @@ class ResultSetMapping * @param $alias The alias for this class. The alias must be unique within this ResultSetMapping. * @param $discriminatorColumn */ - public function addEntityResult($class, $alias, $discriminatorColumn = null) + public function addEntityResult($class, $alias) { $this->_aliasMap[$alias] = $class; - if ($discriminatorColumn !== null) { - $this->_discriminatorMap[$discriminatorColumn] = $class; - } + } + + public function setDiscriminatorColumn($className, $alias, $discrColumn) + { + $this->_discriminatorColumns[$className] = $discrColumn; + $this->_columnOwnerMap[$discrColumn] = $alias; + } + + public function getDiscriminatorColumn($className) + { + return isset($this->_discriminatorColumns[$className]) ? + $this->_discriminatorColumns[$className] : null; } public function addIndexBy($alias, $fieldName) @@ -75,30 +88,38 @@ class ResultSetMapping return $this->_indexByMap[$alias]; } + public function isFieldResult($columnName) + { + return isset($this->_fieldMappings[$columnName]); + } + public function addFieldResult($alias, $columnName, $fieldName) { $this->_fieldMappings[$columnName] = $fieldName; $this->_columnOwnerMap[$columnName] = $alias; + if ( ! $this->_isMixed && $this->_scalarMappings) { + $this->_isMixed = true; + } } - public function addJoinedEntityResult($class, $alias, $parentAlias, $relation, $discriminatorColumn = null) + public function addJoinedEntityResult($class, $alias, $parentAlias, $relation) { $this->_aliasMap[$alias] = $class; $this->_parentAliasMap[$alias] = $parentAlias; $this->_relationMap[$alias] = $relation; - if ($discriminatorColumn !== null) { - $this->_discriminatorMap[$discriminatorColumn] = $class; - } } - public function isDiscriminatorColumn($columnName) + /*public function isDiscriminatorColumn($columnName) { return isset($this->_discriminatorMap[$columnName]); - } + }*/ public function addScalarResult($columnName, $alias) { $this->_scalarMappings[$columnName] = $alias; + if ( ! $this->_isMixed && $this->_fieldMappings) { + $this->_isMixed = true; + } } /** @@ -115,9 +136,6 @@ class ResultSetMapping */ public function getClass($alias) { - if ( ! isset($this->_aliasMap[$alias])) { - var_dump($alias); die(); - } return $this->_aliasMap[$alias]; } @@ -192,5 +210,20 @@ class ResultSetMapping { return count($this->_aliasMap); } + + public function isMixedResult() + { + return $this->_isMixed; + } + + public function addIgnoredColumn($columnName) + { + $this->_ignoredColumns[$columnName] = true; + } + + public function isIgnoredColumn($columnName) + { + return isset($this->_ignoredColumns[$columnName]); + } } diff --git a/lib/Doctrine/ORM/Query/SqlWalker.php b/lib/Doctrine/ORM/Query/SqlWalker.php index 72bc5ca88..872cba4ee 100644 --- a/lib/Doctrine/ORM/Query/SqlWalker.php +++ b/lib/Doctrine/ORM/Query/SqlWalker.php @@ -64,20 +64,12 @@ class SqlWalker $this->_em = $query->getEntityManager(); $this->_parserResult = $parserResult; $this->_queryComponents = $queryComponents; - /*$sqlToDqlAliasMap = array(Parser::SCALAR_QUERYCOMPONENT_ALIAS => Parser::SCALAR_QUERYCOMPONENT_ALIAS); - foreach ($parserResult->getQueryComponents() as $dqlAlias => $qComp) { - $sqlAlias = $this->generateSqlTableAlias($qComp['metadata']->getTableName()); - $sqlToDqlAliasMap[$sqlAlias] = $dqlAlias; - } - // SQL => DQL alias stored in ParserResult, needed for hydration. - $parserResult->setTableAliasMap($sqlToDqlAliasMap);*/ - // DQL => SQL alias stored only locally, needed for SQL construction. - //$this->_dqlToSqlAliasMap = array_flip($sqlToDqlAliasMap); + // In a mixed query we start alias counting for scalars with 1 since // index 0 will hold the object. - if ($parserResult->isMixedQuery()) { + /*if ($parserResult->isMixedQuery()) { $this->_scalarResultCounter = 1; - } + }*/ } public function getConnection() @@ -99,7 +91,6 @@ class SqlWalker $sql .= $AST->getHavingClause() ? $this->walkHavingClause($AST->getHavingClause()) : ''; $sql .= $AST->getOrderByClause() ? $this->walkOrderByClause($AST->getOrderByClause()) : ''; - //... more clauses return $sql; } @@ -113,16 +104,6 @@ class SqlWalker $sql = 'SELECT ' . (($selectClause->isDistinct()) ? 'DISTINCT ' : '') . implode(', ', array_map(array($this, 'walkSelectExpression'), $selectClause->getSelectExpressions())); - // Append discriminator columns - /*if ($this->_query->getHydrationMode() == \Doctrine\ORM\Query::HYDRATE_OBJECT) { - foreach ($this->_selectedClasses as $dqlAlias => $class) { - if ($class->isInheritanceTypeSingleTable() || $class->isInheritanceTypeJoined()) { - $tblAlias = $this->_dqlToSqlAliasMap[$dqlAlias]; - $discrColumn = $class->getDiscriminatorColumn(); - $sql .= ", $tblAlias." . $discrColumn['name'] . ' AS discr__' . $discrColumn['name']; - } - } - }*/ foreach ($this->_selectedClasses as $dqlAlias => $class) { if ($this->_queryComponents[$dqlAlias]['relation'] === null) { @@ -134,6 +115,15 @@ class SqlWalker $this->_queryComponents[$dqlAlias]['relation'] ); } + //if ($this->_query->getHydrationMode() == \Doctrine\ORM\Query::HYDRATE_OBJECT) { + if ($class->isInheritanceTypeSingleTable() || $class->isInheritanceTypeJoined()) { + $tblAlias = $this->getSqlTableAlias($class->getTableName()); + $discrColumn = $class->getDiscriminatorColumn(); + $columnAlias = $this->getSqlColumnAlias($discrColumn['name']); + $sql .= ", $tblAlias." . $discrColumn['name'] . ' AS ' . $columnAlias; + $this->_resultSetMapping->setDiscriminatorColumn($class->getClassName(), $dqlAlias, $columnAlias); + } + //} } return $sql; diff --git a/lib/Doctrine/ORM/UnitOfWork.php b/lib/Doctrine/ORM/UnitOfWork.php index 25fee1a97..f4c44c4d3 100644 --- a/lib/Doctrine/ORM/UnitOfWork.php +++ b/lib/Doctrine/ORM/UnitOfWork.php @@ -1173,15 +1173,6 @@ class UnitOfWork implements PropertyChangedListener */ public function createEntity($className, array $data, $query = null) { - // Infer the correct class to instantiate - $class = $this->_em->getClassMetadata($className); - $discCol = $class->getDiscriminatorColumn(); - if ($discCol) { - $discMap = $class->getDiscriminatorMap(); - if (isset($data[$discCol['name']], $discMap[$data[$discCol['name']]])) { - $className = $discMap[$data[$discCol['name']]]; - } - } $class = $this->_em->getClassMetadata($className); $id = array(); @@ -1232,15 +1223,17 @@ class UnitOfWork implements PropertyChangedListener private function _mergeData($entity, array $data, $class, $overrideLocalChanges = false) { if ($overrideLocalChanges) { foreach ($data as $field => $value) { - $class->getReflectionProperty($field)->setValue($entity, $value); + $class->setValue($entity, $field, $value); } } else { $oid = spl_object_hash($entity); foreach ($data as $field => $value) { - $currentValue = $class->getReflectionProperty($field)->getValue($entity); - if ( ! isset($this->_originalEntityData[$oid][$field]) || + if ($class->hasField($field)) { + $currentValue = $class->getReflectionProperty($field)->getValue($entity); + if ( ! isset($this->_originalEntityData[$oid][$field]) || $currentValue == $this->_originalEntityData[$oid][$field]) { - $class->getReflectionProperty($field)->setValue($entity, $value); + $class->getReflectionProperty($field)->setValue($entity, $value); + } } } } diff --git a/tests/Doctrine/Tests/Models/Forum/ForumUser.php b/tests/Doctrine/Tests/Models/Forum/ForumUser.php index 9b51eaa7e..9212b58ed 100644 --- a/tests/Doctrine/Tests/Models/Forum/ForumUser.php +++ b/tests/Doctrine/Tests/Models/Forum/ForumUser.php @@ -5,12 +5,6 @@ namespace Doctrine\Tests\Models\Forum; /** * @DoctrineEntity * @DoctrineTable(name="forum_users") - * @DoctrineInheritanceType("joined") - * @DoctrineDiscriminatorColumn(name="dtype", type="varchar", length=20) - * @DoctrineDiscriminatorMap({ - "user" = "Doctrine\Tests\Models\Forum\ForumUser", - "admin" = "Doctrine\Tests\Models\Forum\ForumAdministrator"}) - * @DoctrineSubclasses({"Doctrine\Tests\Models\Forum\ForumAdministrator"}) */ class ForumUser { diff --git a/tests/Doctrine/Tests/ORM/EntityPersisterTest.php b/tests/Doctrine/Tests/ORM/EntityPersisterTest.php index 5f6fe2421..f4d3d9600 100644 --- a/tests/Doctrine/Tests/ORM/EntityPersisterTest.php +++ b/tests/Doctrine/Tests/ORM/EntityPersisterTest.php @@ -70,8 +70,5 @@ class EntityPersisterTest extends \Doctrine\Tests\OrmTestCase //avatar_id join column $this->assertTrue(isset($inserts['forum_users'][0]['avatar_id'])); $this->assertEquals(0, $inserts['forum_users'][0]['avatar_id']); - //dtype discriminator column - $this->assertTrue(isset($inserts['forum_users'][0]['dtype'])); - $this->assertEquals('user', $inserts['forum_users'][0]['dtype']); } } \ No newline at end of file diff --git a/tests/Doctrine/Tests/ORM/Functional/AllTests.php b/tests/Doctrine/Tests/ORM/Functional/AllTests.php index 4c40cd882..5cab14775 100644 --- a/tests/Doctrine/Tests/ORM/Functional/AllTests.php +++ b/tests/Doctrine/Tests/ORM/Functional/AllTests.php @@ -20,6 +20,8 @@ class AllTests $suite = new \Doctrine\Tests\OrmFunctionalTestSuite('Doctrine Orm Functional'); $suite->addTestSuite('Doctrine\Tests\ORM\Functional\BasicFunctionalTest'); + $suite->addTestSuite('Doctrine\Tests\ORM\Functional\NativeQueryTest'); + $suite->addTestSuite('Doctrine\Tests\ORM\Functional\SingleTableInheritanceTest'); return $suite; } diff --git a/tests/Doctrine/Tests/ORM/Functional/NativeQueryTest.php b/tests/Doctrine/Tests/ORM/Functional/NativeQueryTest.php new file mode 100644 index 000000000..a65cb68fe --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Functional/NativeQueryTest.php @@ -0,0 +1,45 @@ +useModelSet('cms'); + parent::setUp(); + } + + public function testBasicNativeQuery() + { + $user = new CmsUser; + $user->name = 'Roman'; + $user->username = 'romanb'; + $user->status = 'dev'; + $this->_em->save($user); + $this->_em->flush(); + + $rsm = new ResultSetMapping; + $rsm->addEntityResult($this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsUser'), 'u'); + $rsm->addFieldResult('u', 'id', 'id'); + $rsm->addFieldResult('u', 'name', 'name'); + + $query = $this->_em->createNativeQuery('SELECT id, name FROM cms_users WHERE username = ?', $rsm); + $query->setParameter(1, 'romanb'); + + $users = $query->getResultList(); + + $this->assertEquals(1, count($users)); + $this->assertEquals('Roman', $users[0]->name); + } +} + diff --git a/tests/Doctrine/Tests/ORM/Functional/SingleTableInheritanceTest.php b/tests/Doctrine/Tests/ORM/Functional/SingleTableInheritanceTest.php new file mode 100644 index 000000000..05da85173 --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Functional/SingleTableInheritanceTest.php @@ -0,0 +1,103 @@ +_schemaTool->createSchema(array( + $this->_em->getClassMetadata('Doctrine\Tests\ORM\Functional\ParentEntity'), + $this->_em->getClassMetadata('Doctrine\Tests\ORM\Functional\ChildEntity') + )); + } + + public function testInsert() + { + + $parent = new ParentEntity; + $parent->setData('foobar'); + + $this->_em->save($parent); + + $child = new ChildEntity; + $child->setData('thedata'); + $child->setNumber(1234); + + $this->_em->save($child); + + $this->_em->flush(); + $this->_em->clear(); + + $query = $this->_em->createQuery("select e from Doctrine\Tests\ORM\Functional\ParentEntity e"); + + $entities = $query->getResultList(); + + $this->assertEquals(2, count($entities)); + $this->assertTrue($entities[0] instanceof ParentEntity); + $this->assertTrue($entities[1] instanceof ChildEntity); + $this->assertEquals('foobar', $entities[0]->getData()); + $this->assertEquals('thedata', $entities[1]->getData()); + $this->assertEquals(1234, $entities[1]->getNumber()); + } +} + +/** + * @DoctrineEntity + * @DoctrineInheritanceType("singleTable") + * @DoctrineDiscriminatorColumn(name="discr", type="varchar") + * @DoctrineSubClasses({"Doctrine\Tests\ORM\Functional\ChildEntity"}) + * @DoctrineDiscriminatorValue("parent") + */ +class ParentEntity { + /** + * @DoctrineId + * @DoctrineColumn(type="integer") + * @DoctrineGeneratedValue(strategy="auto") + */ + private $id; + + /** + * @DoctrineColumn(type="varchar") + */ + private $data; + + public function getId() { + return $this->id; + } + + public function getData() { + return $this->data; + } + + public function setData($data) { + $this->data = $data; + } +} + +/** + * @DoctrineEntity + * @DoctrineDiscriminatorValue("child") + */ +class ChildEntity extends ParentEntity { + /** + * @DoctrineColumn(type="integer", nullable=true) + */ + private $number; + + public function getNumber() { + return $this->number; + } + + public function setNumber($number) { + $this->number = $number; + } +} + diff --git a/tests/Doctrine/Tests/ORM/Hydration/ArrayHydratorTest.php b/tests/Doctrine/Tests/ORM/Hydration/ArrayHydratorTest.php index 5f4c18a43..eba448e09 100644 --- a/tests/Doctrine/Tests/ORM/Hydration/ArrayHydratorTest.php +++ b/tests/Doctrine/Tests/ORM/Hydration/ArrayHydratorTest.php @@ -35,7 +35,7 @@ class ArrayHydratorTest extends HydrationTest $stmt = new HydratorMockStatement($resultSet); $hydrator = new \Doctrine\ORM\Internal\Hydration\ArrayHydrator($this->_em); - $result = $hydrator->hydrateAll($stmt, $this->_createParserResult($rsm)); + $result = $hydrator->hydrateAll($stmt, $rsm); $this->assertEquals(2, count($result)); $this->assertTrue(is_array($result)); @@ -78,7 +78,7 @@ class ArrayHydratorTest extends HydrationTest $stmt = new HydratorMockStatement($resultSet); $hydrator = new \Doctrine\ORM\Internal\Hydration\ArrayHydrator($this->_em); - $result = $hydrator->hydrateAll($stmt, $this->_createParserResult($rsm)); + $result = $hydrator->hydrateAll($stmt, $rsm); $this->assertEquals(4, count($result)); @@ -140,7 +140,7 @@ class ArrayHydratorTest extends HydrationTest $stmt = new HydratorMockStatement($resultSet); $hydrator = new \Doctrine\ORM\Internal\Hydration\ArrayHydrator($this->_em); - $result = $hydrator->hydrateAll($stmt, $this->_createParserResult($rsm, true)); + $result = $hydrator->hydrateAll($stmt, $rsm); $this->assertEquals(2, count($result)); $this->assertTrue(is_array($result)); @@ -192,7 +192,7 @@ class ArrayHydratorTest extends HydrationTest $stmt = new HydratorMockStatement($resultSet); $hydrator = new \Doctrine\ORM\Internal\Hydration\ArrayHydrator($this->_em); - $result = $hydrator->hydrateAll($stmt, $this->_createParserResult($rsm, true)); + $result = $hydrator->hydrateAll($stmt, $rsm); $this->assertEquals(2, count($result)); $this->assertTrue(is_array($result)); @@ -255,7 +255,7 @@ class ArrayHydratorTest extends HydrationTest $stmt = new HydratorMockStatement($resultSet); $hydrator = new \Doctrine\ORM\Internal\Hydration\ArrayHydrator($this->_em); - $result = $hydrator->hydrateAll($stmt, $this->_createParserResult($rsm, true)); + $result = $hydrator->hydrateAll($stmt, $rsm); $this->assertEquals(2, count($result)); $this->assertTrue(is_array($result)); @@ -365,7 +365,7 @@ class ArrayHydratorTest extends HydrationTest $stmt = new HydratorMockStatement($resultSet); $hydrator = new \Doctrine\ORM\Internal\Hydration\ArrayHydrator($this->_em); - $result = $hydrator->hydrateAll($stmt, $this->_createParserResult($rsm, true)); + $result = $hydrator->hydrateAll($stmt, $rsm); $this->assertEquals(2, count($result)); $this->assertTrue(is_array($result)); @@ -505,7 +505,7 @@ class ArrayHydratorTest extends HydrationTest $stmt = new HydratorMockStatement($resultSet); $hydrator = new \Doctrine\ORM\Internal\Hydration\ArrayHydrator($this->_em); - $result = $hydrator->hydrateAll($stmt, $this->_createParserResult($rsm, true)); + $result = $hydrator->hydrateAll($stmt, $rsm); $this->assertEquals(2, count($result)); $this->assertTrue(is_array($result)); @@ -618,7 +618,7 @@ class ArrayHydratorTest extends HydrationTest $stmt = new HydratorMockStatement($resultSet); $hydrator = new \Doctrine\ORM\Internal\Hydration\ArrayHydrator($this->_em); - $result = $hydrator->hydrateAll($stmt, $this->_createParserResult($rsm)); + $result = $hydrator->hydrateAll($stmt, $rsm); $this->assertEquals(2, count($result)); $this->assertTrue(is_array($result)); @@ -653,7 +653,7 @@ class ArrayHydratorTest extends HydrationTest $stmt = new HydratorMockStatement($resultSet); $hydrator = new \Doctrine\ORM\Internal\Hydration\ArrayHydrator($this->_em); - $iterableResult = $hydrator->iterate($stmt, $this->_createParserResult($rsm)); + $iterableResult = $hydrator->iterate($stmt, $rsm); $rowNum = 0; while (($row = $iterableResult->next()) !== false) { diff --git a/tests/Doctrine/Tests/ORM/Hydration/ObjectHydratorTest.php b/tests/Doctrine/Tests/ORM/Hydration/ObjectHydratorTest.php index d5712da98..34ef8aea6 100644 --- a/tests/Doctrine/Tests/ORM/Hydration/ObjectHydratorTest.php +++ b/tests/Doctrine/Tests/ORM/Hydration/ObjectHydratorTest.php @@ -35,7 +35,7 @@ class ObjectHydratorTest extends HydrationTest $stmt = new HydratorMockStatement($resultSet); $hydrator = new \Doctrine\ORM\Internal\Hydration\ObjectHydrator($this->_em); - $result = $hydrator->hydrateAll($stmt, $this->_createParserResult($rsm)); + $result = $hydrator->hydrateAll($stmt, $rsm); $this->assertEquals(2, count($result)); $this->assertTrue($result[0] instanceof \Doctrine\Tests\Models\CMS\CmsUser); @@ -79,7 +79,7 @@ class ObjectHydratorTest extends HydrationTest $stmt = new HydratorMockStatement($resultSet); $hydrator = new \Doctrine\ORM\Internal\Hydration\ObjectHydrator($this->_em); - $result = $hydrator->hydrateAll($stmt, $this->_createParserResult($rsm)); + $result = $hydrator->hydrateAll($stmt, $rsm); $this->assertEquals(4, count($result)); @@ -146,7 +146,7 @@ class ObjectHydratorTest extends HydrationTest $stmt = new HydratorMockStatement($resultSet); $hydrator = new \Doctrine\ORM\Internal\Hydration\ObjectHydrator($this->_em); - $result = $hydrator->hydrateAll($stmt, $this->_createParserResult($rsm, true)); + $result = $hydrator->hydrateAll($stmt, $rsm); $this->assertEquals(2, count($result)); $this->assertTrue(is_array($result)); @@ -205,7 +205,7 @@ class ObjectHydratorTest extends HydrationTest $stmt = new HydratorMockStatement($resultSet); $hydrator = new \Doctrine\ORM\Internal\Hydration\ObjectHydrator($this->_em); - $result = $hydrator->hydrateAll($stmt, $this->_createParserResult($rsm, true)); + $result = $hydrator->hydrateAll($stmt, $rsm); $this->assertEquals(2, count($result)); $this->assertTrue(is_array($result)); @@ -270,7 +270,7 @@ class ObjectHydratorTest extends HydrationTest $stmt = new HydratorMockStatement($resultSet); $hydrator = new \Doctrine\ORM\Internal\Hydration\ObjectHydrator($this->_em); - $result = $hydrator->hydrateAll($stmt, $this->_createParserResult($rsm, true)); + $result = $hydrator->hydrateAll($stmt, $rsm); $this->assertEquals(2, count($result)); $this->assertTrue(is_array($result)); @@ -384,7 +384,7 @@ class ObjectHydratorTest extends HydrationTest $stmt = new HydratorMockStatement($resultSet); $hydrator = new \Doctrine\ORM\Internal\Hydration\ObjectHydrator($this->_em); - $result = $hydrator->hydrateAll($stmt, $this->_createParserResult($rsm, true)); + $result = $hydrator->hydrateAll($stmt, $rsm); $this->assertEquals(2, count($result)); $this->assertTrue(is_array($result)); @@ -519,7 +519,7 @@ class ObjectHydratorTest extends HydrationTest $stmt = new HydratorMockStatement($resultSet); $hydrator = new \Doctrine\ORM\Internal\Hydration\ObjectHydrator($this->_em); - $result = $hydrator->hydrateAll($stmt, $this->_createParserResult($rsm, true)); + $result = $hydrator->hydrateAll($stmt, $rsm); $this->assertEquals(2, count($result)); $this->assertTrue(is_array($result)); @@ -626,7 +626,7 @@ class ObjectHydratorTest extends HydrationTest $stmt = new HydratorMockStatement($resultSet); $hydrator = new \Doctrine\ORM\Internal\Hydration\ObjectHydrator($this->_em); - $result = $hydrator->hydrateAll($stmt, $this->_createParserResult($rsm)); + $result = $hydrator->hydrateAll($stmt, $rsm); $this->assertEquals(2, count($result)); $this->assertTrue($result[0] instanceof \Doctrine\Tests\Models\Forum\ForumCategory); @@ -663,7 +663,7 @@ class ObjectHydratorTest extends HydrationTest $stmt = new HydratorMockStatement($resultSet); $hydrator = new \Doctrine\ORM\Internal\Hydration\ObjectHydrator($this->_em); - $iterableResult = $hydrator->iterate($stmt, $this->_createParserResult($rsm)); + $iterableResult = $hydrator->iterate($stmt, $rsm); $rowNum = 0; while (($row = $iterableResult->next()) !== false) { @@ -739,6 +739,6 @@ class ObjectHydratorTest extends HydrationTest $stmt = new HydratorMockStatement($resultSet); $hydrator = new \Doctrine\ORM\Internal\Hydration\ObjectHydrator($this->_em); - $result = $hydrator->hydrateAll($stmt, $this->_createParserResult($rsm, true)); + $result = $hydrator->hydrateAll($stmt, $rsm); } } \ No newline at end of file diff --git a/tests/Doctrine/Tests/ORM/Hydration/ScalarHydratorTest.php b/tests/Doctrine/Tests/ORM/Hydration/ScalarHydratorTest.php index 2d2b22c96..96e755507 100644 --- a/tests/Doctrine/Tests/ORM/Hydration/ScalarHydratorTest.php +++ b/tests/Doctrine/Tests/ORM/Hydration/ScalarHydratorTest.php @@ -35,7 +35,7 @@ class ScalarHydratorTest extends HydrationTest $stmt = new HydratorMockStatement($resultSet); $hydrator = new \Doctrine\ORM\Internal\Hydration\ScalarHydrator($this->_em); - $result = $hydrator->hydrateAll($stmt, $this->_createParserResult($rsm)); + $result = $hydrator->hydrateAll($stmt, $rsm); $this->assertTrue(is_array($result)); $this->assertEquals(2, count($result)); diff --git a/tests/Doctrine/Tests/ORM/Hydration/SingleScalarHydratorTest.php b/tests/Doctrine/Tests/ORM/Hydration/SingleScalarHydratorTest.php index 19d871d91..65ea0a561 100644 --- a/tests/Doctrine/Tests/ORM/Hydration/SingleScalarHydratorTest.php +++ b/tests/Doctrine/Tests/ORM/Hydration/SingleScalarHydratorTest.php @@ -63,14 +63,14 @@ class SingleScalarHydratorTest extends HydrationTest $hydrator = new \Doctrine\ORM\Internal\Hydration\SingleScalarHydrator($this->_em); if ($name == 'result1') { - $result = $hydrator->hydrateAll($stmt, $this->_createParserResult($rsm)); + $result = $hydrator->hydrateAll($stmt, $rsm); $this->assertEquals('romanb', $result); } else if ($name == 'result2') { - $result = $hydrator->hydrateAll($stmt, $this->_createParserResult($rsm)); + $result = $hydrator->hydrateAll($stmt, $rsm); $this->assertEquals(1, $result); } else if ($name == 'result3' || $name == 'result4') { try { - $result = $hydrator->hydrateall($stmt, $this->_createParserResult($rsm)); + $result = $hydrator->hydrateall($stmt, $rsm); $this->fail(); } catch (\Doctrine\ORM\Internal\Hydration\HydrationException $ex) {} }