Commit ee46dba3 authored by romanb's avatar romanb

[2.0] Moved code between Query and AbstractQuery. Added first NativeQuery...

[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.
parent 67ba9661
...@@ -31,8 +31,9 @@ use \ArrayIterator; ...@@ -31,8 +31,9 @@ use \ArrayIterator;
* A Collection is a thin wrapper around a php array. Think of it as an OO version * A Collection is a thin wrapper around a php array. Think of it as an OO version
* of a plain array. * of a plain array.
* *
* @author Roman S. Borschel * @author Roman S. Borschel <roman@code-factory.org>
* @since 2.0 * @since 2.0
* @todo Consider extending ArrayObject
*/ */
class Collection implements Countable, IteratorAggregate, ArrayAccess class Collection implements Countable, IteratorAggregate, ArrayAccess
{ {
......
...@@ -49,9 +49,8 @@ interface Statement ...@@ -49,9 +49,8 @@ interface Statement
public function bindColumn($column, &$param, $type = null); public function bindColumn($column, &$param, $type = null);
/** /**
* bindValue * Binds a value to a corresponding named or positional
* Binds a value to a corresponding named or question mark * placeholder in the SQL statement that was used to prepare the statement.
* placeholder in the SQL statement that was use to prepare the statement.
* *
* @param mixed $param Parameter identifier. For a prepared statement using named placeholders, * @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 * this will be a parameter name of the form :name. For a prepared statement
...@@ -65,7 +64,6 @@ interface Statement ...@@ -65,7 +64,6 @@ interface Statement
public function bindValue($param, $value, $type = null); public function bindValue($param, $value, $type = null);
/** /**
* bindParam
* Binds a PHP variable to a corresponding named or question mark placeholder in the * 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(), * 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 * the variable is bound as a reference and will only be evaluated at the time
......
...@@ -36,8 +36,11 @@ abstract class Type ...@@ -36,8 +36,11 @@ abstract class Type
} }
abstract public function getSqlDeclaration(array $fieldDeclaration, \Doctrine\DBAL\Platforms\AbstractPlatform $platform); abstract public function getSqlDeclaration(array $fieldDeclaration, \Doctrine\DBAL\Platforms\AbstractPlatform $platform);
abstract public function getName(); abstract public function getName();
//abstract public function getTypeCode();
/** /**
* Factory method to create type instances. * Factory method to create type instances.
* Type instances are implemented as flyweights. * Type instances are implemented as flyweights.
......
<?php <?php
/* /*
* $Id: Abstract.php 1393 2008-03-06 17:49:16Z guilhermeblanco $ * $Id: Abstract.php 1393 2008-03-06 17:49:16Z guilhermeblanco $
* *
...@@ -35,78 +34,27 @@ namespace Doctrine\ORM; ...@@ -35,78 +34,27 @@ namespace Doctrine\ORM;
*/ */
abstract class AbstractQuery abstract class AbstractQuery
{ {
/* Hydration mode constants */
/** /**
* QUERY TYPE CONSTANTS * Hydrates an object graph. This is the default behavior.
*/ */
const HYDRATE_OBJECT = 1;
/** /**
* Constant for SELECT queries. * Hydrates an array graph.
*/ */
const SELECT = 0; const HYDRATE_ARRAY = 2;
/** /**
* Constant for DELETE queries. * Hydrates a flat, rectangular result set with scalar values.
*/ */
const DELETE = 1; const HYDRATE_SCALAR = 3;
/** /**
* Constant for UPDATE queries. * Hydrates a single scalar value.
*/ */
const UPDATE = 2; const HYDRATE_SINGLE_SCALAR = 4;
/** /**
* @todo [TODO] Remove these ones (INSERT and CREATE)? * Hydrates nothing.
*/ */
const HYDRATE_NONE = 5;
/**
* Constant for INSERT queries.
*/
//const INSERT = 3;
/**
* Constant for CREATE queries.
*/
//const CREATE = 4;
/**
* 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;
/**
* @todo [TODO] Remove these ones (DIRECT and LOCKED)?
*/
/**
* A query is in DIRECT state when ... ?
*/
//const STATE_DIRECT = 3;
/**
* A query object is on LOCKED state when ... ?
*/
//const STATE_LOCKED = 4;
/**
* @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 $params Parameters of this query. * @var array $params Parameters of this query.
...@@ -121,894 +69,464 @@ abstract class AbstractQuery ...@@ -121,894 +69,464 @@ abstract class AbstractQuery
protected $_enumParams = array(); protected $_enumParams = array();
/** /**
* @var array $_dqlParts An array containing all DQL query parts. * The user-specified ResultSetMapping to use.
* @see Query::free that initializes this property *
* @var ResultSetMapping
*/ */
protected $_dqlParts = array(); protected $_resultSetMapping;
/** /**
* @var string $_dql Cached DQL query. * @var Doctrine\ORM\EntityManager The entity manager used by this query object.
*/ */
protected $_dql = null; protected $_em;
/**
* Frees the resources used by the query object. It especially breaks a
* cyclic reference between the query object and it's parsers. This enables
* PHP's current GC to reclaim the memory.
* This method can therefore be used to reduce memory usage when creating a lot
* of query objects during a request.
*/
public function free()
{
/** /**
* @todo [TODO] What about "forUpdate" support? Remove it? * A set of query hints.
*/
$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->_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 * @var array
*/ */
public function getDql() protected $_hints = array();
{
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)? * @var integer The hydration mode.
*/ */
/* protected $_hydrationMode = self::HYDRATE_OBJECT;
case self::INSERT:
break;
case self::CREATE:
break;
*/
case self::SELECT:
default:
$dql = $this->_getDqlForSelect();
break;
}
return $dql;
}
/** /**
* Builds the DQL of DELETE * The locally set cache driver used for caching result sets of this query.
*/
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
* *
* @var CacheDriver
*/ */
return 'DELETE' protected $_resultCache;
. $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 * @var boolean Boolean value that indicates whether or not expire the result cache.
*/ */
protected function _getDqlForUpdate() protected $_expireResultCache = false;
{
/*
* 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 * @var int Result Cache lifetime.
*/
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' protected $_resultCacheTTL;
. (($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 * Initializes a new instance of a class derived from <tt>AbstractQuery</tt>.
*
* @param Doctrine\ORM\EntityManager $entityManager
*/ */
protected function _getReducedDqlQueryPart($queryPartName, $options = array()) public function __construct(EntityManager $entityManager)
{ {
if (empty($this->_dqlParts[$queryPartName])) { $this->_em = $entityManager;
return (isset($options['empty']) ? $options['empty'] : ''); $this->free();
}
$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 * Retrieves the associated EntityManager of this Query instance.
* 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 * @return Doctrine\ORM\EntityManager
*/ */
public function getType() public function getEntityManager()
{ {
return $this->_type; return $this->_em;
} }
/** /**
* Returns the state of this query object * Frees the resources used by the query object. It especially breaks a
* By default the type is Doctrine_ORM_Query_Abstract::STATE_CLEAN but if it appears any unprocessed DQL * cyclic reference between the query object and it's parsers. This enables
* part, it is switched to Doctrine_ORM_Query_Abstract::STATE_DIRTY. * PHP's current GC to reclaim the memory.
* * This method can therefore be used to reduce memory usage when creating a lot
* @see Doctrine_ORM_Query_Abstract::STATE_CLEAN * of query objects during a request.
* @see Doctrine_ORM_Query_Abstract::STATE_DIRTY
*
* @return integer Return the query state
*/ */
public function getState() public function free()
{ {
return $this->_state; $this->_params = array();
$this->_enumParams = array();
} }
/** /**
* Adds fields to the SELECT part of the query * Set enumerated parameters
* *
* @param string $select Query SELECT part * @param array $enumParams Enum parameters.
* @return Doctrine_ORM_Query
*/ */
public function select($select = '', $override = false) protected function _setEnumParams($enumParams = array())
{ {
if ($select === '') { $this->_enumParams = $enumParams;
return $this;
}
return $this->_addDqlQueryPart('select', $select, ! $override);
} }
/** /**
* Makes the query SELECT DISTINCT. * Get all enumerated parameters
* *
* @param bool $flag Whether or not the SELECT is DISTINCT (default true). * @return array All enumerated parameters
* @return Doctrine_ORM_Query
*/ */
public function distinct($flag = true) public function getEnumParams()
{ {
$this->_dqlParts['distinct'] = (bool) $flag; return $this->_enumParams;
return $this;
} }
/** /**
* Makes the query SELECT FOR UPDATE. * Convert ENUM parameters to their integer equivalents
*
* @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? * @param $params Parameters to be converted
* @return array Converted parameters array
*/ */
public function forUpdate($flag = true) public function convertEnums($params)
{ {
return $this->_addDqlQueryPart('forUpdate', (bool) $flag); foreach ($this->_enumParams as $key => $values) {
if (isset($params[$key]) && ! empty($values)) {
$params[$key] = $values[0]->enumIndex($values[1], $params[$key]);
}
} }
return $params;
}
/** /**
* Sets the query type to DELETE * Get all defined parameters
* *
* @return Doctrine_ORM_Query * @return array Defined parameters
*/ */
public function delete() public function getParams($params = array())
{ {
$this->_type = self::DELETE; return array_merge($this->_params, $params);
return $this;
} }
/** /**
* Sets the UPDATE part of the query * setParams
* *
* @param string $update Query UPDATE part * @param array $params
* @return Doctrine_ORM_Query
*/ */
public function update($update) public function setParams(array $params = array()) {
{ $this->_params = $params;
$this->_type = self::UPDATE;
return $this->_addDqlQueryPart('from', $update);
} }
/** /**
* Sets the SET part of the query * Gets the SQL query that corresponds to this query object.
* The returned SQL syntax depends on the connection driver that is used
* by this query object at the time of this method call.
* *
* @param mixed $key UPDATE keys. Accepts either a string (requiring then $value or $params to be defined) * @return string SQL query
* 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) abstract public function getSql();
{
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 * Sets a query parameter.
* *
* @param string $from Query FROM part * @param string|integer $key
* @return Doctrine_ORM_Query * @param mixed $value
*/ */
public function from($from, $override = false) public function setParameter($key, $value)
{ {
return $this->_addDqlQueryPart('from', $from, ! $override); $this->_params[$key] = $value;
} }
/** /**
* Appends an INNER JOIN to the FROM part of the query * Sets a collection of query parameters.
* *
* @param string $join Query INNER JOIN * @param array $params
* @param mixed $params Optional JOIN params (array of parameters or a simple scalar)
* @return Doctrine_ORM_Query
*/ */
public function innerJoin($join, $params = array()) public function setParameters(array $params)
{ {
if (is_array($params)) { foreach ($params as $key => $value) {
$this->_params['join'] = array_merge($this->_params['join'], $params); $this->setParameter($key, $value);
} else {
$this->_params['join'][] = $params;
} }
return $this->_addDqlQueryPart('from', 'INNER JOIN ' . $join, true);
} }
/** /**
* Appends an INNER JOIN to the FROM part of the query * Sets the ResultSetMapping that should be used for hydration.
* *
* @param string $join Query INNER JOIN * @param ResultSetMapping $rsm
* @param mixed $params Optional JOIN params (array of parameters or a simple scalar)
* @return Doctrine_ORM_Query
*/ */
public function join($join, $params = array()) public function setResultSetMapping($rsm)
{ {
return $this->innerJoin($join, $params); $this->_resultSetMapping = $rsm;
} }
/** /**
* Appends a LEFT JOIN to the FROM part of the query * Defines a cache driver to be used for caching result sets.
* *
* @param string $join Query LEFT JOIN * @param Doctrine\ORM\Cache\Cache $driver Cache driver
* @param mixed $params Optional JOIN params (array of parameters or a simple scalar) * @return Doctrine\ORM\Query
* @return Doctrine_ORM_Query
*/ */
public function leftJoin($join, $params = array()) public function setResultCache($resultCache = null)
{ {
if (is_array($params)) { if ($resultCache !== null && ! ($resultCache instanceof \Doctrine\ORM\Cache\Cache)) {
$this->_params['join'] = array_merge($this->_params['join'], $params); throw DoctrineException::updateMe(
} else { 'Method setResultCache() accepts only an instance of Doctrine_Cache_Interface or null.'
$this->_params['join'][] = $params; );
} }
$this->_resultCache = $resultCache;
return $this->_addDqlQueryPart('from', 'LEFT JOIN ' . $join, true); return $this;
} }
/** /**
* Adds conditions to the WHERE part of the query * Returns the cache driver used for caching result sets.
* *
* @param string $where Query WHERE part * @return Doctrine_Cache_Interface Cache driver
* @param mixed $params An array of parameters or a simple scalar
* @return Doctrine_ORM_Query
*/ */
public function where($where, $params = array(), $override = false) public function getResultCache()
{ {
if ($override) { if ($this->_resultCache instanceof \Doctrine\ORM\Cache\Cache) {
$this->_params['where'] = array(); return $this->_resultCache;
}
if (is_array($params)) {
$this->_params['where'] = array_merge($this->_params['where'], $params);
} else { } else {
$this->_params['where'][] = $params; return $this->_em->getConfiguration()->getResultCacheImpl();
} }
return $this->_addDqlQueryPart('where', $where, ! $override);
} }
/** /**
* Adds conditions to the WHERE part of the query * Defines how long the result cache will be active before expire.
* *
* @param string $where Query WHERE part * @param integer $timeToLive How long the cache entry is valid
* @param mixed $params An array of parameters or a simple scalar * @return Doctrine\ORM\Query
* @return Doctrine_ORM_Query
*/ */
public function andWhere($where, $params = array(), $override = false) public function setResultCacheLifetime($timeToLive)
{ {
if (count($this->getDqlQueryPart('where')) > 0) { if ($timeToLive !== null) {
$this->_addDqlQueryPart('where', 'AND', true); $timeToLive = (int) $timeToLive;
} }
return $this->where($where, $params, $override); $this->_resultCacheTTL = $timeToLive;
}
return $this;
}
/** /**
* Adds conditions to the WHERE part of the query * Retrieves the lifetime of resultset cache.
* *
* @param string $where Query WHERE part * @return int
* @param mixed $params An array of parameters or a simple scalar
* @return Doctrine_ORM_Query
*/ */
public function orWhere($where, $params = array(), $override = false) public function getResultCacheLifetime()
{ {
if (count($this->getDqlQueryPart('where')) > 0) { return $this->_resultCacheTTL;
$this->_addDqlQueryPart('where', 'OR', true);
}
return $this->where($where, $params, $override);
} }
/** /**
* Adds IN condition to the query WHERE part * Defines if the resultset cache is active or not.
* *
* @param string $expr The operand of the IN * @param boolean $expire Whether or not to force resultset cache expiration.
* @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 * @return Doctrine_ORM_Query
*/ */
public function whereIn($expr, $params = array(), $override = false, $not = false) public function setExpireResultCache($expire = true)
{ {
$params = (array) $params; $this->_expireResultCache = (bool) $expire;
// Must have at least one param, otherwise we'll get an empty IN () => invalid SQL
if ( ! count($params)) {
return $this; 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 * Retrieves if the resultset cache is active or not.
* *
* @param string $expr The operand of the NOT IN * @return bool
* @param mixed $params An array of parameters or a simple scalar
* @return Doctrine_ORM_Query
*/ */
public function whereNotIn($expr, $params = array(), $override = false) public function getExpireResultCache()
{ {
return $this->whereIn($expr, $params, $override, true); return $this->_expireResultCache;
} }
/** /**
* Adds IN condition to the query WHERE part * Defines the processing mode to be used during hydration process.
* *
* @param string $expr The operand of the IN * @param integer $hydrationMode Doctrine processing mode to be used during hydration process.
* @param mixed $params An array of parameters or a simple scalar * One of the Query::HYDRATE_* constants.
* @param boolean $not Whether or not to use NOT in front of IN * @return Doctrine\ORM\Query
* @return Doctrine_ORM_Query
*/ */
public function andWhereIn($expr, $params = array(), $override = false) public function setHydrationMode($hydrationMode)
{ {
if (count($this->getDqlQueryPart('where')) > 0) { $this->_hydrationMode = $hydrationMode;
$this->_addDqlQueryPart('where', 'AND', true); return $this;
}
return $this->whereIn($expr, $params, $override);
} }
/** /**
* Adds NOT IN condition to the query WHERE part * Gets the hydration mode currently used by the query.
* *
* @param string $expr The operand of the NOT IN * @return integer
* @param mixed $params An array of parameters or a simple scalar
* @return Doctrine_ORM_Query
*/ */
public function andWhereNotIn($expr, $params = array(), $override = false) public function getHydrationMode()
{ {
if (count($this->getDqlQueryPart('where')) > 0) { return $this->_hydrationMode;
$this->_addDqlQueryPart('where', 'AND', true);
}
return $this->whereIn($expr, $params, $override, true);
} }
/** /**
* Adds IN condition to the query WHERE part * Gets the list of results for the query.
* *
* @param string $expr The operand of the IN * Alias for execute(array(), HYDRATE_OBJECT).
* @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 * @return Collection
* @param mixed $params An array of parameters or a simple scalar
* @return Doctrine_ORM_Query
*/ */
public function orWhereNotIn($expr, $params = array(), $override = false) public function getResultList()
{ {
if (count($this->getDqlQueryPart('where')) > 0) { return $this->execute(array(), self::HYDRATE_OBJECT);
$this->_addDqlQueryPart('where', 'OR', true);
} }
return $this->whereIn($expr, $params, $override, true);
}
/** /**
* Adds fields to the GROUP BY part of the query * Gets the array of results for the query.
* *
* @param string $groupby Query GROUP BY part * Alias for execute(array(), HYDRATE_ARRAY).
* @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 * @return array
* @param mixed $params An array of parameters or a simple scalar
* @return Doctrine_ORM_Query
*/ */
public function having($having, $params = array(), $override = false) public function getResultArray()
{ {
if ($override) { return $this->execute(array(), self::HYDRATE_ARRAY);
$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 * Gets the scalar results for the query.
* *
* @param string $having Query HAVING part * Alias for execute(array(), HYDRATE_SCALAR).
* @param mixed $params An array of parameters or a simple scalar *
* @return Doctrine_ORM_Query * @return array
*/ */
public function andHaving($having, $params = array(), $override = false) public function getScalarResult()
{ {
if (count($this->getDqlQueryPart('having')) > 0) { return $this->execute(array(), self::HYDRATE_SCALAR);
$this->_addDqlQueryPart('having', 'AND', true);
} }
return $this->having($having, $params, $override);
}
/** /**
* Adds conditions to the HAVING part of the query * Gets the single result of the query.
* Enforces the uniqueness of the result. If the result is not unique,
* a QueryException is thrown.
* *
* @param string $having Query HAVING part * @param integer $hydrationMode
* @param mixed $params An array of parameters or a simple scalar * @return mixed
* @return Doctrine_ORM_Query * @throws QueryException If the query result is not unique.
*/ */
public function orHaving($having, $params = array(), $override = false) public function getSingleResult($hydrationMode = null)
{ {
if (count($this->getDqlQueryPart('having')) > 0) { $result = $this->execute(array(), $hydrationMode);
$this->_addDqlQueryPart('having', 'OR', true); if (is_array($result)) {
if (count($result) > 1) {
throw QueryException::nonUniqueResult();
} }
return array_shift($result);
return $this->having($having, $params, $override); } else if (is_object($result)) {
if (count($result) > 1) {
throw QueryException::nonUniqueResult();
} }
return $result->getFirst();
/**
* 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);
} }
return $result;
/**
* 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 * Gets the single scalar result of the query.
* *
* @param integer $offset Offset to be used for paginating the query * Alias for getSingleResult(HYDRATE_SINGLE_SCALAR).
* @return Doctrine_ORM_Query
*/
public function offset($offset)
{
return $this->_addDqlQueryPart('offset', $offset);
}
/**
* Set enumerated parameters
* *
* @param array $enumParams Enum parameters. * @return mixed
* @throws QueryException If the query result is not unique.
*/ */
protected function _setEnumParams($enumParams = array()) public function getSingleScalarResult()
{ {
$this->_enumParams = $enumParams; return $this->getSingleResult(self::HYDRATE_SINGLE_SCALAR);
} }
/** /**
* Get all enumerated parameters * Sets an implementation-specific hint. If the hint name is not recognized,
* it is silently ignored.
* *
* @return array All enumerated parameters * @param string $name The name of the hint.
* @param mixed $value The value of the hint.
*/ */
public function getEnumParams() public function setHint($name, $value)
{ {
return $this->_enumParams; $this->_hints[$name] = $value;
} }
/** /**
* Convert ENUM parameters to their integer equivalents * Gets an implementation-specific hint. If the hint name is not recognized,
* FALSE is returned.
* *
* @param $params Parameters to be converted * @param string $name The name of the hint.
* @return array Converted parameters array * @return mixed The value of the hint or FALSe, if the hint name is not recognized.
*/ */
public function convertEnums($params) public function getHint($name)
{ {
foreach ($this->_enumParams as $key => $values) { return isset($this->_hints[$name]) ? $this->_hints[$name] : false;
if (isset($params[$key]) && ! empty($values)) {
$params[$key] = $values[0]->enumIndex($values[1], $params[$key]);
}
}
return $params;
} }
/** /**
* Get all defined parameters * Executes the query and returns an IterableResult that can be used to incrementally
* iterated over the result.
* *
* @return array Defined parameters * @param array $params The query parameters.
* @param integer $hydrationMode The hydratio mode to use.
* @return IterableResult
*/ */
public function getParams($params = array()) public function iterate(array $params = array(), $hydrationMode = self::HYDRATE_OBJECT)
{ {
return array_merge( return $this->_em->getHydrator($this->_hydrationMode)->iterate(
$this->_params['join'], $this->_execute($params, $hydrationMode), $this->_resultSetMapping
$this->_params['set'],
$this->_params['where'],
$this->_params['having'],
$params
); );
} }
/** /**
* setParams * Executes the query.
* *
* @param array $params * @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 setParams(array $params = array()) { public function execute($params = array(), $hydrationMode = null)
$this->_params = $params; {
if ($this->_em->getUnitOfWork()->hasPendingInsertions()) {
$this->_em->flush();
} }
if ($hydrationMode !== null) {
/** $this->_hydrationMode = $hydrationMode;
* 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;
} }
$params = $this->getParams($params);
/** // Check result cache (Only for SELECT queries)
* Retrieve a DQL part for internal purposes if ($this->_resultCache && $this->_type === self::SELECT) {
* $cacheDriver = $this->getResultCacheDriver();
* @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]; // 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;
* 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 { } else {
$this->_dqlParts[$queryPartName] = array($queryPart); // Cache hit.
} $queryResult = CacheHandler::fromCachedResult($this, $cached);
$this->_state = Doctrine_ORM_Query::STATE_DIRTY; return $queryResult->getResultSet();
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')),
);
} }
$stmt = $this->_doExecute($params);
/** if (is_integer($stmt)) {
* @nodoc return $stmt;
*/
protected function _processWhereInSqlPart($value)
{
// [TODO] Add support to imbricated query (must deliver the hardest effort to Parser)
return ($value instanceof Doctrine_Expression) ? $value->getSql() : '?';
} }
return $this->_em->getHydrator($this->_hydrationMode)->hydrateAll($stmt, $this->_resultSetMapping);
/**
* @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 * @nodoc
*
* @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) protected function _prepareParams(array $params)
{ {
// Parameters inclusion // Convert boolean params
$this->_params['where'] = $override ? $params : array_merge($this->_params['where'], $params); $params = $this->_em->getConnection()->getDatabasePlatform()->convertBooleans($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
* by this query object at the time of this method call.
*
* @return string SQL query
*/
abstract public function getSql();
/** // Convert enum params
* Sets a query parameter. return $this->convertEnums($params);
*
* @param string|integer $key
* @param mixed $value
*/
public function setParameter($key, $value)
{
$this->_params[$key] = $value;
} }
/** /**
* Sets a collection of query parameters. * Executes the query and returns a reference to the resulting Statement object.
* *
* @param array $params * @param <type> $params
*/ */
public function setParameters(array $params) abstract protected function _doExecute(array $params);
{
foreach ($params as $key => $value) {
$this->setParameter($key, $value);
}
}
} }
...@@ -223,7 +223,7 @@ class EntityManager ...@@ -223,7 +223,7 @@ class EntityManager
} }
/** /**
* Creates a query with the specified name. * Creates a DQL query with the specified name.
* *
* @todo Implementation. * @todo Implementation.
* @throws DoctrineException If there is no query registered with the given name. * @throws DoctrineException If there is no query registered with the given name.
...@@ -234,11 +234,17 @@ class EntityManager ...@@ -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;
} }
/** /**
......
...@@ -24,7 +24,8 @@ namespace Doctrine\ORM\Internal\Hydration; ...@@ -24,7 +24,8 @@ namespace Doctrine\ORM\Internal\Hydration;
use \PDO; 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 * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org * @link www.doctrine-project.org
...@@ -50,11 +51,8 @@ abstract class AbstractHydrator ...@@ -50,11 +51,8 @@ abstract class AbstractHydrator
/** @var Statement The statement that provides the data to hydrate. */ /** @var Statement The statement that provides the data to hydrate. */
protected $_stmt; 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 <tt>AbstractHydrator</tt>.
* *
* @param Doctrine\ORM\EntityManager $em The EntityManager to use. * @param Doctrine\ORM\EntityManager $em The EntityManager to use.
*/ */
...@@ -68,13 +66,14 @@ abstract class AbstractHydrator ...@@ -68,13 +66,14 @@ abstract class AbstractHydrator
* Initiates a row-by-row hydration. * Initiates a row-by-row hydration.
* *
* @param object $stmt * @param object $stmt
* @param object $parserResult * @param object $resultSetMapping
* @return IterableResult * @return IterableResult
*/ */
public function iterate($stmt, $parserResult) public function iterate($stmt, $resultSetMapping)
{ {
$this->_stmt = $stmt; $this->_stmt = $stmt;
$this->_prepare($parserResult); $this->_resultSetMapping = $resultSetMapping;
$this->_prepare();
return new IterableResult($this); return new IterableResult($this);
} }
...@@ -82,13 +81,14 @@ abstract class AbstractHydrator ...@@ -82,13 +81,14 @@ abstract class AbstractHydrator
* Hydrates all rows returned by the passed statement instance at once. * Hydrates all rows returned by the passed statement instance at once.
* *
* @param object $stmt * @param object $stmt
* @param object $parserResult * @param object $resultSetMapping
* @return mixed * @return mixed
*/ */
public function hydrateAll($stmt, $parserResult) public function hydrateAll($stmt, $resultSetMapping)
{ {
$this->_stmt = $stmt; $this->_stmt = $stmt;
$this->_prepare($parserResult); $this->_resultSetMapping = $resultSetMapping;
$this->_prepare();
$result = $this->_hydrateAll(); $result = $this->_hydrateAll();
$this->_cleanup(); $this->_cleanup();
return $result; return $result;
...@@ -115,14 +115,9 @@ abstract class AbstractHydrator ...@@ -115,14 +115,9 @@ abstract class AbstractHydrator
/** /**
* Excutes one-time preparation tasks once each time hydration is started * Excutes one-time preparation tasks once each time hydration is started
* through {@link hydrateAll} or {@link iterate()}. * through {@link hydrateAll} or {@link iterate()}.
*
* @param object $parserResult
*/ */
protected function _prepare($parserResult) protected function _prepare()
{ {}
$this->_resultSetMapping = $parserResult->getResultSetMapping();
$this->_parserResult = $parserResult;
}
/** /**
* Excutes one-time cleanup tasks at the end of a hydration that was initiated * Excutes one-time cleanup tasks at the end of a hydration that was initiated
...@@ -131,7 +126,6 @@ abstract class AbstractHydrator ...@@ -131,7 +126,6 @@ abstract class AbstractHydrator
protected function _cleanup() protected function _cleanup()
{ {
$this->_resultSetMapping = null; $this->_resultSetMapping = null;
$this->_parserResult = null;
$this->_stmt->closeCursor(); $this->_stmt->closeCursor();
$this->_stmt = null; $this->_stmt = null;
} }
...@@ -152,8 +146,6 @@ abstract class AbstractHydrator ...@@ -152,8 +146,6 @@ abstract class AbstractHydrator
/** /**
* Hydrates all rows from the current statement instance at once. * Hydrates all rows from the current statement instance at once.
*
* @param object $parserResult
*/ */
abstract protected function _hydrateAll(); abstract protected function _hydrateAll();
...@@ -180,21 +172,26 @@ abstract class AbstractHydrator ...@@ -180,21 +172,26 @@ abstract class AbstractHydrator
foreach ($data as $key => $value) { foreach ($data as $key => $value) {
// Parse each column name only once. Cache the results. // Parse each column name only once. Cache the results.
if ( ! isset($cache[$key])) { if ( ! isset($cache[$key])) {
if ($this->_isIgnoredName($key)) continue; if ($this->_resultSetMapping->isIgnoredColumn($key)) {
$cache[$key] = false;
if ($this->_resultSetMapping->isScalarResult($key)) { } else if ($this->_resultSetMapping->isScalarResult($key)) {
$cache[$key]['fieldName'] = $this->_resultSetMapping->getScalarAlias($key); $cache[$key]['fieldName'] = $this->_resultSetMapping->getScalarAlias($key);
$cache[$key]['isScalar'] = true; $cache[$key]['isScalar'] = true;
} else { } else if ($this->_resultSetMapping->isFieldResult($key)) {
$classMetadata = $this->_resultSetMapping->getOwningClass($key); $classMetadata = $this->_resultSetMapping->getOwningClass($key);
$fieldName = $this->_resultSetMapping->getFieldName($key); $fieldName = $this->_resultSetMapping->getFieldName($key);
$classMetadata = $this->_lookupDeclaringClass($classMetadata, $fieldName); $classMetadata = $this->_lookupDeclaringClass($classMetadata, $fieldName);
//$fieldName = $classMetadata->getFieldNameForLowerColumnName($columnName);
$cache[$key]['fieldName'] = $fieldName; $cache[$key]['fieldName'] = $fieldName;
$cache[$key]['isScalar'] = false; $cache[$key]['isScalar'] = false;
$cache[$key]['type'] = $classMetadata->getTypeOfField($fieldName); $cache[$key]['type'] = $classMetadata->getTypeOfField($fieldName);
$cache[$key]['isIdentifier'] = $classMetadata->isIdentifier($fieldName); $cache[$key]['isIdentifier'] = $classMetadata->isIdentifier($fieldName);
$cache[$key]['dqlAlias'] = $this->_resultSetMapping->getEntityAlias($key); $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 ...@@ -207,6 +204,11 @@ abstract class AbstractHydrator
$dqlAlias = $cache[$key]['dqlAlias']; $dqlAlias = $cache[$key]['dqlAlias'];
if (isset($cache[$key]['isDiscriminator'])) {
$rowData[$dqlAlias][$fieldName] = $value;
continue;
}
if ($cache[$key]['isIdentifier']) { if ($cache[$key]['isIdentifier']) {
$id[$dqlAlias] .= '|' . $value; $id[$dqlAlias] .= '|' . $value;
} }
...@@ -243,9 +245,10 @@ abstract class AbstractHydrator ...@@ -243,9 +245,10 @@ abstract class AbstractHydrator
foreach ($data as $key => $value) { foreach ($data as $key => $value) {
// Parse each column name only once. Cache the results. // Parse each column name only once. Cache the results.
if ( ! isset($cache[$key])) { if ( ! isset($cache[$key])) {
if ($this->_isIgnoredName($key)) continue; if ($this->_resultSetMapping->isIgnoredColumn($key)) {
$cache[$key] = false;
if ($this->_resultSetMapping->isScalarResult($key)) { continue;
} else if ($this->_resultSetMapping->isScalarResult($key)) {
$cache[$key]['fieldName'] = $this->_resultSetMapping->getScalarAlias($key); $cache[$key]['fieldName'] = $this->_resultSetMapping->getScalarAlias($key);
$cache[$key]['isScalar'] = true; $cache[$key]['isScalar'] = true;
} else { } else {
...@@ -285,19 +288,6 @@ abstract class AbstractHydrator ...@@ -285,19 +288,6 @@ abstract class AbstractHydrator
$this->_resultSetMapping->getIndexByField($alias) : null; $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. * Looks up the field name for a (lowercased) column name.
* *
...@@ -319,14 +309,12 @@ abstract class AbstractHydrator ...@@ -319,14 +309,12 @@ abstract class AbstractHydrator
private function _lookupDeclaringClass($class, $fieldName) private function _lookupDeclaringClass($class, $fieldName)
{ {
if ($class->hasField($fieldName)) { if ($class->hasField($fieldName)) {
//return $class->getFieldNameForLowerColumnName($lcColumnName);
return $class; return $class;
} }
foreach ($class->getSubclasses() as $subClass) { foreach ($class->getSubclasses() as $subClass) {
$subClassMetadata = $this->_em->getClassMetadata($subClass); $subClassMetadata = $this->_em->getClassMetadata($subClass);
if ($subClassMetadata->hasField($fieldName)) { if ($subClassMetadata->hasField($fieldName)) {
//return $subClassMetadata->getFieldNameForLowerColumnName($lcColumnName);
return $subClassMetadata; return $subClassMetadata;
} }
} }
......
...@@ -38,9 +38,8 @@ class ArrayHydrator extends AbstractHydrator ...@@ -38,9 +38,8 @@ class ArrayHydrator extends AbstractHydrator
private $_resultCounter = 0; private $_resultCounter = 0;
/** @override */ /** @override */
protected function _prepare($parserResult) protected function _prepare()
{ {
parent::_prepare($parserResult);
$this->_isSimpleQuery = $this->_resultSetMapping->getEntityResultCount() <= 1; $this->_isSimpleQuery = $this->_resultSetMapping->getEntityResultCount() <= 1;
$this->_identifierMap = array(); $this->_identifierMap = array();
$this->_resultPointers = array(); $this->_resultPointers = array();
...@@ -98,7 +97,7 @@ class ArrayHydrator extends AbstractHydrator ...@@ -98,7 +97,7 @@ class ArrayHydrator extends AbstractHydrator
// Get a reference to the right element in the result tree. // Get a reference to the right element in the result tree.
// This element will get the associated element attached. // 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)); $key = key(reset($this->_resultPointers));
// TODO: Exception if $key === null ? // TODO: Exception if $key === null ?
$baseElement =& $this->_resultPointers[$parent][$key]; $baseElement =& $this->_resultPointers[$parent][$key];
...@@ -155,14 +154,14 @@ class ArrayHydrator extends AbstractHydrator ...@@ -155,14 +154,14 @@ class ArrayHydrator extends AbstractHydrator
if ($this->_isSimpleQuery || ! isset($this->_identifierMap[$dqlAlias][$id[$dqlAlias]])) { if ($this->_isSimpleQuery || ! isset($this->_identifierMap[$dqlAlias][$id[$dqlAlias]])) {
$element = $rowData[$dqlAlias]; $element = $rowData[$dqlAlias];
if ($field = $this->_getCustomIndexField($dqlAlias)) { if ($field = $this->_getCustomIndexField($dqlAlias)) {
if ($this->_parserResult->isMixedQuery()) { if ($this->_resultSetMapping->isMixedResult()) {
$result[] = array($element[$field] => $element); $result[] = array($element[$field] => $element);
++$this->_resultCounter; ++$this->_resultCounter;
} else { } else {
$result[$element[$field]] = $element; $result[$element[$field]] = $element;
} }
} else { } else {
if ($this->_parserResult->isMixedQuery()) { if ($this->_resultSetMapping->isMixedResult()) {
$result[] = array($element); $result[] = array($element);
++$this->_resultCounter; ++$this->_resultCounter;
} else { } else {
......
...@@ -33,22 +33,24 @@ use Doctrine\Common\Collections\Collection; ...@@ -33,22 +33,24 @@ use Doctrine\Common\Collections\Collection;
*/ */
class ObjectHydrator extends AbstractHydrator class ObjectHydrator extends AbstractHydrator
{ {
/* TOOD: Consider unifying _collections and _initializedRelations */
/** Collections initialized by the hydrator */ /** Collections initialized by the hydrator */
private $_collections = array(); private $_collections = array();
/** Memory for initialized relations */ /** Memory for initialized relations */
private $_initializedRelations = array(); private $_initializedRelations = array();
private $_metadataMap = array();
private $_classMetadatas = array();
private $_rootAliases = array(); private $_rootAliases = array();
private $_isSimpleQuery = false; private $_isSimpleQuery = false;
private $_identifierMap = array(); private $_identifierMap = array();
private $_resultPointers = array(); private $_resultPointers = array();
private $_idTemplate = array(); private $_idTemplate = array();
private $_resultCounter = 0; private $_resultCounter = 0;
private $_discriminatorMap = array();
/** @override */ /** @override */
protected function _prepare($parserResult) protected function _prepare()
{ {
parent::_prepare($parserResult);
$this->_isSimpleQuery = $this->_resultSetMapping->getEntityResultCount() <= 1; $this->_isSimpleQuery = $this->_resultSetMapping->getEntityResultCount() <= 1;
$this->_identifierMap = array(); $this->_identifierMap = array();
$this->_resultPointers = array(); $this->_resultPointers = array();
...@@ -58,6 +60,14 @@ class ObjectHydrator extends AbstractHydrator ...@@ -58,6 +60,14 @@ class ObjectHydrator extends AbstractHydrator
$this->_identifierMap[$dqlAlias] = array(); $this->_identifierMap[$dqlAlias] = array();
$this->_resultPointers[$dqlAlias] = array(); $this->_resultPointers[$dqlAlias] = array();
$this->_idTemplate[$dqlAlias] = ''; $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 ...@@ -70,7 +80,7 @@ class ObjectHydrator extends AbstractHydrator
{ {
$s = microtime(true); $s = microtime(true);
if ($this->_parserResult->isMixedQuery()) { if ($this->_resultSetMapping->isMixedResult()) {
$result = array(); $result = array();
} else { } else {
$result = new Collection; $result = new Collection;
...@@ -90,7 +100,7 @@ class ObjectHydrator extends AbstractHydrator ...@@ -90,7 +100,7 @@ class ObjectHydrator extends AbstractHydrator
// Clean up // Clean up
$this->_collections = array(); $this->_collections = array();
$this->_initializedRelations = array(); $this->_initializedRelations = array();
$this->_metadataMap = array(); $this->_classMetadatas = array();
$e = microtime(true); $e = microtime(true);
echo 'Hydration took: ' . ($e - $s) . PHP_EOL; echo 'Hydration took: ' . ($e - $s) . PHP_EOL;
...@@ -108,7 +118,7 @@ class ObjectHydrator extends AbstractHydrator ...@@ -108,7 +118,7 @@ class ObjectHydrator extends AbstractHydrator
* @param string $dqlAlias * @param string $dqlAlias
* @param boolean $oneToOne Whether it is a single-valued association or not. * @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) { if ($coll === null) {
unset($this->_resultPointers[$dqlAlias]); // Ticket #1228 unset($this->_resultPointers[$dqlAlias]); // Ticket #1228
...@@ -132,6 +142,12 @@ class ObjectHydrator extends AbstractHydrator ...@@ -132,6 +142,12 @@ class ObjectHydrator extends AbstractHydrator
} }
} }
/**
*
* @param <type> $component
* @return <type>
* @todo Consider inlining this method.
*/
private function getCollection($component) private function getCollection($component)
{ {
$coll = new PersistentCollection($this->_em, $component); $coll = new PersistentCollection($this->_em, $component);
...@@ -139,10 +155,16 @@ class ObjectHydrator extends AbstractHydrator ...@@ -139,10 +155,16 @@ class ObjectHydrator extends AbstractHydrator
return $coll; return $coll;
} }
/**
*
* @param <type> $entity
* @param <type> $name
* @todo Consider inlining this method.
*/
private function initRelatedCollection($entity, $name) private function initRelatedCollection($entity, $name)
{ {
$oid = spl_object_hash($entity); $oid = spl_object_hash($entity);
$classMetadata = $this->_metadataMap[$oid]; $classMetadata = $this->_classMetadatas[get_class($entity)];
if ( ! isset($this->_initializedRelations[$oid][$name])) { if ( ! isset($this->_initializedRelations[$oid][$name])) {
$relation = $classMetadata->getAssociationMapping($name); $relation = $classMetadata->getAssociationMapping($name);
$relatedClass = $this->_em->getClassMetadata($relation->getTargetEntityName()); $relatedClass = $this->_em->getClassMetadata($relation->getTargetEntityName());
...@@ -157,7 +179,7 @@ class ObjectHydrator extends AbstractHydrator ...@@ -157,7 +179,7 @@ class ObjectHydrator extends AbstractHydrator
private function isIndexKeyInUse($entity, $assocField, $indexField) private function isIndexKeyInUse($entity, $assocField, $indexField)
{ {
return $this->_metadataMap[spl_object_hash($entity)] return $this->_classMetadatas[get_class($entity)]
->getReflectionProperty($assocField) ->getReflectionProperty($assocField)
->getValue($entity) ->getValue($entity)
->containsKey($indexField); ->containsKey($indexField);
...@@ -178,9 +200,11 @@ class ObjectHydrator extends AbstractHydrator ...@@ -178,9 +200,11 @@ class ObjectHydrator extends AbstractHydrator
private function getEntity(array $data, $className) 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); $entity = $this->_uow->createEntity($className, $data);
$oid = spl_object_hash($entity);
$this->_metadataMap[$oid] = $this->_em->getClassMetadata($className);
return $entity; return $entity;
} }
...@@ -191,11 +215,13 @@ class ObjectHydrator extends AbstractHydrator ...@@ -191,11 +215,13 @@ class ObjectHydrator extends AbstractHydrator
* @param <type> $property * @param <type> $property
* @param <type> $entity2 * @param <type> $entity2
* @param <type> $indexField * @param <type> $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) private function addRelatedIndexedEntity($entity1, $property, $entity2, $indexField)
{ {
$classMetadata1 = $this->_metadataMap[spl_object_hash($entity1)]; $classMetadata1 = $this->_classMetadatas[get_class($entity1)];
$classMetadata2 = $this->_metadataMap[spl_object_hash($entity2)]; $classMetadata2 = $this->_classMetadatas[get_class($entity2)];
$indexValue = $classMetadata2->getReflectionProperty($indexField)->getValue($entity2); $indexValue = $classMetadata2->getReflectionProperty($indexField)->getValue($entity2);
$classMetadata1->getReflectionProperty($property)->getValue($entity1)->set($indexValue, $entity2); $classMetadata1->getReflectionProperty($property)->getValue($entity1)->set($indexValue, $entity2);
} }
...@@ -209,8 +235,10 @@ class ObjectHydrator extends AbstractHydrator ...@@ -209,8 +235,10 @@ class ObjectHydrator extends AbstractHydrator
*/ */
private function addRelatedEntity($entity1, $property, $entity2) private function addRelatedEntity($entity1, $property, $entity2)
{ {
$classMetadata1 = $this->_metadataMap[spl_object_hash($entity1)]; $this->_classMetadatas[get_class($entity1)]
$classMetadata1->getReflectionProperty($property)->getValue($entity1)->add($entity2); ->getReflectionProperty($property)
->getValue($entity1)
->add($entity2);
} }
/** /**
...@@ -222,7 +250,7 @@ class ObjectHydrator extends AbstractHydrator ...@@ -222,7 +250,7 @@ class ObjectHydrator extends AbstractHydrator
*/ */
private function isFieldSet($entity, $field) private function isFieldSet($entity, $field)
{ {
return $this->_metadataMap[spl_object_hash($entity)] return $this->_classMetadatas[get_class($entity)]
->getReflectionProperty($field) ->getReflectionProperty($field)
->getValue($entity) !== null; ->getValue($entity) !== null;
} }
...@@ -237,12 +265,13 @@ class ObjectHydrator extends AbstractHydrator ...@@ -237,12 +265,13 @@ class ObjectHydrator extends AbstractHydrator
private function setRelatedElement($entity1, $property, $entity2) private function setRelatedElement($entity1, $property, $entity2)
{ {
$oid = spl_object_hash($entity1); $oid = spl_object_hash($entity1);
$classMetadata1 = $this->_metadataMap[$oid]; $classMetadata1 = $this->_classMetadatas[get_class($entity1)];
$classMetadata1->getReflectionProperty($property)->setValue($entity1, $entity2); $classMetadata1->getReflectionProperty($property)->setValue($entity1, $entity2);
$this->_uow->setOriginalEntityProperty($oid, $property, $entity2); $this->_uow->setOriginalEntityProperty($oid, $property, $entity2);
$relation = $classMetadata1->getAssociationMapping($property); $relation = $classMetadata1->getAssociationMapping($property);
if ($relation->isOneToOne()) { if ($relation->isOneToOne()) {
$targetClass = $this->_em->getClassMetadata($relation->getTargetEntityName()); //$targetClass = $this->_em->getClassMetadata($relation->getTargetEntityName());
$targetClass = $this->_classMetadatas[$relation->getTargetEntityName()];
if ($relation->isOwningSide()) { if ($relation->isOwningSide()) {
// If there is an inverse mapping on the target class its bidirectional // If there is an inverse mapping on the target class its bidirectional
if ($targetClass->hasInverseAssociationMapping($property)) { if ($targetClass->hasInverseAssociationMapping($property)) {
...@@ -291,7 +320,7 @@ class ObjectHydrator extends AbstractHydrator ...@@ -291,7 +320,7 @@ class ObjectHydrator extends AbstractHydrator
// Get a reference to the right element in the result tree. // Get a reference to the right element in the result tree.
// This element will get the associated element attached. // 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)); $key = key(reset($this->_resultPointers));
// TODO: Exception if $key === null ? // TODO: Exception if $key === null ?
$baseElement =& $this->_resultPointers[$parent][$key]; $baseElement =& $this->_resultPointers[$parent][$key];
...@@ -302,11 +331,11 @@ class ObjectHydrator extends AbstractHydrator ...@@ -302,11 +331,11 @@ class ObjectHydrator extends AbstractHydrator
continue; continue;
} }
$oid = spl_object_hash($baseElement); $parentClass = get_class($baseElement);
// Check the type of the relation (many or single-valued) // Check the type of the relation (many or single-valued)
if ( ! $relation->isOneToOne()) { if ( ! $relation->isOneToOne()) {
$oneToOne = false; //$oneToOne = false;
if (isset($nonemptyComponents[$dqlAlias])) { if (isset($nonemptyComponents[$dqlAlias])) {
$this->initRelatedCollection($baseElement, $relationAlias); $this->initRelatedCollection($baseElement, $relationAlias);
$indexExists = isset($this->_identifierMap[$path][$id[$parent]][$id[$dqlAlias]]); $indexExists = isset($this->_identifierMap[$path][$id[$parent]][$id[$dqlAlias]]);
...@@ -320,9 +349,10 @@ class ObjectHydrator extends AbstractHydrator ...@@ -320,9 +349,10 @@ class ObjectHydrator extends AbstractHydrator
$this->addRelatedEntity($baseElement, $relationAlias, $element); $this->addRelatedEntity($baseElement, $relationAlias, $element);
} }
$this->_identifierMap[$path][$id[$parent]][$id[$dqlAlias]] = $this->getLastKey( $this->_identifierMap[$path][$id[$parent]][$id[$dqlAlias]] = $this->getLastKey(
$this->_metadataMap[$oid] $this->_classMetadatas[$parentClass]
->getReflectionProperty($relationAlias) ->getReflectionProperty($relationAlias)
->getValue($baseElement)); ->getValue($baseElement)
);
} }
} else if ( ! $this->isFieldSet($baseElement, $relationAlias)) { } else if ( ! $this->isFieldSet($baseElement, $relationAlias)) {
$coll = new PersistentCollection($this->_em, $entityName); $coll = new PersistentCollection($this->_em, $entityName);
...@@ -330,7 +360,7 @@ class ObjectHydrator extends AbstractHydrator ...@@ -330,7 +360,7 @@ class ObjectHydrator extends AbstractHydrator
$this->setRelatedElement($baseElement, $relationAlias, $coll); $this->setRelatedElement($baseElement, $relationAlias, $coll);
} }
} else { } else {
$oneToOne = true; //$oneToOne = true;
if ( ! isset($nonemptyComponents[$dqlAlias]) && if ( ! isset($nonemptyComponents[$dqlAlias]) &&
! $this->isFieldSet($baseElement, $relationAlias)) { ! $this->isFieldSet($baseElement, $relationAlias)) {
$this->setRelatedElement($baseElement, $relationAlias, null); $this->setRelatedElement($baseElement, $relationAlias, null);
...@@ -340,12 +370,12 @@ class ObjectHydrator extends AbstractHydrator ...@@ -340,12 +370,12 @@ class ObjectHydrator extends AbstractHydrator
} }
} }
$coll = $this->_metadataMap[$oid] $coll = $this->_classMetadatas[$parentClass]
->getReflectionProperty($relationAlias) ->getReflectionProperty($relationAlias)
->getValue($baseElement); ->getValue($baseElement);
if ($coll !== null) { if ($coll !== null) {
$this->updateResultPointer($coll, $index, $dqlAlias, $oneToOne); $this->updateResultPointer($coll, $index, $dqlAlias/*, $oneToOne*/);
} }
} else { } else {
// Its a root result element // Its a root result element
...@@ -353,24 +383,22 @@ class ObjectHydrator extends AbstractHydrator ...@@ -353,24 +383,22 @@ class ObjectHydrator extends AbstractHydrator
$this->_rootAliases[$dqlAlias] = true; // Mark as root alias $this->_rootAliases[$dqlAlias] = true; // Mark as root alias
if ($this->_isSimpleQuery || ! isset($this->_identifierMap[$dqlAlias][$id[$dqlAlias]])) { if ($this->_isSimpleQuery || ! isset($this->_identifierMap[$dqlAlias][$id[$dqlAlias]])) {
$element = $this->_uow->createEntity($entityName, $rowData[$dqlAlias]); $element = $this->getEntity($rowData[$dqlAlias], $entityName);
$oid = spl_object_hash($element);
$this->_metadataMap[$oid] = $this->_em->getClassMetadata($entityName);
if ($field = $this->_getCustomIndexField($dqlAlias)) { if ($field = $this->_getCustomIndexField($dqlAlias)) {
if ($this->_parserResult->isMixedQuery()) { if ($this->_resultSetMapping->isMixedResult()) {
$result[] = array( $result[] = array(
$this->_metadataMap[$oid] $this->_classMetadatas[$entityName]
->getReflectionProperty($field) ->getReflectionProperty($field)
->getValue($element) => $element ->getValue($element) => $element
); );
++$this->_resultCounter; ++$this->_resultCounter;
} else { } else {
$result->set($element, $this->_metadataMap[$oid] $result->set($element, $this->_classMetadatas[$entityName]
->getReflectionProperty($field) ->getReflectionProperty($field)
->getValue($element)); ->getValue($element));
} }
} else { } else {
if ($this->_parserResult->isMixedQuery()) { if ($this->_resultSetMapping->isMixedResult()) {
$result[] = array($element); $result[] = array($element);
++$this->_resultCounter; ++$this->_resultCounter;
} else { } else {
...@@ -381,7 +409,7 @@ class ObjectHydrator extends AbstractHydrator ...@@ -381,7 +409,7 @@ class ObjectHydrator extends AbstractHydrator
} else { } else {
$index = $this->_identifierMap[$dqlAlias][$id[$dqlAlias]]; $index = $this->_identifierMap[$dqlAlias][$id[$dqlAlias]];
} }
$this->updateResultPointer($result, $index, $dqlAlias, false); $this->updateResultPointer($result, $index, $dqlAlias/*, false*/);
//unset($rowData[$dqlAlias]); //unset($rowData[$dqlAlias]);
} }
} }
......
<?php <?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Internal\Hydration; namespace Doctrine\ORM\Internal\Hydration;
...@@ -9,7 +28,7 @@ use \PDO; ...@@ -9,7 +28,7 @@ use \PDO;
* The created result is almost the same as a regular SQL result set, except * 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. * that column names are mapped to field names and data type conversions.
* *
* @author robo * @author Roman Borschel <roman@code-factory.org>
* @since 2.0 * @since 2.0
*/ */
class ScalarHydrator extends AbstractHydrator class ScalarHydrator extends AbstractHydrator
......
...@@ -273,7 +273,8 @@ final class ClassMetadata ...@@ -273,7 +273,8 @@ final class ClassMetadata
* @var array * @var array
* @see _discriminatorColumn * @see _discriminatorColumn
*/ */
private $_discriminatorMap = array(); //private $_discriminatorMap = array();
private $_discriminatorValue;
/** /**
* The definition of the descriminator column used in JOINED and SINGLE_TABLE * The definition of the descriminator column used in JOINED and SINGLE_TABLE
...@@ -826,6 +827,20 @@ final class ClassMetadata ...@@ -826,6 +827,20 @@ final class ClassMetadata
return isset($this->_columnNames[$fieldName]); 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. * Gets all field mappings.
* *
...@@ -1500,25 +1515,25 @@ final class ClassMetadata ...@@ -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. * Used for JOINED and SINGLE_TABLE inheritance mapping strategies.
* *
* @param array $map * @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. * Used for JOINED and SINGLE_TABLE inheritance mapping strategies.
* *
* @return array * @return array
*/ */
public function getDiscriminatorMap() public function getDiscriminatorValue()
{ {
return $this->_discriminatorMap; return $this->_discriminatorValue;
} }
/** /**
......
...@@ -126,7 +126,7 @@ class ClassMetadataFactory ...@@ -126,7 +126,7 @@ class ClassMetadataFactory
$class = $this->_newClassMetadataInstance($className); $class = $this->_newClassMetadataInstance($className);
if ($parent) { if ($parent) {
$class->setInheritanceType($parent->getInheritanceType()); $class->setInheritanceType($parent->getInheritanceType());
$class->setDiscriminatorMap($parent->getDiscriminatorMap()); //$class->setDiscriminatorMap($parent->getDiscriminatorMap());
$class->setDiscriminatorColumn($parent->getDiscriminatorColumn()); $class->setDiscriminatorColumn($parent->getDiscriminatorColumn());
$class->setIdGeneratorType($parent->getIdGeneratorType()); $class->setIdGeneratorType($parent->getIdGeneratorType());
$this->_addInheritedFields($class, $parent); $this->_addInheritedFields($class, $parent);
......
...@@ -75,11 +75,16 @@ class AnnotationDriver ...@@ -75,11 +75,16 @@ class AnnotationDriver
'length' => $discrColumnAnnot->length 'length' => $discrColumnAnnot->length
)); ));
} }
/*
// Evaluate DoctrineDiscriminatorMap annotation // Evaluate DoctrineDiscriminatorMap annotation
if ($discrMapAnnot = $annotClass->getAnnotation('DoctrineDiscriminatorMap')) { if ($discrMapAnnot = $annotClass->getAnnotation('DoctrineDiscriminatorMap')) {
$metadata->setDiscriminatorMap((array)$discrMapAnnot->value); $metadata->setDiscriminatorMap((array)$discrMapAnnot->value);
} }
*/
// Evaluate DoctrineDiscriminatorMap annotation
if ($discrValueAnnot = $annotClass->getAnnotation('DoctrineDiscriminatorValue')) {
$metadata->setDiscriminatorValue($discrValueAnnot->value);
}
// Evaluate DoctrineSubClasses annotation // Evaluate DoctrineSubClasses annotation
if ($subClassesAnnot = $annotClass->getAnnotation('DoctrineSubClasses')) { if ($subClassesAnnot = $annotClass->getAnnotation('DoctrineSubClasses')) {
......
...@@ -31,6 +31,7 @@ final class DoctrineDiscriminatorColumn extends \Addendum\Annotation { ...@@ -31,6 +31,7 @@ final class DoctrineDiscriminatorColumn extends \Addendum\Annotation {
public $length; public $length;
} }
final class DoctrineDiscriminatorMap extends \Addendum\Annotation {} final class DoctrineDiscriminatorMap extends \Addendum\Annotation {}
final class DoctrineDiscriminatorValue extends \Addendum\Annotation {}
final class DoctrineSubClasses extends \Addendum\Annotation {} final class DoctrineSubClasses extends \Addendum\Annotation {}
final class DoctrineId extends \Addendum\Annotation {} final class DoctrineId extends \Addendum\Annotation {}
final class DoctrineGeneratedValue extends \Addendum\Annotation { final class DoctrineGeneratedValue extends \Addendum\Annotation {
......
<?php <?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM; namespace Doctrine\ORM;
...@@ -6,51 +25,60 @@ namespace Doctrine\ORM; ...@@ -6,51 +25,60 @@ namespace Doctrine\ORM;
* Represents a native SQL query. * Represents a native SQL query.
* *
* @since 2.0 * @since 2.0
* @author Roman Borschel <roman@code-factory.org>
*/ */
class NativeQuery class NativeQuery extends AbstractQuery
{ {
private $_sql; private $_sql;
private $_conn;
private $_params = array();
public function __construct($sql, Connection $conn) /**
* Initializes a new instance of the <tt>NativeQuery</tt> class that is bound
* to the given EntityManager.
*
* @param EntityManager $em
*/
public function __construct(EntityManager $em)
{ {
$this->_sql = $sql; parent::__construct($em);
$this->_conn = $conn;
} }
/*public function addScalar() /**
{ * Sets the SQL of the query.
*
}*/ * @param string $sql
*/
public function addEntity($alias, $className) public function setSql($sql)
{ {
$this->_entities[$alias] = $className; $this->_sql = $sql;
} }
public function addJoin($join) /**
* 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()
{ {
return $this->_sql;
} }
public function setParameter($key, $value) /**
* Executed the query.
*
* @param array $params
* @return Statement The Statement handle.
* @override
*/
protected function _doExecute(array $params)
{ {
$this->_params[$key] = $value; // Assignments for Enums
} //$this->_setEnumParams($this->_parserResult->getEnumParams());
// Converting parameters
$params = $this->_prepareParams($params);
public function execute(array $params) // Executing the query and returning statement
{ return $this->_em->getConnection()->execute($this->_sql, $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));
} }
} }
\ No newline at end of file
...@@ -244,13 +244,5 @@ abstract class AbstractEntityPersister ...@@ -244,13 +244,5 @@ abstract class AbstractEntityPersister
$result[$columnName] = $type->convertToDatabaseValue($newVal, $this->_conn->getDatabasePlatform()); $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
...@@ -18,8 +18,8 @@ class SingleTablePersister extends AbstractEntityPersister ...@@ -18,8 +18,8 @@ class SingleTablePersister extends AbstractEntityPersister
// Populate the discriminator column // Populate the discriminator column
if ($isInsert) { if ($isInsert) {
$discColumn = $this->_classMetadata->getDiscriminatorColumn(); $discColumn = $this->_classMetadata->getDiscriminatorColumn();
$discMap = $this->_classMetadata->getDiscriminatorMap(); //$discMap = $this->_classMetadata->getDiscriminatorMap();
$result[$discColumn['name']] = array_search($this->_entityName, $discMap); $result[$discColumn['name']] = $this->_classMetadata->getDiscriminatorValue(); //array_search($this->_entityName, $discMap);
} }
} }
......
...@@ -38,589 +38,943 @@ use Doctrine\ORM\Query\QueryException; ...@@ -38,589 +38,943 @@ use Doctrine\ORM\Query\QueryException;
*/ */
class Query extends AbstractQuery 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. * Constant for SELECT queries.
*/ */
const HYDRATE_SINGLE_SCALAR = 4; const SELECT = 0;
/** /**
* Hydrates nothing. * Constant for DELETE queries.
*/ */
const HYDRATE_NONE = 5; const DELETE = 1;
/** /**
* @var Doctrine\ORM\EntityManager The entity manager used by this query object. * Constant for UPDATE queries.
*/ */
protected $_em; const UPDATE = 2;
/** /**
* @var integer The hydration mode. * A query object is in CLEAN state when it has NO unparsed/unprocessed DQL parts.
*/ */
protected $_hydrationMode = self::HYDRATE_OBJECT; const STATE_CLEAN = 1;
/** /**
* @var Doctrine\ORM\Query\ParserResult The parser result that holds DQL => SQL information. * 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.
*/ */
protected $_parserResult; const STATE_DIRTY = 2;
/** /**
* A set of query hints. * @var integer $type Query type.
* *
* @var array * @see Query::* constants
*/ */
protected $_hints = array(); protected $_type = self::SELECT;
// Caching Stuff
/** /**
* @var Doctrine_Cache_Interface The cache driver used for caching result sets. * @var integer $_state The current state of this query.
*/ */
protected $_resultCache; protected $_state = self::STATE_CLEAN;
/** /**
* @var boolean Boolean value that indicates whether or not expire the result cache. * @var array $_dqlParts An array containing all DQL query parts.
* @see Query::free that initializes this property
*/ */
protected $_expireResultCache = false; protected $_dqlParts = array();
/** /**
* @var int Result Cache lifetime. * @var string $_dql Cached DQL query.
*/ */
protected $_resultCacheTTL; protected $_dql = null;
/**
* @var Doctrine\ORM\Query\ParserResult The parser result that holds DQL => SQL information.
*/
protected $_parserResult;
/** /**
* @var Doctrine_Cache_Interface The cache driver used for caching queries. * @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. * @var boolean Boolean value that indicates whether or not expire the query cache.
*/ */
protected $_expireQueryCache = false; //protected $_expireQueryCache = false;
/** /**
* @var int Query Cache lifetime. * @var int Query Cache lifetime.
*/ */
protected $_queryCacheTTL; //protected $_queryCacheTTL;
// End of Caching Stuff // End of Caching Stuff
/** /**
* Initializes a new instance of the Query class. * Initializes a new Query instance.
* *
* @param Doctrine\ORM\EntityManager $entityManager * @param Doctrine\ORM\EntityManager $entityManager
*/ */
public function __construct(EntityManager $entityManager) public function __construct(EntityManager $entityManager)
{ {
$this->_em = $entityManager; parent::__construct($entityManager);
$this->free();
} }
/** /**
* Retrieves the associated EntityManager of this Query instance. * Gets the SQL query/queries that correspond to this DQL query.
* *
* @return Doctrine\ORM\EntityManager * @return mixed The built sql query or an array of all sql queries.
* @override
*/ */
public function getEntityManager() public function getSql()
{ {
return $this->_em; return $this->parse()->getSqlExecutor()->getSqlStatements();
} }
/** /**
* Convenience method to execute using array fetching as hydration mode. * Parses the DQL query, if necessary, and stores the parser result.
* *
* @param string $params * Note: Populates $this->_parserResult as a side-effect.
* @return array *
* @return Doctrine\ORM\Query\ParserResult
*/ */
public function fetchArray($params = array()) public function parse()
{ {
return $this->execute($params, self::HYDRATE_ARRAY); if ($this->_state === self::STATE_DIRTY) {
$parser = new Parser($this);
$this->_parserResult = $parser->parse();
$this->_state = self::STATE_CLEAN;
}
return $this->_parserResult;
} }
/** /**
* Convenience method to execute the query and return the first item * _execute
* of the collection.
* *
* @param string $params Parameters * @param array $params
* @param int $hydrationMode Hydration mode * @return PDOStatement The executed PDOStatement.
* @return mixed Array or Doctrine\Common\Collection or false if no result. * @override
*/ */
public function fetchOne($params = array(), $hydrationMode = null) protected function _doExecute(array $params)
{ {
$collection = $this->limit(1)->execute($params, $hydrationMode); // If there is a CacheDriver associated to cache queries...
if ($queryCache = $this->_em->getConfiguration()->getQueryCacheImpl()) {
// Calculate hash for dql query.
$hash = md5($this->getDql() . 'DOCTRINE_QUERY_CACHE_SALT');
$cached = ($this->_expireQueryCache) ? false : $queryCache->fetch($hash);
if (count($collection) === 0) { if ($cached === false) {
return false; // Cache miss.
$executor = $this->parse()->getSqlExecutor();
$queryCache->save($hash, $this->_parserResult->toCachedForm(), null);
} else {
// Cache hit.
$this->_parserResult = CacheHandler::fromCachedQuery($this, $cached);
$executor = $this->_parserResult->getSqlExecutor();
}
} else {
$executor = $this->parse()->getSqlExecutor();
} }
if ($collection instanceof Collection) { // Assignments for Enums
return $collection->getFirst(); $this->_setEnumParams($this->_parserResult->getEnumParams());
} else if (is_array($collection)) {
return array_shift($collection); // Converting parameters
$params = $this->_prepareParams($params);
if ( ! $this->_resultSetMapping) {
$this->_resultSetMapping = $this->_parserResult->getResultSetMapping();
} }
return false; // Executing the query and returning statement
return $executor->execute($this->_em->getConnection(), $params);
} }
/** /**
* Query the database with DQL (Doctrine Query Language). * Defines a cache driver to be used for caching queries.
* *
* @param string $query The DQL query. * @param Doctrine_Cache_Interface|null $driver Cache driver
* @param array $params The query parameters. * @return Doctrine_ORM_Query
* @param int $hydrationMode
* @return mixed
*/ */
public function query($query, $params = array(), $hydrationMode = null) /*public function setQueryCache($queryCache)
{ {
$this->setDql($query); if ($queryCache !== null && ! ($queryCache instanceof \Doctrine\ORM\Cache\Cache)) {
return $this->execute($params, $hydrationMode); throw DoctrineException::updateMe(
'Method setResultCache() accepts only an instance of Doctrine_ORM_Cache_Interface or null.'
);
} }
$this->_queryCache = $queryCache;
return $this;
}*/
/** /**
* Gets the SQL query/queries that correspond to this DQL query. * Returns the cache driver used for caching queries.
* *
* @return mixed The built sql query or an array of all sql queries. * @return Doctrine_Cache_Interface Cache driver
*/ */
public function getSql() /*public function getQueryCache()
{ {
return $this->parse()->getSqlExecutor()->getSqlStatements(); if ($this->_queryCache instanceof \Doctrine\ORM\Cache\Cache) {
return $this->_queryCache;
} else {
return $this->_em->getConnection()->getQueryCacheDriver();
} }
}*/
/** /**
* Parses the DQL query, if necessary, and stores the parser result. * Defines how long the query cache will be active before expire.
* *
* Note: Populates $this->_parserResult as a side-effect. * @param integer $timeToLive How long the cache entry is valid
* @return Doctrine_ORM_Query
*/
/*public function setQueryCacheLifetime($timeToLive)
{
if ($timeToLive !== null) {
$timeToLive = (int) $timeToLive;
}
$this->_queryCacheTTL = $timeToLive;
return $this;
}*/
/**
* Retrieves the lifetime of resultset cache.
* *
* @return Doctrine\ORM\Query\ParserResult * @return int
*/ */
public function parse() /*public function getQueryCacheLifetime()
{ {
if ($this->_state === self::STATE_DIRTY) { return $this->_queryCacheTTL;
$parser = new Parser($this); }*/
$this->_parserResult = $parser->parse();
/**
* Defines if the query cache is active or not.
*
* @param boolean $expire Whether or not to force query cache expiration.
* @return Doctrine_ORM_Query
*/
/*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()
{
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; $this->_state = self::STATE_CLEAN;
} }
return $this->_parserResult; /**
* Sets a DQL query string.
*
* @param string $dqlQuery DQL Query
*/
public function setDql($dqlQuery)
{
$this->free();
if ($dqlQuery !== null) {
$this->_dql = $dqlQuery;
$this->_state = self::STATE_DIRTY;
}
} }
/** /**
* Executes the query. * Returns the DQL query that is represented by this query object.
* *
* @param string $params Parameters to be sent to query. * @return string DQL 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) public function getDql()
{ {
if ($this->_em->getUnitOfWork()->hasPendingInsertions()) { if ($this->_dql !== null) {
$this->_em->flush(); return $this->_dql;
} }
if ($hydrationMode !== null) { $dql = '';
$this->_hydrationMode = $hydrationMode;
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;
} }
$params = $this->getParams($params); return $dql;
}
// Check result cache /**
if ($this->_resultCache && $this->_type === self::SELECT) { // Only executes if "SELECT" * Builds the DQL of DELETE
$cacheDriver = $this->getResultCacheDriver(); */
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' => ' '));
}
// 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. * Builds the DQL of UPDATE
$result = $this->_execute($params, self::HYDRATE_ARRAY); */
$queryResult = CacheHandler::fromResultSet($this, $result); protected function _getDqlForUpdate()
$cacheDriver->save($hash, $queryResult->toCachedForm(), $this->_resultCacheTTL); {
/*
* 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' => ' '));
}
return $result;
} else {
// Cache exists, recover it and return the results.
$queryResult = CacheHandler::fromCachedResult($this, $cached);
return $queryResult->getResultSet(); /**
* 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'] : '');
} }
$stmt = $this->_execute($params); $str = (isset($options['pre']) ? $options['pre'] : '');
$str .= implode($options['separator'], $this->getDqlQueryPart($queryPartName));
$str .= (isset($options['post']) ? $options['post'] : '');
if (is_integer($stmt)) { return $str;
return $stmt;
} }
return $this->_em->getHydrator($this->_hydrationMode)->hydrateAll($stmt, $this->_parserResult); /**
* 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;
} }
/** /**
* _execute * 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.
* *
* @param array $params * @see AbstractQuery::STATE_CLEAN
* @return PDOStatement The executed PDOStatement. * @see AbstractQuery::STATE_DIRTY
*
* @return integer Return the query state
*/ */
protected function _execute(array $params) public function getState()
{ {
// If there is a CacheDriver associated to cache queries... return $this->_state;
if ($this->_queryCache || $this->_em->getConfiguration()->getQueryCacheImpl()) { }
$queryCacheDriver = $this->getQueryCacheDriver();
// Calculate hash for dql query. /**
$hash = md5($this->getDql() . 'DOCTRINE_QUERY_CACHE_SALT'); * Adds fields to the SELECT part of the query
$cached = ($this->_expireQueryCache) ? false : $queryCacheDriver->fetch($hash); *
* @param string $select Query SELECT part
* @return Doctrine_ORM_Query
*/
public function select($select = '', $override = false)
{
if ($select === '') {
return $this;
}
if ($cached === false) { return $this->_addDqlQueryPart('select', $select, ! $override);
// Cache does not exist, we have to create it. }
$executor = $this->parse()->getSqlExecutor();
// To-be cached item is parserResult /**
$cacheDriver->save($hash, $this->_parserResult->toCachedForm(), $this->_queryCacheTTL); * Makes the query SELECT DISTINCT.
} else { *
// Cache exists, recover it and return the results. * @param bool $flag Whether or not the SELECT is DISTINCT (default true).
$this->_parserResult = CacheHandler::fromCachedQuery($this, $cached); * @return Doctrine_ORM_Query
*/
public function distinct($flag = true)
{
$this->_dqlParts['distinct'] = (bool) $flag;
return $this;
}
$executor = $this->_parserResult->getSqlExecutor(); /**
* 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 { } else {
$executor = $this->parse()->getSqlExecutor(); if ($params !== null) {
if (is_array($params)) {
$this->_params['set'] = array_merge($this->_params['set'], $params);
} else {
$this->_params['set'][] = $params;
}
} }
// Assignments for Enums if ($value === null) {
$this->_setEnumParams($this->_parserResult->getEnumParams()); throw \Doctrine\Common\DoctrineException::updateMe( 'Cannot try to set \''.$key.'\' without a value.' );
}
// Converting parameters return $this->_addDqlQueryPart('set', $key . ' = ' . $value, true);
$params = $this->_prepareParams($params); }
}
// Executing the query and returning statement /**
return $executor->execute($this->_em->getConnection(), $params); * 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);
} }
/** /**
* @nodoc * 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
*/ */
protected function _prepareParams(array $params) public function innerJoin($join, $params = array())
{ {
// Convert boolean params if (is_array($params)) {
$params = $this->_em->getConnection()->getDatabasePlatform()->convertBooleans($params); $this->_params['join'] = array_merge($this->_params['join'], $params);
} else {
$this->_params['join'][] = $params;
}
// Convert enum params return $this->_addDqlQueryPart('from', 'INNER JOIN ' . $join, true);
return $this->convertEnums($params);
} }
/** /**
* Defines a cache driver to be used for caching result sets. * Appends an INNER JOIN to the FROM part of the query
* *
* @param Doctrine\ORM\Cache\Cache $driver Cache driver * @param string $join Query INNER JOIN
* @return Doctrine\ORM\Query * @param mixed $params Optional JOIN params (array of parameters or a simple scalar)
* @return Doctrine_ORM_Query
*/ */
public function setResultCache($resultCache) public function join($join, $params = array())
{ {
if ($resultCache !== null && ! ($resultCache instanceof \Doctrine\ORM\Cache\Cache)) { return $this->innerJoin($join, $params);
throw DoctrineException::updateMe(
'Method setResultCache() accepts only an instance of Doctrine_Cache_Interface or null.'
);
} }
$this->_resultCache = $resultCache;
return $this; /**
* 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);
} }
/** /**
* Returns the cache driver used for caching result sets. * Adds conditions to the WHERE part of the query
* *
* @return Doctrine_Cache_Interface Cache driver * @param string $where Query WHERE part
* @param mixed $params An array of parameters or a simple scalar
* @return Doctrine_ORM_Query
*/ */
public function getResultCache() public function where($where, $params = array(), $override = false)
{ {
if ($this->_resultCache instanceof \Doctrine\ORM\Cache\Cache) { if ($override) {
return $this->_resultCache; $this->_params['where'] = array();
}
if (is_array($params)) {
$this->_params['where'] = array_merge($this->_params['where'], $params);
} else { } else {
return $this->_em->getConnection()->getResultCacheDriver(); $this->_params['where'][] = $params;
} }
return $this->_addDqlQueryPart('where', $where, ! $override);
} }
/** /**
* Defines how long the result cache will be active before expire. * Adds conditions to the WHERE part of the query
* *
* @param integer $timeToLive How long the cache entry is valid * @param string $where Query WHERE part
* @return Doctrine\ORM\Query * @param mixed $params An array of parameters or a simple scalar
* @return Doctrine_ORM_Query
*/ */
public function setResultCacheLifetime($timeToLive) public function andWhere($where, $params = array(), $override = false)
{ {
if ($timeToLive !== null) { if (count($this->getDqlQueryPart('where')) > 0) {
$timeToLive = (int) $timeToLive; $this->_addDqlQueryPart('where', 'AND', true);
} }
$this->_resultCacheTTL = $timeToLive; return $this->where($where, $params, $override);
return $this;
} }
/** /**
* Retrieves the lifetime of resultset cache. * Adds conditions to the WHERE part of the query
* *
* @return int * @param string $where Query WHERE part
* @param mixed $params An array of parameters or a simple scalar
* @return Doctrine_ORM_Query
*/ */
public function getResultCacheLifetime() public function orWhere($where, $params = array(), $override = false)
{ {
return $this->_resultCacheTTL; if (count($this->getDqlQueryPart('where')) > 0) {
$this->_addDqlQueryPart('where', 'OR', true);
}
return $this->where($where, $params, $override);
} }
/** /**
* Defines if the resultset cache is active or not. * Adds IN condition to the query WHERE part
* *
* @param boolean $expire Whether or not to force resultset cache expiration. * @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 * @return Doctrine_ORM_Query
*/ */
public function setExpireResultCache($expire = true) public function whereIn($expr, $params = array(), $override = false, $not = false)
{ {
$this->_expireResultCache = (bool) $expire; $params = (array) $params;
// Must have at least one param, otherwise we'll get an empty IN () => invalid SQL
if ( ! count($params)) {
return $this; return $this;
} }
list($sqlPart, $params) = $this->_processWhereInParams($params);
$where = $expr . ($not === true ? ' NOT' : '') . ' IN (' . $sqlPart . ')';
return $this->_returnWhereIn($where, $params, $override);
}
/** /**
* Retrieves if the resultset cache is active or not. * Adds NOT IN condition to the query WHERE part
* *
* @return bool * @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 getExpireResultCache() public function whereNotIn($expr, $params = array(), $override = false)
{ {
return $this->_expireResultCache; return $this->whereIn($expr, $params, $override, true);
} }
/** /**
* Defines a cache driver to be used for caching queries. * Adds IN condition to the query WHERE part
* *
* @param Doctrine_Cache_Interface|null $driver Cache driver * @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 * @return Doctrine_ORM_Query
*/ */
public function setQueryCache($queryCache) public function andWhereIn($expr, $params = array(), $override = false)
{ {
if ($queryCache !== null && ! ($queryCache instanceof \Doctrine\ORM\Cache\Cache)) { if (count($this->getDqlQueryPart('where')) > 0) {
throw DoctrineException::updateMe( $this->_addDqlQueryPart('where', 'AND', true);
'Method setResultCache() accepts only an instance of Doctrine_ORM_Cache_Interface or null.'
);
} }
$this->_queryCache = $queryCache; return $this->whereIn($expr, $params, $override);
return $this;
} }
/** /**
* Returns the cache driver used for caching queries. * Adds NOT IN condition to the query WHERE part
* *
* @return Doctrine_Cache_Interface Cache driver * @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 getQueryCache() public function andWhereNotIn($expr, $params = array(), $override = false)
{ {
if ($this->_queryCache instanceof \Doctrine\ORM\Cache\Cache) { if (count($this->getDqlQueryPart('where')) > 0) {
return $this->_queryCache; $this->_addDqlQueryPart('where', 'AND', true);
} else {
return $this->_em->getConnection()->getQueryCacheDriver();
} }
return $this->whereIn($expr, $params, $override, true);
} }
/** /**
* Defines how long the query cache will be active before expire. * Adds IN condition to the query WHERE part
* *
* @param integer $timeToLive How long the cache entry is valid * @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 * @return Doctrine_ORM_Query
*/ */
public function setQueryCacheLifetime($timeToLive) public function orWhereIn($expr, $params = array(), $override = false)
{ {
if ($timeToLive !== null) { if (count($this->getDqlQueryPart('where')) > 0) {
$timeToLive = (int) $timeToLive; $this->_addDqlQueryPart('where', 'OR', true);
} }
$this->_queryCacheTTL = $timeToLive; return $this->whereIn($expr, $params, $override);
}
return $this; /**
* 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);
} }
/** /**
* Retrieves the lifetime of resultset cache. * Adds fields to the GROUP BY part of the query
* *
* @return int * @param string $groupby Query GROUP BY part
* @return Doctrine_ORM_Query
*/ */
public function getQueryCacheLifetime() public function groupBy($groupby, $override = false)
{ {
return $this->_queryCacheTTL; return $this->_addDqlQueryPart('groupby', $groupby, ! $override);
} }
/** /**
* Defines if the query cache is active or not. * Adds conditions to the HAVING part of the query
* *
* @param boolean $expire Whether or not to force query cache expiration. * @param string $having Query HAVING part
* @param mixed $params An array of parameters or a simple scalar
* @return Doctrine_ORM_Query * @return Doctrine_ORM_Query
*/ */
public function setExpireQueryCache($expire = true) public function having($having, $params = array(), $override = false)
{ {
$this->_expireQueryCache = (bool) $expire; if ($override) {
$this->_params['having'] = array();
}
return $this; if (is_array($params)) {
$this->_params['having'] = array_merge($this->_params['having'], $params);
} else {
$this->_params['having'][] = $params;
}
return $this->_addDqlQueryPart('having', $having, true);
} }
/** /**
* Retrieves if the query cache is active or not. * Adds conditions to the HAVING part of the query
* *
* @return bool * @param string $having Query HAVING part
* @param mixed $params An array of parameters or a simple scalar
* @return Doctrine_ORM_Query
*/ */
public function getExpireQueryCache() public function andHaving($having, $params = array(), $override = false)
{ {
return $this->_expireQueryCache; if (count($this->getDqlQueryPart('having')) > 0) {
$this->_addDqlQueryPart('having', 'AND', true);
}
return $this->having($having, $params, $override);
} }
/** /**
* Defines the processing mode to be used during hydration process. * Adds conditions to the HAVING part of the query
* *
* @param integer $hydrationMode Doctrine processing mode to be used during hydration process. * @param string $having Query HAVING part
* One of the Query::HYDRATE_* constants. * @param mixed $params An array of parameters or a simple scalar
* @return Doctrine\ORM\Query * @return Doctrine_ORM_Query
*/ */
public function setHydrationMode($hydrationMode) public function orHaving($having, $params = array(), $override = false)
{ {
$this->_hydrationMode = $hydrationMode; if (count($this->getDqlQueryPart('having')) > 0) {
return $this; $this->_addDqlQueryPart('having', 'OR', true);
}
return $this->having($having, $params, $override);
} }
/** /**
* Gets the hydration mode currently used by the query. * Adds fields to the ORDER BY part of the query
* *
* @return integer * @param string $orderby Query ORDER BY part
* @return Doctrine_ORM_Query
*/ */
public function getHydrationMode() public function orderBy($orderby, $override = false)
{ {
return $this->_hydrationMode; return $this->_addDqlQueryPart('orderby', $orderby, ! $override);
} }
/** /**
* Gets the list of results for the query. * Sets the Query query limit
*
* Alias for execute(array(), HYDRATE_OBJECT).
* *
* @return Collection * @param integer $limit Limit to be used for limiting the query results
* @return Doctrine_ORM_Query
*/ */
public function getResultList() public function limit($limit)
{ {
return $this->execute(array(), self::HYDRATE_OBJECT); return $this->_addDqlQueryPart('limit', $limit);
} }
/** /**
* Gets the array of results for the query. * Sets the Query query offset
*
* Alias for execute(array(), HYDRATE_ARRAY).
* *
* @return array * @param integer $offset Offset to be used for paginating the query
* @return Doctrine_ORM_Query
*/ */
public function getResultArray() public function offset($offset)
{ {
return $this->execute(array(), self::HYDRATE_ARRAY); return $this->_addDqlQueryPart('offset', $offset);
} }
/** /**
* Gets the scalar results for the query. * Method to check if a arbitrary piece of DQL exists
*
* Alias for execute(array(), HYDRATE_SCALAR).
* *
* @return array * @param string $dql Arbitrary piece of DQL to check for
* @return boolean
*/ */
public function getScalarResult() public function contains($dql)
{ {
return $this->execute(array(), self::HYDRATE_SCALAR); return stripos($this->getDql(), $dql) === false ? false : true;
} }
/** /**
* Gets the single result of the query. * Retrieve a DQL part for internal purposes
* Enforces the uniqueness of the result. If the result is not unique,
* a QueryException is thrown.
* *
* @param integer $hydrationMode * @param string $queryPartName The name of the query part.
* @return mixed * @return mixed Array related to query part or simple scalar
* @throws QueryException If the query result is not unique.
*/ */
public function getSingleResult($hydrationMode = null) public function getDqlQueryPart($queryPartName)
{ {
$result = $this->execute(array(), $hydrationMode); if ( ! isset($this->_dqlParts[$queryPartName])) {
if (is_array($result)) { throw \Doctrine\Common\DoctrineException::updateMe('Unknown DQL query part \'' . $queryPartName . '\'');
if (count($result) > 1) {
throw QueryException::nonUniqueResult();
} }
return array_shift($result);
} else if (is_object($result)) { return $this->_dqlParts[$queryPartName];
if (count($result) > 1) {
throw QueryException::nonUniqueResult();
} }
return $result->getFirst();
/**
* 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);
} }
return $result;
$this->_state = Doctrine_ORM_Query::STATE_DIRTY;
return $this;
} }
/** /**
* Gets the single scalar result of the query. * 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
* Alias for getSingleResult(HYDRATE_SINGLE_SCALAR). * bound in SQL execution)
* *
* @return mixed * @param array $params Parameters to be processed
* @throws QueryException If the query result is not unique. * @return array
*/ */
public function getSingleScalarResult() protected function _processWhereInParams($params = array())
{ {
return $this->getSingleResult(self::HYDRATE_SINGLE_SCALAR); return array(
// [0] => sqlPart
implode(', ', array_map(array(&$this, '_processWhereInSqlPart'), $params)),
// [1] => params
array_filter($params, array(&$this, '_processWhereInParamItem')),
);
} }
/** /**
* Sets an implementation-specific hint. If the hint name is not recognized, * @nodoc
* 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) protected function _processWhereInSqlPart($value)
{ {
$this->_hints[$name] = $value; // [TODO] Add support to imbricated query (must deliver the hardest effort to Parser)
return ($value instanceof Doctrine_Expression) ? $value->getSql() : '?';
} }
/** /**
* Gets an implementation-specific hint. If the hint name is not recognized, * @nodoc
* FALSE is returned.
*
* @param string $name The name of the hint.
*/ */
public function getHint($name) protected function _processWhereInParamItem($value)
{ {
return isset($this->_hints[$name]) ? $this->_hints[$name] : false; // [TODO] Add support to imbricated query (must deliver the hardest effort to Parser)
return ( ! ($value instanceof Doctrine_Expression));
} }
/** /**
* Executes the query and returns an IterableResult that can be used to incrementally * Processes a WHERE IN () and build defined stuff to add in DQL
* iterated over the result.
* *
* @param array $params The query parameters. * @param string $where The WHERE clause to be added
* @param integer $hydrationMode The hydratio mode to use. * @param array $params WHERE clause parameters
* @return IterableResult * @param mixed $appender Where this clause may be not be appended, or appended
* (two possible values: AND or OR)
* @return Doctrine_ORM_Query
*/ */
public function iterate(array $params = array(), $hydrationMode = self::HYDRATE_OBJECT) protected function _returnWhereIn($where, $params = array(), $override = false)
{ {
return $this->_em->getHydrator($this->_hydrationMode)->iterate( // Parameters inclusion
$this->_execute($params, $hydrationMode), $this->_parserResult $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
...@@ -46,31 +46,6 @@ abstract class AbstractResult ...@@ -46,31 +46,6 @@ abstract class AbstractResult
*/ */
protected $_enumParams = array(); 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. * Returns the enum parameters.
* *
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
* *
* This software consists of voluntary contributions made by many individuals * This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see * and is licensed under the LGPL. For more information, see
* <http://www.phpdoctrine.org>. * <http://www.doctrine-project.org>.
*/ */
namespace Doctrine\ORM\Query\Exec; namespace Doctrine\ORM\Query\Exec;
......
...@@ -112,30 +112,12 @@ class Parser ...@@ -112,30 +112,12 @@ class Parser
*/ */
private $_query; 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. * Map of declared classes in the parsed query.
* Maps the declared DQL alias (key) to the class name (value). * Maps the declared DQL alias (key) to the class name (value).
* *
* @var array * @var array
*/ */
//private $_declaredClasses = array();
private $_queryComponents = array(); private $_queryComponents = array();
/** /**
...@@ -656,10 +638,6 @@ class Parser ...@@ -656,10 +638,6 @@ class Parser
$peek = $this->_lexer->glimpse(); $peek = $this->_lexer->glimpse();
// First we recognize for an IdentificationVariable (DQL class alias) // First we recognize for an IdentificationVariable (DQL class alias)
if ($peek['value'] != '.' && $peek['value'] != '(' && $this->_lexer->lookahead['type'] === Lexer::T_IDENTIFIER) { 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(); $expression = $this->_IdentificationVariable();
} else if (($isFunction = $this->_isFunction()) !== false || $this->_isSubselect()) { } else if (($isFunction = $this->_isFunction()) !== false || $this->_isSubselect()) {
if ($isFunction) { if ($isFunction) {
...@@ -680,15 +658,7 @@ class Parser ...@@ -680,15 +658,7 @@ class Parser
$this->match(Lexer::T_IDENTIFIER); $this->match(Lexer::T_IDENTIFIER);
$fieldIdentificationVariable = $this->_lexer->token['value']; $fieldIdentificationVariable = $this->_lexer->token['value'];
} }
$this->_resultContainsScalars = true;
if ($this->_resultContainsProperties) {
$this->_parserResult->setMixedQuery(true);
}
} else { } else {
$this->_resultContainsProperties = true;
if ($this->_resultContainsScalars) {
$this->_parserResult->setMixedQuery(true);
}
//TODO: If hydration mode is OBJECT throw an exception ("partial object dangerous...") //TODO: If hydration mode is OBJECT throw an exception ("partial object dangerous...")
// unless the doctrine.forcePartialLoad query hint is set // unless the doctrine.forcePartialLoad query hint is set
$expression = $this->_StateFieldPathExpression(); $expression = $this->_StateFieldPathExpression();
......
...@@ -29,6 +29,8 @@ namespace Doctrine\ORM\Query; ...@@ -29,6 +29,8 @@ namespace Doctrine\ORM\Query;
*/ */
class ResultSetMapping class ResultSetMapping
{ {
/** Whether the result is mixed (contains scalar values together with field values). */
private $_isMixed = false;
/** Maps alias names to ClassMetadata descriptors. */ /** Maps alias names to ClassMetadata descriptors. */
private $_aliasMap = array(); private $_aliasMap = array();
/** Maps alias names to related association mappings. */ /** Maps alias names to related association mappings. */
...@@ -41,10 +43,12 @@ class ResultSetMapping ...@@ -41,10 +43,12 @@ class ResultSetMapping
private $_scalarMappings = array(); private $_scalarMappings = array();
/** Maps column names in the result set to the alias they belong to. */ /** Maps column names in the result set to the alias they belong to. */
private $_columnOwnerMap = array(); private $_columnOwnerMap = array();
/** Maps discriminator columns in the result set to the class they represent. */ /** List of columns in the result set that are used as discriminator columns. */
private $_discriminatorMap = array(); private $_discriminatorColumns = array();
/** Maps alias names to field names that should be used for indexing. */ /** Maps alias names to field names that should be used for indexing. */
private $_indexByMap = array(); private $_indexByMap = array();
/** A list of columns that should be ignored/skipped during hydration. */
private $_ignoredColumns = array();
/** /**
* *
...@@ -52,12 +56,21 @@ class ResultSetMapping ...@@ -52,12 +56,21 @@ class ResultSetMapping
* @param <type> $alias The alias for this class. The alias must be unique within this ResultSetMapping. * @param <type> $alias The alias for this class. The alias must be unique within this ResultSetMapping.
* @param <type> $discriminatorColumn * @param <type> $discriminatorColumn
*/ */
public function addEntityResult($class, $alias, $discriminatorColumn = null) public function addEntityResult($class, $alias)
{ {
$this->_aliasMap[$alias] = $class; $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) public function addIndexBy($alias, $fieldName)
...@@ -75,30 +88,38 @@ class ResultSetMapping ...@@ -75,30 +88,38 @@ class ResultSetMapping
return $this->_indexByMap[$alias]; return $this->_indexByMap[$alias];
} }
public function isFieldResult($columnName)
{
return isset($this->_fieldMappings[$columnName]);
}
public function addFieldResult($alias, $columnName, $fieldName) public function addFieldResult($alias, $columnName, $fieldName)
{ {
$this->_fieldMappings[$columnName] = $fieldName; $this->_fieldMappings[$columnName] = $fieldName;
$this->_columnOwnerMap[$columnName] = $alias; $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->_aliasMap[$alias] = $class;
$this->_parentAliasMap[$alias] = $parentAlias; $this->_parentAliasMap[$alias] = $parentAlias;
$this->_relationMap[$alias] = $relation; $this->_relationMap[$alias] = $relation;
if ($discriminatorColumn !== null) {
$this->_discriminatorMap[$discriminatorColumn] = $class;
}
} }
public function isDiscriminatorColumn($columnName) /*public function isDiscriminatorColumn($columnName)
{ {
return isset($this->_discriminatorMap[$columnName]); return isset($this->_discriminatorMap[$columnName]);
} }*/
public function addScalarResult($columnName, $alias) public function addScalarResult($columnName, $alias)
{ {
$this->_scalarMappings[$columnName] = $alias; $this->_scalarMappings[$columnName] = $alias;
if ( ! $this->_isMixed && $this->_fieldMappings) {
$this->_isMixed = true;
}
} }
/** /**
...@@ -115,9 +136,6 @@ class ResultSetMapping ...@@ -115,9 +136,6 @@ class ResultSetMapping
*/ */
public function getClass($alias) public function getClass($alias)
{ {
if ( ! isset($this->_aliasMap[$alias])) {
var_dump($alias); die();
}
return $this->_aliasMap[$alias]; return $this->_aliasMap[$alias];
} }
...@@ -192,5 +210,20 @@ class ResultSetMapping ...@@ -192,5 +210,20 @@ class ResultSetMapping
{ {
return count($this->_aliasMap); 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]);
}
} }
...@@ -64,20 +64,12 @@ class SqlWalker ...@@ -64,20 +64,12 @@ class SqlWalker
$this->_em = $query->getEntityManager(); $this->_em = $query->getEntityManager();
$this->_parserResult = $parserResult; $this->_parserResult = $parserResult;
$this->_queryComponents = $queryComponents; $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 // In a mixed query we start alias counting for scalars with 1 since
// index 0 will hold the object. // index 0 will hold the object.
if ($parserResult->isMixedQuery()) { /*if ($parserResult->isMixedQuery()) {
$this->_scalarResultCounter = 1; $this->_scalarResultCounter = 1;
} }*/
} }
public function getConnection() public function getConnection()
...@@ -99,7 +91,6 @@ class SqlWalker ...@@ -99,7 +91,6 @@ class SqlWalker
$sql .= $AST->getHavingClause() ? $this->walkHavingClause($AST->getHavingClause()) : ''; $sql .= $AST->getHavingClause() ? $this->walkHavingClause($AST->getHavingClause()) : '';
$sql .= $AST->getOrderByClause() ? $this->walkOrderByClause($AST->getOrderByClause()) : ''; $sql .= $AST->getOrderByClause() ? $this->walkOrderByClause($AST->getOrderByClause()) : '';
//... more clauses
return $sql; return $sql;
} }
...@@ -113,16 +104,6 @@ class SqlWalker ...@@ -113,16 +104,6 @@ class SqlWalker
$sql = 'SELECT ' . (($selectClause->isDistinct()) ? 'DISTINCT ' : '') $sql = 'SELECT ' . (($selectClause->isDistinct()) ? 'DISTINCT ' : '')
. implode(', ', array_map(array($this, 'walkSelectExpression'), . implode(', ', array_map(array($this, 'walkSelectExpression'),
$selectClause->getSelectExpressions())); $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) { foreach ($this->_selectedClasses as $dqlAlias => $class) {
if ($this->_queryComponents[$dqlAlias]['relation'] === null) { if ($this->_queryComponents[$dqlAlias]['relation'] === null) {
...@@ -134,6 +115,15 @@ class SqlWalker ...@@ -134,6 +115,15 @@ class SqlWalker
$this->_queryComponents[$dqlAlias]['relation'] $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; return $sql;
......
...@@ -1173,15 +1173,6 @@ class UnitOfWork implements PropertyChangedListener ...@@ -1173,15 +1173,6 @@ class UnitOfWork implements PropertyChangedListener
*/ */
public function createEntity($className, array $data, $query = null) 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); $class = $this->_em->getClassMetadata($className);
$id = array(); $id = array();
...@@ -1232,11 +1223,12 @@ class UnitOfWork implements PropertyChangedListener ...@@ -1232,11 +1223,12 @@ class UnitOfWork implements PropertyChangedListener
private function _mergeData($entity, array $data, $class, $overrideLocalChanges = false) { private function _mergeData($entity, array $data, $class, $overrideLocalChanges = false) {
if ($overrideLocalChanges) { if ($overrideLocalChanges) {
foreach ($data as $field => $value) { foreach ($data as $field => $value) {
$class->getReflectionProperty($field)->setValue($entity, $value); $class->setValue($entity, $field, $value);
} }
} else { } else {
$oid = spl_object_hash($entity); $oid = spl_object_hash($entity);
foreach ($data as $field => $value) { foreach ($data as $field => $value) {
if ($class->hasField($field)) {
$currentValue = $class->getReflectionProperty($field)->getValue($entity); $currentValue = $class->getReflectionProperty($field)->getValue($entity);
if ( ! isset($this->_originalEntityData[$oid][$field]) || if ( ! isset($this->_originalEntityData[$oid][$field]) ||
$currentValue == $this->_originalEntityData[$oid][$field]) { $currentValue == $this->_originalEntityData[$oid][$field]) {
...@@ -1245,6 +1237,7 @@ class UnitOfWork implements PropertyChangedListener ...@@ -1245,6 +1237,7 @@ class UnitOfWork implements PropertyChangedListener
} }
} }
} }
}
/** /**
* Gets the identity map of the UnitOfWork. * Gets the identity map of the UnitOfWork.
......
...@@ -5,12 +5,6 @@ namespace Doctrine\Tests\Models\Forum; ...@@ -5,12 +5,6 @@ namespace Doctrine\Tests\Models\Forum;
/** /**
* @DoctrineEntity * @DoctrineEntity
* @DoctrineTable(name="forum_users") * @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 class ForumUser
{ {
......
...@@ -70,8 +70,5 @@ class EntityPersisterTest extends \Doctrine\Tests\OrmTestCase ...@@ -70,8 +70,5 @@ class EntityPersisterTest extends \Doctrine\Tests\OrmTestCase
//avatar_id join column //avatar_id join column
$this->assertTrue(isset($inserts['forum_users'][0]['avatar_id'])); $this->assertTrue(isset($inserts['forum_users'][0]['avatar_id']));
$this->assertEquals(0, $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
...@@ -20,6 +20,8 @@ class AllTests ...@@ -20,6 +20,8 @@ class AllTests
$suite = new \Doctrine\Tests\OrmFunctionalTestSuite('Doctrine Orm Functional'); $suite = new \Doctrine\Tests\OrmFunctionalTestSuite('Doctrine Orm Functional');
$suite->addTestSuite('Doctrine\Tests\ORM\Functional\BasicFunctionalTest'); $suite->addTestSuite('Doctrine\Tests\ORM\Functional\BasicFunctionalTest');
$suite->addTestSuite('Doctrine\Tests\ORM\Functional\NativeQueryTest');
$suite->addTestSuite('Doctrine\Tests\ORM\Functional\SingleTableInheritanceTest');
return $suite; return $suite;
} }
......
<?php
namespace Doctrine\Tests\ORM\Functional;
use Doctrine\ORM\Query\ResultSetMapping;
use Doctrine\Tests\Models\CMS\CmsUser;
require_once __DIR__ . '/../../TestInit.php';
/**
* NativeQueryTest
*
* @author robo
*/
class NativeQueryTest extends \Doctrine\Tests\OrmFunctionalTestCase
{
protected function setUp() {
$this->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);
}
}
<?php
namespace Doctrine\Tests\ORM\Functional;
require_once __DIR__ . '/../../TestInit.php';
/**
* Functional tests for the Single Table Inheritance mapping strategy.
*
* @author robo
*/
class SingleTableInheritanceTest extends \Doctrine\Tests\OrmFunctionalTestCase
{
protected function setUp() {
parent::setUp();
$this->_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;
}
}
...@@ -35,7 +35,7 @@ class ArrayHydratorTest extends HydrationTest ...@@ -35,7 +35,7 @@ class ArrayHydratorTest extends HydrationTest
$stmt = new HydratorMockStatement($resultSet); $stmt = new HydratorMockStatement($resultSet);
$hydrator = new \Doctrine\ORM\Internal\Hydration\ArrayHydrator($this->_em); $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->assertEquals(2, count($result));
$this->assertTrue(is_array($result)); $this->assertTrue(is_array($result));
...@@ -78,7 +78,7 @@ class ArrayHydratorTest extends HydrationTest ...@@ -78,7 +78,7 @@ class ArrayHydratorTest extends HydrationTest
$stmt = new HydratorMockStatement($resultSet); $stmt = new HydratorMockStatement($resultSet);
$hydrator = new \Doctrine\ORM\Internal\Hydration\ArrayHydrator($this->_em); $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)); $this->assertEquals(4, count($result));
...@@ -140,7 +140,7 @@ class ArrayHydratorTest extends HydrationTest ...@@ -140,7 +140,7 @@ class ArrayHydratorTest extends HydrationTest
$stmt = new HydratorMockStatement($resultSet); $stmt = new HydratorMockStatement($resultSet);
$hydrator = new \Doctrine\ORM\Internal\Hydration\ArrayHydrator($this->_em); $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->assertEquals(2, count($result));
$this->assertTrue(is_array($result)); $this->assertTrue(is_array($result));
...@@ -192,7 +192,7 @@ class ArrayHydratorTest extends HydrationTest ...@@ -192,7 +192,7 @@ class ArrayHydratorTest extends HydrationTest
$stmt = new HydratorMockStatement($resultSet); $stmt = new HydratorMockStatement($resultSet);
$hydrator = new \Doctrine\ORM\Internal\Hydration\ArrayHydrator($this->_em); $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->assertEquals(2, count($result));
$this->assertTrue(is_array($result)); $this->assertTrue(is_array($result));
...@@ -255,7 +255,7 @@ class ArrayHydratorTest extends HydrationTest ...@@ -255,7 +255,7 @@ class ArrayHydratorTest extends HydrationTest
$stmt = new HydratorMockStatement($resultSet); $stmt = new HydratorMockStatement($resultSet);
$hydrator = new \Doctrine\ORM\Internal\Hydration\ArrayHydrator($this->_em); $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->assertEquals(2, count($result));
$this->assertTrue(is_array($result)); $this->assertTrue(is_array($result));
...@@ -365,7 +365,7 @@ class ArrayHydratorTest extends HydrationTest ...@@ -365,7 +365,7 @@ class ArrayHydratorTest extends HydrationTest
$stmt = new HydratorMockStatement($resultSet); $stmt = new HydratorMockStatement($resultSet);
$hydrator = new \Doctrine\ORM\Internal\Hydration\ArrayHydrator($this->_em); $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->assertEquals(2, count($result));
$this->assertTrue(is_array($result)); $this->assertTrue(is_array($result));
...@@ -505,7 +505,7 @@ class ArrayHydratorTest extends HydrationTest ...@@ -505,7 +505,7 @@ class ArrayHydratorTest extends HydrationTest
$stmt = new HydratorMockStatement($resultSet); $stmt = new HydratorMockStatement($resultSet);
$hydrator = new \Doctrine\ORM\Internal\Hydration\ArrayHydrator($this->_em); $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->assertEquals(2, count($result));
$this->assertTrue(is_array($result)); $this->assertTrue(is_array($result));
...@@ -618,7 +618,7 @@ class ArrayHydratorTest extends HydrationTest ...@@ -618,7 +618,7 @@ class ArrayHydratorTest extends HydrationTest
$stmt = new HydratorMockStatement($resultSet); $stmt = new HydratorMockStatement($resultSet);
$hydrator = new \Doctrine\ORM\Internal\Hydration\ArrayHydrator($this->_em); $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->assertEquals(2, count($result));
$this->assertTrue(is_array($result)); $this->assertTrue(is_array($result));
...@@ -653,7 +653,7 @@ class ArrayHydratorTest extends HydrationTest ...@@ -653,7 +653,7 @@ class ArrayHydratorTest extends HydrationTest
$stmt = new HydratorMockStatement($resultSet); $stmt = new HydratorMockStatement($resultSet);
$hydrator = new \Doctrine\ORM\Internal\Hydration\ArrayHydrator($this->_em); $hydrator = new \Doctrine\ORM\Internal\Hydration\ArrayHydrator($this->_em);
$iterableResult = $hydrator->iterate($stmt, $this->_createParserResult($rsm)); $iterableResult = $hydrator->iterate($stmt, $rsm);
$rowNum = 0; $rowNum = 0;
while (($row = $iterableResult->next()) !== false) { while (($row = $iterableResult->next()) !== false) {
......
...@@ -35,7 +35,7 @@ class ObjectHydratorTest extends HydrationTest ...@@ -35,7 +35,7 @@ class ObjectHydratorTest extends HydrationTest
$stmt = new HydratorMockStatement($resultSet); $stmt = new HydratorMockStatement($resultSet);
$hydrator = new \Doctrine\ORM\Internal\Hydration\ObjectHydrator($this->_em); $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->assertEquals(2, count($result));
$this->assertTrue($result[0] instanceof \Doctrine\Tests\Models\CMS\CmsUser); $this->assertTrue($result[0] instanceof \Doctrine\Tests\Models\CMS\CmsUser);
...@@ -79,7 +79,7 @@ class ObjectHydratorTest extends HydrationTest ...@@ -79,7 +79,7 @@ class ObjectHydratorTest extends HydrationTest
$stmt = new HydratorMockStatement($resultSet); $stmt = new HydratorMockStatement($resultSet);
$hydrator = new \Doctrine\ORM\Internal\Hydration\ObjectHydrator($this->_em); $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)); $this->assertEquals(4, count($result));
...@@ -146,7 +146,7 @@ class ObjectHydratorTest extends HydrationTest ...@@ -146,7 +146,7 @@ class ObjectHydratorTest extends HydrationTest
$stmt = new HydratorMockStatement($resultSet); $stmt = new HydratorMockStatement($resultSet);
$hydrator = new \Doctrine\ORM\Internal\Hydration\ObjectHydrator($this->_em); $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->assertEquals(2, count($result));
$this->assertTrue(is_array($result)); $this->assertTrue(is_array($result));
...@@ -205,7 +205,7 @@ class ObjectHydratorTest extends HydrationTest ...@@ -205,7 +205,7 @@ class ObjectHydratorTest extends HydrationTest
$stmt = new HydratorMockStatement($resultSet); $stmt = new HydratorMockStatement($resultSet);
$hydrator = new \Doctrine\ORM\Internal\Hydration\ObjectHydrator($this->_em); $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->assertEquals(2, count($result));
$this->assertTrue(is_array($result)); $this->assertTrue(is_array($result));
...@@ -270,7 +270,7 @@ class ObjectHydratorTest extends HydrationTest ...@@ -270,7 +270,7 @@ class ObjectHydratorTest extends HydrationTest
$stmt = new HydratorMockStatement($resultSet); $stmt = new HydratorMockStatement($resultSet);
$hydrator = new \Doctrine\ORM\Internal\Hydration\ObjectHydrator($this->_em); $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->assertEquals(2, count($result));
$this->assertTrue(is_array($result)); $this->assertTrue(is_array($result));
...@@ -384,7 +384,7 @@ class ObjectHydratorTest extends HydrationTest ...@@ -384,7 +384,7 @@ class ObjectHydratorTest extends HydrationTest
$stmt = new HydratorMockStatement($resultSet); $stmt = new HydratorMockStatement($resultSet);
$hydrator = new \Doctrine\ORM\Internal\Hydration\ObjectHydrator($this->_em); $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->assertEquals(2, count($result));
$this->assertTrue(is_array($result)); $this->assertTrue(is_array($result));
...@@ -519,7 +519,7 @@ class ObjectHydratorTest extends HydrationTest ...@@ -519,7 +519,7 @@ class ObjectHydratorTest extends HydrationTest
$stmt = new HydratorMockStatement($resultSet); $stmt = new HydratorMockStatement($resultSet);
$hydrator = new \Doctrine\ORM\Internal\Hydration\ObjectHydrator($this->_em); $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->assertEquals(2, count($result));
$this->assertTrue(is_array($result)); $this->assertTrue(is_array($result));
...@@ -626,7 +626,7 @@ class ObjectHydratorTest extends HydrationTest ...@@ -626,7 +626,7 @@ class ObjectHydratorTest extends HydrationTest
$stmt = new HydratorMockStatement($resultSet); $stmt = new HydratorMockStatement($resultSet);
$hydrator = new \Doctrine\ORM\Internal\Hydration\ObjectHydrator($this->_em); $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->assertEquals(2, count($result));
$this->assertTrue($result[0] instanceof \Doctrine\Tests\Models\Forum\ForumCategory); $this->assertTrue($result[0] instanceof \Doctrine\Tests\Models\Forum\ForumCategory);
...@@ -663,7 +663,7 @@ class ObjectHydratorTest extends HydrationTest ...@@ -663,7 +663,7 @@ class ObjectHydratorTest extends HydrationTest
$stmt = new HydratorMockStatement($resultSet); $stmt = new HydratorMockStatement($resultSet);
$hydrator = new \Doctrine\ORM\Internal\Hydration\ObjectHydrator($this->_em); $hydrator = new \Doctrine\ORM\Internal\Hydration\ObjectHydrator($this->_em);
$iterableResult = $hydrator->iterate($stmt, $this->_createParserResult($rsm)); $iterableResult = $hydrator->iterate($stmt, $rsm);
$rowNum = 0; $rowNum = 0;
while (($row = $iterableResult->next()) !== false) { while (($row = $iterableResult->next()) !== false) {
...@@ -739,6 +739,6 @@ class ObjectHydratorTest extends HydrationTest ...@@ -739,6 +739,6 @@ class ObjectHydratorTest extends HydrationTest
$stmt = new HydratorMockStatement($resultSet); $stmt = new HydratorMockStatement($resultSet);
$hydrator = new \Doctrine\ORM\Internal\Hydration\ObjectHydrator($this->_em); $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
...@@ -35,7 +35,7 @@ class ScalarHydratorTest extends HydrationTest ...@@ -35,7 +35,7 @@ class ScalarHydratorTest extends HydrationTest
$stmt = new HydratorMockStatement($resultSet); $stmt = new HydratorMockStatement($resultSet);
$hydrator = new \Doctrine\ORM\Internal\Hydration\ScalarHydrator($this->_em); $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->assertTrue(is_array($result));
$this->assertEquals(2, count($result)); $this->assertEquals(2, count($result));
......
...@@ -63,14 +63,14 @@ class SingleScalarHydratorTest extends HydrationTest ...@@ -63,14 +63,14 @@ class SingleScalarHydratorTest extends HydrationTest
$hydrator = new \Doctrine\ORM\Internal\Hydration\SingleScalarHydrator($this->_em); $hydrator = new \Doctrine\ORM\Internal\Hydration\SingleScalarHydrator($this->_em);
if ($name == 'result1') { if ($name == 'result1') {
$result = $hydrator->hydrateAll($stmt, $this->_createParserResult($rsm)); $result = $hydrator->hydrateAll($stmt, $rsm);
$this->assertEquals('romanb', $result); $this->assertEquals('romanb', $result);
} else if ($name == 'result2') { } else if ($name == 'result2') {
$result = $hydrator->hydrateAll($stmt, $this->_createParserResult($rsm)); $result = $hydrator->hydrateAll($stmt, $rsm);
$this->assertEquals(1, $result); $this->assertEquals(1, $result);
} else if ($name == 'result3' || $name == 'result4') { } else if ($name == 'result3' || $name == 'result4') {
try { try {
$result = $hydrator->hydrateall($stmt, $this->_createParserResult($rsm)); $result = $hydrator->hydrateall($stmt, $rsm);
$this->fail(); $this->fail();
} catch (\Doctrine\ORM\Internal\Hydration\HydrationException $ex) {} } catch (\Doctrine\ORM\Internal\Hydration\HydrationException $ex) {}
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment