1
0
mirror of synced 2025-02-20 14:13:15 +03:00

First commit of new extensible hydrator structure. Cleanup to follow.

This commit is contained in:
romanb 2009-01-13 21:56:43 +00:00
parent 1843539fa2
commit 60fb69dd03
22 changed files with 2770 additions and 587 deletions

View File

@ -8,7 +8,6 @@
#use \Countable;
#use \IteratorAggregate;
#use \Serializable;
#use \ArrayAccess;
/**
@ -17,7 +16,8 @@
*
* @author robo
*/
class Doctrine_Common_Collections_Collection implements Countable, IteratorAggregate, Serializable, ArrayAccess {
class Doctrine_Common_Collections_Collection implements Countable, IteratorAggregate, ArrayAccess
{
/**
* An array containing the entries of this collection.
* This is the wrapped php array.
@ -89,10 +89,7 @@ class Doctrine_Common_Collections_Collection implements Countable, IteratorAggre
}
/**
* __isset()
*
* @param string $name
* @return boolean whether or not this object contains $name
* @see containsKey()
*/
public function __isset($key)
{
@ -100,10 +97,7 @@ class Doctrine_Common_Collections_Collection implements Countable, IteratorAggre
}
/**
* __unset()
*
* @param string $key
* @return mixed
* @see remove()
*/
public function __unset($key)
{
@ -113,10 +107,7 @@ class Doctrine_Common_Collections_Collection implements Countable, IteratorAggre
/* ArrayAccess implementation */
/**
* Check if an offset exists.
*
* @param mixed $offset
* @return boolean Whether or not this object contains $offset
* @see containsKey()
*/
public function offsetExists($offset)
{
@ -124,12 +115,7 @@ class Doctrine_Common_Collections_Collection implements Countable, IteratorAggre
}
/**
* Gets the element with the given key.
*
* Part of the ArrayAccess implementation.
*
* @param mixed $offset
* @return mixed
* @see get()
*/
public function offsetGet($offset)
{
@ -137,13 +123,8 @@ class Doctrine_Common_Collections_Collection implements Countable, IteratorAggre
}
/**
* Part of the ArrayAccess implementation.
*
* sets $offset to $value
* @see set, __set
* @param mixed $offset
* @param mixed $value
* @return void
* @see add()
* @see set()
*/
public function offsetSet($offset, $value)
{
@ -154,11 +135,7 @@ class Doctrine_Common_Collections_Collection implements Countable, IteratorAggre
}
/**
* Part of the ArrayAccess implementation.
*
* unset a given offset
* @see set, offsetSet, __set
* @param mixed $offset
* @see remove()
*/
public function offsetUnset($offset)
{
@ -171,7 +148,7 @@ class Doctrine_Common_Collections_Collection implements Countable, IteratorAggre
* Checks whether the collection contains a specific key/index.
*
* @param mixed $key The key to check for.
* @return boolean
* @return boolean TRUE if the given key/index exists, FALSE otherwise.
*/
public function containsKey($key)
{
@ -185,7 +162,8 @@ class Doctrine_Common_Collections_Collection implements Countable, IteratorAggre
* For objects this means reference equality.
*
* @param mixed $element
* @return boolean
* @return boolean TRUE if the given element is contained in the collection,
* FALSE otherwise.
*/
public function contains($element)
{
@ -196,9 +174,9 @@ class Doctrine_Common_Collections_Collection implements Countable, IteratorAggre
* Tests for the existance of an element that satisfies the given predicate.
*
* @param function $func
* @return boolean
* @return boolean TRUE if the predicate is TRUE for at least one element, FALSe otherwise.
*/
public function exists($func) {
public function exists(Closure $func) {
foreach ($this->_data as $key => $element)
if ($func($key, $element))
return true;
@ -213,7 +191,7 @@ class Doctrine_Common_Collections_Collection implements Countable, IteratorAggre
*/
public function containsAll($otherColl)
{
//...
throw new Doctrine_Exception("Not yet implemented.");
}
/**
@ -343,7 +321,7 @@ class Doctrine_Common_Collections_Collection implements Countable, IteratorAggre
*
* @param function $func
*/
public function map($func)
public function map(Closure $func)
{
return new Doctrine_Common_Collections_Collection(array_map($func, $this->_data));
}
@ -354,7 +332,7 @@ class Doctrine_Common_Collections_Collection implements Countable, IteratorAggre
*
* @param function $func
*/
public function filter($func)
public function filter(Closure $func)
{
return new Doctrine_Common_Collections_Collection(array_filter($this->_data, $func));
}
@ -376,39 +354,5 @@ class Doctrine_Common_Collections_Collection implements Countable, IteratorAggre
{
$this->_data = array();
}
/* Serializable implementation */
/**
* Serializes the collection.
* This method is automatically called when the Collection is serialized.
*
* Part of the implementation of the Serializable interface.
*
* @return array
*/
public function serialize()
{
$vars = get_object_vars($this);
//TODO
return serialize($vars);
}
/**
* Reconstitutes the collection object from it's serialized form.
* This method is automatically called everytime the Collection object is unserialized.
*
* Part of the implementation of the Serializable interface.
*
* @param string $serialized The serialized data
*
* @return void
*/
public function unserialize($serialized)
{
//TODO
}
}

View File

@ -6,14 +6,24 @@
class Doctrine_DBAL_Driver_PDOMySql_Driver implements Doctrine_DBAL_Driver
{
/**
* Attempts to establish a connection with the underlying driver.
*
* @param array $params
* @param string $username
* @param string $password
* @param array $driverOptions
* @return Doctrine\DBAL\Driver\Connection
*/
public function connect(array $params, $username = null, $password = null, array $driverOptions = array())
{
return new Doctrine_DBAL_Driver_PDOConnection(
$conn = new Doctrine_DBAL_Driver_PDOConnection(
$this->_constructPdoDsn($params),
$username,
$password,
$driverOptions);
$conn->setAttribute(PDO::ATTR_AUTOCOMMIT, false);
return $conn;
}
/**
@ -54,4 +64,3 @@ class Doctrine_DBAL_Driver_PDOMySql_Driver implements Doctrine_DBAL_Driver
}
?>

View File

@ -24,12 +24,8 @@
/**
* A persistent collection wrapper.
*
* A collection object is strongly typed in the sense that it can only contain
* entities of a specific type or one of it's subtypes. A collection object is
* basically a wrapper around an ordinary php array and just like a php array
* it can have List or Map semantics.
*
* A collection of entities represents only the associations (links) to those entities.
* A PersistentCollection represents a collection of entities. Collections of
* entities represent only the associations (links) to those entities.
* That means, if the collection is part of a many-many mapping and you remove
* entities from the collection, only the links in the xref table are removed (on flush).
* Similarly, if you remove entities from a collection that is part of a one-many
@ -46,14 +42,14 @@
* @author Roman Borschel <roman@code-factory.org>
* @todo Rename to PersistentCollection
*/
class Doctrine_ORM_Collection extends Doctrine_Common_Collections_Collection
final class Doctrine_ORM_Collection extends Doctrine_Common_Collections_Collection
{
/**
* The base type of the collection.
*
* @var string
*/
protected $_entityBaseType;
private $_entityBaseType;
/**
* A snapshot of the collection at the moment it was fetched from the database.
@ -61,14 +57,14 @@ class Doctrine_ORM_Collection extends Doctrine_Common_Collections_Collection
*
* @var array
*/
protected $_snapshot = array();
private $_snapshot = array();
/**
* The entity that owns this collection.
*
* @var Doctrine\ORM\Entity
* @var object
*/
protected $_owner;
private $_owner;
/**
* The association mapping the collection belongs to.
@ -76,21 +72,21 @@ class Doctrine_ORM_Collection extends Doctrine_Common_Collections_Collection
*
* @var Doctrine\ORM\Mapping\AssociationMapping
*/
protected $_association;
private $_association;
/**
* The name of the field that is used for collection key mapping.
*
* @var string
*/
protected $_keyField;
private $_keyField;
/**
* The EntityManager that manages the persistence of the collection.
*
* @var Doctrine\ORM\EntityManager
*/
protected $_em;
private $_em;
/**
* The name of the field on the target entities that points to the owner
@ -98,7 +94,7 @@ class Doctrine_ORM_Collection extends Doctrine_Common_Collections_Collection
*
* @var string
*/
protected $_backRefFieldName;
private $_backRefFieldName;
/**
* Hydration flag.
@ -106,7 +102,7 @@ class Doctrine_ORM_Collection extends Doctrine_Common_Collections_Collection
* @var boolean
* @see _setHydrationFlag()
*/
protected $_hydrationFlag;
private $_hydrationFlag;
/**
* Creates a new persistent collection.
@ -115,7 +111,6 @@ class Doctrine_ORM_Collection extends Doctrine_Common_Collections_Collection
{
$this->_entityBaseType = $entityBaseType;
$this->_em = $em;
if ($keyField !== null) {
if ( ! $this->_em->getClassMetadata($entityBaseType)->hasField($keyField)) {
throw new Doctrine_Exception("Invalid field '$keyField' can't be uses as key.");
@ -124,17 +119,6 @@ class Doctrine_ORM_Collection extends Doctrine_Common_Collections_Collection
}
}
/**
* setData
*
* @param array $data
* @todo Remove?
*/
public function setData(array $data)
{
$this->_data = $data;
}
/**
* INTERNAL: Sets the key column for this collection
*
@ -161,7 +145,8 @@ class Doctrine_ORM_Collection extends Doctrine_Common_Collections_Collection
* INTERNAL:
* Sets the collection owner. Used (only?) during hydration.
*
* @return void
* @param object $entity
* @param AssociationMapping $relation
*/
public function _setOwner($entity, Doctrine_ORM_Mapping_AssociationMapping $relation)
{
@ -184,7 +169,7 @@ class Doctrine_ORM_Collection extends Doctrine_Common_Collections_Collection
* INTERNAL:
* Gets the collection owner.
*
* @return Doctrine\ORM\Entity
* @return object
*/
public function _getOwner()
{
@ -196,6 +181,7 @@ class Doctrine_ORM_Collection extends Doctrine_Common_Collections_Collection
*
* @param mixed $key
* @return boolean
* @override
*/
public function remove($key)
{
@ -205,8 +191,9 @@ class Doctrine_ORM_Collection extends Doctrine_Common_Collections_Collection
/*if ($this->_association->isOneToMany() && $this->_association->shouldDeleteOrphans()) {
$this->_em->delete($removed);
}*/
return parent::remove($key);
$removed = parent::remove($key);
$this->_changed();
return $removed;
}
/**
@ -215,13 +202,13 @@ class Doctrine_ORM_Collection extends Doctrine_Common_Collections_Collection
*
* @param integer $key
* @param mixed $value
* @return void
* @override
*/
public function set($key, $value)
{
parent::set($key, $value);
//TODO: Register collection as dirty with the UoW if necessary
$this->_changed();
if ( ! $this->_hydrationFlag) $this->_changed();
}
/**
@ -230,10 +217,11 @@ class Doctrine_ORM_Collection extends Doctrine_Common_Collections_Collection
* @param mixed $value
* @param string $key
* @return boolean
* @override
*/
public function add($value, $key = null)
public function add($value)
{
$result = parent::add($value, $key);
$result = parent::add($value);
if ( ! $result) return $result; // EARLY EXIT
if ($this->_hydrationFlag) {
@ -287,10 +275,6 @@ class Doctrine_ORM_Collection extends Doctrine_Common_Collections_Collection
* Snapshots are used for diff processing, for example
* when a fetched collection has three elements, then two of those
* are being removed the diff would contain one element.
*
* Collection::save() attaches the diff with the help of last snapshot.
*
* @return void
*/
public function _takeSnapshot()
{
@ -299,9 +283,9 @@ class Doctrine_ORM_Collection extends Doctrine_Common_Collections_Collection
/**
* INTERNAL:
* Returns the data of the last snapshot.
* Returns the last snapshot of the elements in the collection.
*
* @return array returns the data in last snapshot
* @return array The last snapshot of the elements.
*/
public function _getSnapshot()
{
@ -366,7 +350,7 @@ class Doctrine_ORM_Collection extends Doctrine_Common_Collections_Collection
/**
* INTERNAL: Gets the association mapping of the collection.
*
* @return Doctrine::ORM::Mapping::AssociationMapping
* @return Doctrine\ORM\Mapping\AssociationMapping
*/
public function getMapping()
{
@ -375,8 +359,6 @@ class Doctrine_ORM_Collection extends Doctrine_Common_Collections_Collection
/**
* Clears the collection.
*
* @return void
*/
public function clear()
{

View File

@ -64,13 +64,12 @@ class Doctrine_ORM_EntityManager
* The currently active EntityManager. Only one EntityManager can be active
* at any time.
*
* @var Doctrine::ORM::EntityManager
* @var Doctrine\ORM\EntityManager
*/
private static $_activeEm;
/**
* The unique name of the EntityManager. The name is used to bind entity classes
* to certain EntityManagers.
* The unique name of the EntityManager.
*
* @var string
*/
@ -79,14 +78,14 @@ class Doctrine_ORM_EntityManager
/**
* The used Configuration.
*
* @var Configuration
* @var Doctrine\ORM\Configuration
*/
private $_config;
/**
* The database connection used by the EntityManager.
*
* @var Connection
* @var Doctrine\DBAL\Connection
*/
private $_conn;
@ -98,7 +97,7 @@ class Doctrine_ORM_EntityManager
private $_metadataFactory;
/**
* The EntityPersister instances.
* The EntityPersister instances used to persist entity instances.
*
* @var array
*/
@ -119,16 +118,16 @@ class Doctrine_ORM_EntityManager
private $_flushMode = 'commit';
/**
* The unit of work used to coordinate object-level transactions.
* The UnitOfWork used to coordinate object-level transactions.
*
* @var UnitOfWork
* @var Doctrine\ORM\UnitOfWork
*/
private $_unitOfWork;
/**
* The event manager that is the central point of the event system.
*
* @var EventManager
* @var Doctrine\Common\EventManager
*/
private $_eventManager;
@ -139,6 +138,13 @@ class Doctrine_ORM_EntityManager
*/
private $_idGenerators = array();
/**
* The maintained (cached) hydrators. One instance per type.
*
* @var array
*/
private $_hydrators = array();
/** Whether the EntityManager is closed or not. */
private $_closed = false;
@ -224,7 +230,7 @@ class Doctrine_ORM_EntityManager
/**
* Returns the metadata for a class.
*
* @return Doctrine_Metadata
* @return Doctrine\ORM\Mapping\ClassMetadata
* @internal Performance-sensitive method.
*/
public function getClassMetadata($className)
@ -249,7 +255,7 @@ class Doctrine_ORM_EntityManager
* Used to lazily create the id generator.
*
* @param string $generatorType
* @return void
* @return object
*/
protected function _createIdGenerator($generatorType)
{
@ -349,6 +355,8 @@ class Doctrine_ORM_EntityManager
/**
* Flushes all changes to objects that have been queued up to now to the database.
* This effectively synchronizes the in-memory state of managed objects with the
* database.
*/
public function flush()
{
@ -362,7 +370,7 @@ class Doctrine_ORM_EntityManager
*
* @param string $entityName
* @param mixed $identifier
* @return Doctrine\ORM\Entity
* @return object
*/
public function find($entityName, $identifier)
{
@ -569,6 +577,49 @@ class Doctrine_ORM_EntityManager
{
return self::$_activeEm === $this;
}
/**
* Gets a hydrator for the given hydration mode.
*
* @param $hydrationMode
*/
public function getHydrator($hydrationMode)
{
if ( ! isset($this->_hydrators[$hydrationMode])) {
switch ($hydrationMode) {
case Doctrine_ORM_Query::HYDRATE_OBJECT:
$this->_hydrators[$hydrationMode] = new Doctrine_ORM_Internal_Hydration_ObjectHydrator($this);
break;
case Doctrine_ORM_Query::HYDRATE_ARRAY:
$this->_hydrators[$hydrationMode] = new Doctrine_ORM_Internal_Hydration_ArrayHydrator($this);
break;
case Doctrine_ORM_Query::HYDRATE_SCALAR:
case Doctrine_ORM_Query::HYDRATE_SINGLE_SCALAR:
$this->_hydrators[$hydrationMode] = new Doctrine_ORM_Internal_Hydration_ScalarHydrator($this);
break;
case Doctrine_ORM_Query::HYDRATE_NONE:
$this->_hydrators[$hydrationMode] = new Doctrine_ORM_Internal_Hydration_NoneHydrator($this);
break;
default:
throw new Doctrine_Exception("No hydrator found for hydration mode '$hydrationMode'.");
}
} else if ($this->_hydrators[$hydrationMode] instanceof Closure) {
$this->_hydrators[$hydrationMode] = $this->_hydrators[$hydrationMode]($this);
}
return $this->_hydrators[$hydrationMode];
}
/**
* Sets a hydrator for a hydration mode.
*
* @param mixed $hydrationMode
* @param object $hydrator Either a hydrator instance or a closure that creates a
* hydrator instance.
*/
public function setHydrator($hydrationMode, $hydrator)
{
$this->_hydrators[$hydrationMode] = $hydrator;
}
/**
* Makes this EntityManager the currently active one.
@ -630,4 +681,3 @@ class Doctrine_ORM_EntityManager
}
}
?>

View File

@ -19,14 +19,14 @@
* <http://www.phpdoctrine.org>.
*/
#namespace Doctrine::ORM::Internal::Hydration;
#namespace Doctrine\ORM\Internal\Hydration;
/**
* Base class for all hydrators (ok, we got only 1 currently).
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.phpdoctrine.org
* @since 1.0
* @link www.doctrine-project.org
* @since 2.0
* @version $Revision: 3192 $
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
* @author Roman Borschel <roman@code-factory.org>
@ -46,102 +46,318 @@ abstract class Doctrine_ORM_Internal_Hydration_AbstractHydrator
*/
protected $_queryComponents = array();
/**
* @var array Table alias map. Keys are SQL aliases and values DQL aliases.
*/
protected $_tableAliasMap = array();
/** @var array Table alias map. Keys are SQL aliases and values DQL aliases. */
protected $_tableAliases = array();
/**
* The current hydration mode.
*/
protected $_hydrationMode = Doctrine_ORM_Query::HYDRATE_OBJECT;
protected $_nullObject;
/** @var EntityManager The EntityManager instance. */
protected $_em;
/** @var UnitOfWork The UnitOfWork of the associated EntityManager. */
protected $_uow;
/** @var array The cache used during row-by-row hydration. */
protected $_cache = array();
/** @var Statement The statement that provides the data to hydrate. */
protected $_stmt;
/** @var object The ParserResult instance that holds the necessary information for hydration. */
protected $_parserResult;
/**
* Constructor.
* Initializes a new instance of a class derived from AbstractHydrator.
*
* @param Doctrine::ORM::EntityManager $em The EntityManager to use during hydration.
* @param Doctrine\ORM\EntityManager $em The EntityManager to use.
*/
public function __construct(Doctrine_ORM_EntityManager $em)
{
$this->_em = $em;
$this->_nullObject = Doctrine_ORM_Internal_Null::$INSTANCE;
$this->_uow = $em->getUnitOfWork();
}
/**
* setHydrationMode
* Initiates a row-by-row hydration.
*
* Defines the hydration process mode.
*
* @param integer $hydrationMode Doctrine processing mode to be used during hydration process.
* One of the Doctrine::HYDRATE_* constants.
* @param object $stmt
* @param object $parserResult
* @return IterableResult
*/
public function setHydrationMode($hydrationMode)
public function iterate($stmt, $parserResult)
{
$this->_hydrationMode = $hydrationMode;
$this->_stmt = $stmt;
$this->_prepare($parserResult);
return new Doctrine_ORM_Internal_Hydration_IterableResult($this);
}
/**
* setQueryComponents
* Hydrates all rows returned by the passed statement instance at once.
*
* Defines the mapping components.
*
* @param array $queryComponents Query components.
*/
public function setQueryComponents(array $queryComponents)
{
$this->_queryComponents = $queryComponents;
}
/**
* getQueryComponents
*
* Gets the mapping components.
*
* @return array Query components.
*/
public function getQueryComponents()
{
return $this->_queryComponents;
}
/**
* setTableAliasMap
*
* Defines the table aliases.
*
* @param array $tableAliasMap Table aliases.
*/
public function setTableAliasMap(array $tableAliasMap)
{
$this->_tableAliasMap = $tableAliasMap;
}
/**
* getTableAliasMap
*
* Returns all table aliases.
*
* @return array Table aliases as an array.
*/
public function getTableAliasMap()
{
return $this->_tableAliasMap;
}
/**
* Processes data returned by statement object.
*
* This is method defines the core of Doctrine object/array population algorithm.
*
* @param mixed $stmt PDOStatement
* @param integer $hydrationMode Doctrine processing mode to be used during hydration process.
* One of the Doctrine::HYDRATE_* constants.
* @param object $stmt
* @param object $parserResult
* @return mixed
*/
abstract public function hydrateResultSet($parserResult);
public function hydrateAll($stmt, $parserResult)
{
$this->_stmt = $stmt;
$this->_prepare($parserResult);
$result = $this->_hydrateAll($parserResult);
$this->_cleanup();
return $result;
}
/**
* Hydrates a single row returned by the current statement instance during
* row-by-row hydration with {@link iterate()}.
*
* @return mixed
*/
public function hydrateRow()
{
$row = $this->_stmt->fetch(PDO::FETCH_ASSOC);
if ( ! $row) {
$this->_cleanup();
return false;
}
$result = $this->_getRowContainer();
$this->_hydrateRow($row, $this->_cache, $result);
return $result;
}
/**
* Excutes one-time preparation tasks once each time hydration is started
* through {@link hydrateAll} or {@link iterate()}.
*
* @param object $parserResult
*/
protected function _prepare($parserResult)
{
$this->_queryComponents = $parserResult->getQueryComponents();
$this->_tableAliases = $parserResult->getTableAliasMap();
$this->_parserResult = $parserResult;
}
/**
* Excutes one-time cleanup tasks at the end of a hydration that was initiated
* through {@link hydrateAll} or {@link iterate()}.
*/
protected function _cleanup()
{
$this->_parserResult = null;
$this->_stmt->closeCursor();
$this->_stmt = null;
}
/**
* Hydrates a single row from the current statement instance.
*
* Template method.
*
* @param array $data The row data.
* @param array $cache The cache to use.
* @param mixed $result The result to fill.
*/
protected function _hydrateRow(array &$data, array &$cache, &$result)
{}
/**
* Hydrates all rows from the current statement instance at once.
*
* @param object $parserResult
*/
abstract protected function _hydrateAll($parserResult);
/**
* Gets the row container used during row-by-row hydration through {@link iterate()}.
*/
abstract protected function _getRowContainer();
/**
* Processes a row of the result set.
* Used for identity hydration (HYDRATE_IDENTITY_OBJECT and HYDRATE_IDENTITY_ARRAY).
* Puts the elements of a result row into a new array, grouped by the class
* they belong to. The column names in the result set are mapped to their
* field names during this procedure as well as any necessary conversions on
* the values applied.
*
* @return array An array with all the fields (name => value) of the data row,
* grouped by their component (alias).
*/
protected function _gatherRowData(&$data, &$cache, &$id, &$nonemptyComponents)
{
$rowData = array();
foreach ($data as $key => $value) {
// Parse each column name only once. Cache the results.
if ( ! isset($cache[$key])) {
if ($this->_isIgnoredName($key)) continue;
// Cache general information like the column name <-> field name mapping
$e = explode(Doctrine_ORM_Query_ParserRule::SQLALIAS_SEPARATOR, $key);
$columnName = array_pop($e);
$cache[$key]['dqlAlias'] = $this->_tableAliases[
implode(Doctrine_ORM_Query_ParserRule::SQLALIAS_SEPARATOR, $e)
];
$classMetadata = $this->_queryComponents[$cache[$key]['dqlAlias']]['metadata'];
// check whether it's an aggregate value or a regular field
if (isset($this->_queryComponents[$cache[$key]['dqlAlias']]['agg'][$columnName])) {
$fieldName = $this->_queryComponents[$cache[$key]['dqlAlias']]['agg'][$columnName];
$cache[$key]['isScalar'] = true;
} else {
$fieldName = $this->_lookupFieldName($classMetadata, $columnName);
$cache[$key]['isScalar'] = false;
$cache[$key]['type'] = $classMetadata->getTypeOfColumn($columnName);
}
$cache[$key]['fieldName'] = $fieldName;
// Cache identifier information
if ($classMetadata->isIdentifier($fieldName)) {
$cache[$key]['isIdentifier'] = true;
} else {
$cache[$key]['isIdentifier'] = false;
}
}
$class = $this->_queryComponents[$cache[$key]['dqlAlias']]['metadata'];
$dqlAlias = $cache[$key]['dqlAlias'];
$fieldName = $cache[$key]['fieldName'];
if ($cache[$key]['isScalar']) {
$rowData['scalars'][$fieldName] = $value;
continue;
}
if ($cache[$key]['isIdentifier']) {
$id[$dqlAlias] .= '|' . $value;
}
$rowData[$dqlAlias][$fieldName] = $cache[$key]['type']->convertToPHPValue($value);
if ( ! isset($nonemptyComponents[$dqlAlias]) && $value !== null) {
$nonemptyComponents[$dqlAlias] = true;
}
}
return $rowData;
}
/**
* Processes a row of the result set.
* Used for HYDRATE_SCALAR. This is a variant of _gatherRowData() that
* simply converts column names to field names and properly prepares the
* values. The resulting row has the same number of elements as before.
*
* @param array $data
* @param array $cache
* @return array The processed row.
*/
protected function _gatherScalarRowData(&$data, &$cache)
{
$rowData = array();
foreach ($data as $key => $value) {
// Parse each column name only once. Cache the results.
if ( ! isset($cache[$key])) {
if ($this->_isIgnoredName($key)) continue;
// cache general information like the column name <-> field name mapping
$e = explode(Doctrine_ORM_Query_ParserRule::SQLALIAS_SEPARATOR, $key);
$columnName = array_pop($e);
$cache[$key]['dqlAlias'] = $this->_tableAliases[
implode(Doctrine_ORM_Query_ParserRule::SQLALIAS_SEPARATOR, $e)
];
$classMetadata = $this->_queryComponents[$cache[$key]['dqlAlias']]['metadata'];
// check whether it's an aggregate value or a regular field
if (isset($this->_queryComponents[$cache[$key]['dqlAlias']]['agg'][$columnName])) {
$fieldName = $this->_queryComponents[$cache[$key]['dqlAlias']]['agg'][$columnName];
$cache[$key]['isScalar'] = true;
} else {
$fieldName = $this->_lookupFieldName($classMetadata, $columnName);
$cache[$key]['isScalar'] = false;
// cache type information
$cache[$key]['type'] = $classMetadata->getTypeOfColumn($columnName);
}
$cache[$key]['fieldName'] = $fieldName;
}
$class = $this->_queryComponents[$cache[$key]['dqlAlias']]['metadata'];
$dqlAlias = $cache[$key]['dqlAlias'];
$fieldName = $cache[$key]['fieldName'];
if ($cache[$key]['isScalar']) {
$rowData[$dqlAlias . '_' . $fieldName] = $value;
} else {
$rowData[$dqlAlias . '_' . $fieldName] = $cache[$key]['type']->convertToPHPValue($value);
}
}
return $rowData;
}
/**
* Gets the custom field used for indexing for the specified DQL alias.
*
* @return string The field name of the field used for indexing or NULL
* if the component does not use any custom field indices.
*/
protected function _getCustomIndexField($alias)
{
return isset($this->_queryComponents[$alias]['map']) ? $this->_queryComponents[$alias]['map'] : 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.
*
* This is mostly used during hydration, because we want to make the
* conversion to field names while iterating over the result set for best
* performance. By doing this at that point, we can avoid re-iterating over
* the data just to convert the column names to field names.
*
* However, when this is happening, we don't know the real
* class name to instantiate yet (the row data may target a sub-type), hence
* this method looks up the field name in the subclass mappings if it's not
* found on this class mapping.
* This lookup on subclasses is costly but happens only *once* for a column
* during hydration because the hydrator caches effectively.
*
* @return string The field name.
* @throws Doctrine::ORM::Exceptions::ClassMetadataException If the field name could
* not be found.
*/
private function _lookupFieldName($class, $lcColumnName)
{
if ($class->hasLowerColumn($lcColumnName)) {
return $class->getFieldNameForLowerColumnName($lcColumnName);
}
foreach ($class->getSubclasses() as $subClass) {
$subClassMetadata = Doctrine_ORM_Mapping_ClassMetadataFactory::getInstance()
->getMetadataFor($subClass);
if ($subClassMetadata->hasLowerColumn($lcColumnName)) {
return $subClassMetadata->getFieldNameForLowerColumnName($lcColumnName);
}
}
throw new Doctrine_Exception("No field name found for column name '$lcColumnName' during hydration.");
}
/** Needed only temporarily until the new parser is ready */
private $_isResultMixed = false;
public function setResultMixed($bool)
{
$this->_isResultMixed = $bool;
}
}

View File

@ -0,0 +1,208 @@
<?php
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
/**
* Description of ArrayHydrator
*
* @author robo
*/
class Doctrine_ORM_Internal_Hydration_ArrayHydrator extends Doctrine_ORM_Internal_Hydration_AbstractHydrator
{
private $_rootAlias;
private $_rootEntityName;
private $_isSimpleQuery = false;
private $_identifierMap = array();
private $_resultPointers = array();
private $_idTemplate = array();
private $_resultCounter = 0;
/** @override */
protected function _prepare($parserResult)
{
parent::_prepare($parserResult);
reset($this->_queryComponents);
$this->_rootAlias = key($this->_queryComponents);
$this->_rootEntityName = $this->_queryComponents[$this->_rootAlias]['metadata']->getClassName();
$this->_isSimpleQuery = count($this->_queryComponents) <= 1;
$this->_identifierMap = array();
$this->_resultPointers = array();
$this->_idTemplate = array();
$this->_resultCounter = 0;
foreach ($this->_queryComponents as $dqlAlias => $component) {
$this->_identifierMap[$dqlAlias] = array();
$this->_resultPointers[$dqlAlias] = array();
$this->_idTemplate[$dqlAlias] = '';
}
}
/** @override */
protected function _hydrateAll($parserResult)
{
$s = microtime(true);
$result = array();
$cache = array();
while ($data = $this->_stmt->fetch(PDO::FETCH_ASSOC)) {
$this->_hydrateRow($data, $cache, $result);
}
$e = microtime(true);
echo 'Hydration took: ' . ($e - $s) . PHP_EOL;
return $result;
}
/** @override */
protected function _hydrateRow(array &$data, array &$cache, &$result)
{
// 1) Initialize
$id = $this->_idTemplate; // initialize the id-memory
$nonemptyComponents = array();
$rowData = parent::_gatherRowData($data, $cache, $id, $nonemptyComponents);
$rootAlias = $this->_rootAlias;
// 2) Hydrate the data of the root entity from the current row
// Check for an existing element
$index = false;
if ($this->_isSimpleQuery || ! isset($this->_identifierMap[$rootAlias][$id[$rootAlias]])) {
$element = $rowData[$rootAlias];
if ($field = $this->_getCustomIndexField($rootAlias)) {
if ($this->_parserResult->isMixedQuery()) {
$result[] = array($element[$field] => $element);
++$this->_resultCounter;
} else {
$result[$element[$field]] = $element;
}
} else {
if ($this->_parserResult->isMixedQuery()) {
$result[] = array($element);
++$this->_resultCounter;
} else {
$result[] = $element;
}
}
end($result);
$this->_identifierMap[$rootAlias][$id[$rootAlias]] = key($result);
} else {
$index = $this->_identifierMap[$rootAlias][$id[$rootAlias]];
}
$this->updateResultPointer($result, $index, $rootAlias, false);
unset($rowData[$rootAlias]);
// end of hydrate data of the root component for the current row
// Extract scalar values. They're appended at the end.
if (isset($rowData['scalars'])) {
$scalars = $rowData['scalars'];
unset($rowData['scalars']);
}
// 3) Now hydrate the rest of the data found in the current row, that
// belongs to other (related) entities.
foreach ($rowData as $dqlAlias => $data) {
$index = false;
$map = $this->_queryComponents[$dqlAlias];
$parent = $map['parent'];
$relationAlias = $map['relation']->getSourceFieldName();
$path = $parent . '.' . $dqlAlias;
// Get a reference to the right element in the result tree.
// This element will get the associated element attached.
if ($this->_parserResult->isMixedQuery() && $parent == $rootAlias) {
$key = key(reset($this->_resultPointers));
// TODO: Exception if $key === null ?
$baseElement =& $this->_resultPointers[$parent][$key];
} else if (isset($this->_resultPointers[$parent])) {
$baseElement =& $this->_resultPointers[$parent];
} else {
unset($this->_resultPointers[$dqlAlias]); // Ticket #1228
continue;
}
// Check the type of the relation (many or single-valued)
if ( ! $map['relation']->isOneToOne()) {
$oneToOne = false;
if (isset($nonemptyComponents[$dqlAlias])) {
if ( ! isset($baseElement[$relationAlias])) {
$baseElement[$relationAlias] = array();
}
$indexExists = isset($this->_identifierMap[$path][$id[$parent]][$id[$dqlAlias]]);
$index = $indexExists ? $this->_identifierMap[$path][$id[$parent]][$id[$dqlAlias]] : false;
$indexIsValid = $index !== false ? isset($baseElement[$relationAlias][$index]) : false;
if ( ! $indexExists || ! $indexIsValid) {
$element = $data;
if ($field = $this->_getCustomIndexField($dqlAlias)) {
$baseElement[$relationAlias][$element[$field]] = $element;
} else {
$baseElement[$relationAlias][] = $element;
}
end($baseElement[$relationAlias]);
$this->_identifierMap[$path][$id[$parent]][$id[$dqlAlias]] =
key($baseElement[$relationAlias]);
}
} else if ( ! isset($baseElement[$relationAlias])) {
$baseElement[$relationAlias] = array();
}
} else {
$oneToOne = true;
if ( ! isset($nonemptyComponents[$dqlAlias]) && ! isset($baseElement[$relationAlias])) {
$baseElement[$relationAlias] = null;
} else if ( ! isset($baseElement[$relationAlias])) {
$baseElement[$relationAlias] = $data;
}
}
$coll =& $baseElement[$relationAlias];
if ($coll !== null) {
$this->updateResultPointer($coll, $index, $dqlAlias, $oneToOne);
}
}
// Append scalar values to mixed result sets
if (isset($scalars)) {
foreach ($scalars as $name => $value) {
$result[$this->_resultCounter - 1][$name] = $value;
}
}
}
/**
* Updates the result pointer for an Entity. The result pointers point to the
* last seen instance of each Entity type. This is used for graph construction.
*
* @param array $coll The element.
* @param boolean|integer $index Index of the element in the collection.
* @param string $dqlAlias
* @param boolean $oneToOne Whether it is a single-valued association or not.
*/
private function updateResultPointer(&$coll, $index, $dqlAlias, $oneToOne)
{
if ($coll === null) {
unset($this->_resultPointers[$dqlAlias]); // Ticket #1228
return;
}
if ($index !== false) {
$this->_resultPointers[$dqlAlias] =& $coll[$index];
return;
} else {
if ($coll) {
if ($oneToOne) {
$this->_resultPointers[$dqlAlias] =& $coll;
} else {
end($coll);
$this->_resultPointers[$dqlAlias] =& $coll[key($coll)];
}
}
}
}
/** {@inheritdoc} */
protected function _getRowContainer()
{
return array();
}
}

View File

@ -0,0 +1,28 @@
<?php
/**
* Represents a result structure that can be iterated over, hydrating row-by-row
* during the iteration. An IterableResult is obtained by AbstractHydrator#iterate().
*
* @author robo
* @since 2.0
*/
class Doctrine_ORM_Internal_Hydration_IterableResult
{
private $_hydrator;
public function __construct($hydrator)
{
$this->_hydrator = $hydrator;
}
/**
* Gets the next set of results.
*
* @return array
*/
public function next()
{
return $this->_hydrator->hydrateRow();
}
}

View File

@ -159,13 +159,13 @@ class Doctrine_ORM_Internal_Hydration_ObjectDriver
$oid2 = spl_object_hash($entity2);
$sourceProp = $targetClass->getInverseAssociationMapping($fieldName)->getSourceFieldName();
$targetClass->getReflectionProperty($sourceProp)->setValue($entity2, $entity1);
$this->_entityData[$oid2][$sourceProp] = $entity1;
//$this->_entityData[$oid2][$sourceProp] = $entity1;
}
} else {
// for sure bidirectional, as there is no inverse side in unidirectional
$mappedByProp = $relation->getMappedByFieldName();
$targetClass->getReflectionProperty($mappedByProp)->setValue($entity2, $entity1);
$this->_entityData[spl_object_hash($entity2)][$mappedByProp] = $entity1;
//$this->_entityData[spl_object_hash($entity2)][$mappedByProp] = $entity1;
}
}
}
@ -217,7 +217,6 @@ class Doctrine_ORM_Internal_Hydration_ObjectDriver
public function updateResultPointer(&$resultPointers, &$coll, $index, $dqlAlias, $oneToOne)
{
if ($coll === null) {
echo "HERE!";
unset($resultPointers[$dqlAlias]); // Ticket #1228
return;
}

View File

@ -0,0 +1,371 @@
<?php
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
/**
* Description of ObjectHydrator
*
* @author robo
*/
class Doctrine_ORM_Internal_Hydration_ObjectHydrator extends Doctrine_ORM_Internal_Hydration_AbstractHydrator
{
/** Collections initialized by the driver */
private $_collections = array();
/** Memory for initialized relations */
private $_initializedRelations = array();
private $_metadataMap = array();
private $_rootAlias;
private $_rootEntityName;
private $_isSimpleQuery = false;
private $_identifierMap = array();
private $_resultPointers = array();
private $_idTemplate = array();
private $_resultCounter = 0;
protected function _prepare($parserResult)
{
parent::_prepare($parserResult);
reset($this->_queryComponents);
$this->_rootAlias = key($this->_queryComponents);
$this->_rootEntityName = $this->_queryComponents[$this->_rootAlias]['metadata']->getClassName();
$this->_isSimpleQuery = count($this->_queryComponents) <= 1;
$this->_identifierMap = array();
$this->_resultPointers = array();
$this->_idTemplate = array();
$this->_resultCounter = 0;
foreach ($this->_queryComponents as $dqlAlias => $component) {
$this->_identifierMap[$dqlAlias] = array();
$this->_resultPointers[$dqlAlias] = array();
$this->_idTemplate[$dqlAlias] = '';
}
}
/** @override */
protected function _hydrateAll($parserResult)
{
$s = microtime(true);
if ($this->_parserResult->isMixedQuery()) {
$result = array();
} else {
$result = new Doctrine_ORM_Collection($this->_em, $this->_rootEntityName);
}
$cache = array();
// Process result set
while ($data = $this->_stmt->fetch(PDO::FETCH_ASSOC)) {
$this->_hydrateRow($data, $cache, $result);
}
// Take snapshots from all initialized collections
foreach ($this->_collections as $coll) {
$coll->_takeSnapshot();
$coll->_setHydrationFlag(false);
$this->_uow->addManagedCollection($coll);
}
// Clean up
$this->_collections = array();
$this->_initializedRelations = array();
$this->_metadataMap = array();
$e = microtime(true);
echo 'Hydration took: ' . ($e - $s) . PHP_EOL;
return $result;
}
/**
* Updates the result pointer for an Entity. The result pointers point to the
* last seen instance of each Entity type. This is used for graph construction.
*
* @param array $resultPointers The result pointers.
* @param Collection $coll The element.
* @param boolean|integer $index Index of the element in the collection.
* @param string $dqlAlias
* @param boolean $oneToOne Whether it is a single-valued association or not.
*/
private function updateResultPointer(&$coll, $index, $dqlAlias, $oneToOne)
{
if ($coll === null) {
unset($this->_resultPointers[$dqlAlias]); // Ticket #1228
return;
}
if ($index !== false) {
$this->_resultPointers[$dqlAlias] = $coll[$index];
return;
}
if ( ! is_object($coll)) {
end($coll);
$this->_resultPointers[$dqlAlias] =& $coll[key($coll)];
} else if ($coll instanceof Doctrine_ORM_Collection) {
if (count($coll) > 0) {
$this->_resultPointers[$dqlAlias] = $coll->last();
}
} else {
$this->_resultPointers[$dqlAlias] = $coll;
}
}
private function getElementCollection($component)
{
$coll = new Doctrine_ORM_Collection($this->_em, $component);
$this->_collections[] = $coll;
return $coll;
}
private function initRelatedCollection($entity, $name)
{
$oid = spl_object_hash($entity);
$classMetadata = $this->_metadataMap[$oid];
if ( ! isset($this->_initializedRelations[$oid][$name])) {
$relation = $classMetadata->getAssociationMapping($name);
$relatedClass = $this->_em->getClassMetadata($relation->getTargetEntityName());
$coll = $this->getElementCollection($relatedClass->getClassName());
$coll->_setOwner($entity, $relation);
$coll->_setHydrationFlag(true);
$classMetadata->getReflectionProperty($name)->setValue($entity, $coll);
$this->_initializedRelations[$oid][$name] = true;
$this->_uow->setOriginalEntityProperty($oid, $name, $coll);
}
}
private function isIndexKeyInUse($entity, $assocField, $indexField)
{
return $this->_metadataMap[spl_object_hash($entity)]->getReflectionProperty($assocField)
->getValue($entity)->containsKey($indexField);
}
private function getLastKey($coll)
{
// check needed because of mixed results.
// is_object instead of is_array because is_array is slow on large arrays.
if (is_object($coll)) {
$coll->last();
return $coll->key();
} else {
end($coll);
return key($coll);
}
}
private function getElement(array $data, $className)
{
$entity = $this->_em->getUnitOfWork()->createEntity($className, $data);
$oid = spl_object_hash($entity);
$this->_metadataMap[$oid] = $this->_em->getClassMetadata($className);
return $entity;
}
/**
* Adds an element to an indexed collection-valued property.
*
* @param <type> $entity1
* @param <type> $property
* @param <type> $entity2
* @param <type> $indexField
*/
private function addRelatedIndexedElement($entity1, $property, $entity2, $indexField)
{
$classMetadata1 = $this->_metadataMap[spl_object_hash($entity1)];
$classMetadata2 = $this->_metadataMap[spl_object_hash($entity2)];
$indexValue = $classMetadata2->getReflectionProperty($indexField)->getValue($entity2);
$classMetadata1->getReflectionProperty($property)->getValue($entity1)->set($indexValue, $entity2);
}
/**
* Adds an element to a collection-valued property.
*
* @param <type> $entity1
* @param <type> $property
* @param <type> $entity2
*/
private function addRelatedElement($entity1, $property, $entity2)
{
$classMetadata1 = $this->_metadataMap[spl_object_hash($entity1)];
$classMetadata1->getReflectionProperty($property)->getValue($entity1)->add($entity2);
}
private function isFieldSet($entity, $field)
{
return $this->_metadataMap[spl_object_hash($entity)]->getReflectionProperty($field)
->getValue($entity) !== null;
}
/**
* Sets a related element.
*
* @param <type> $entity1
* @param <type> $property
* @param <type> $entity2
*/
private function setRelatedElement($entity1, $property, $entity2)
{
$oid = spl_object_hash($entity1);
$classMetadata1 = $this->_metadataMap[$oid];
$classMetadata1->getReflectionProperty($property)->setValue($entity1, $entity2);
$this->_uow->setOriginalEntityProperty($oid, $property, $entity2);
$relation = $classMetadata1->getAssociationMapping($property);
if ($relation->isOneToOne()) {
$targetClass = $this->_em->getClassMetadata($relation->getTargetEntityName());
if ($relation->isOwningSide()) {
// If there is an inverse mapping on the target class its bidirectional
if ($targetClass->hasInverseAssociationMapping($property)) {
$oid2 = spl_object_hash($entity2);
$sourceProp = $targetClass->getInverseAssociationMapping($fieldName)->getSourceFieldName();
$targetClass->getReflectionProperty($sourceProp)->setValue($entity2, $entity1);
}
} else {
// for sure bidirectional, as there is no inverse side in unidirectional
$mappedByProp = $relation->getMappedByFieldName();
$targetClass->getReflectionProperty($mappedByProp)->setValue($entity2, $entity1);
}
}
}
/**
* Hydrates a single row.
*
* @param <type> $data The row data.
* @param <type> $cache The cache to use.
* @param <type> $result The result to append to.
* @override
*/
protected function _hydrateRow(array &$data, array &$cache, &$result)
{
// 1) Initialize
$id = $this->_idTemplate; // initialize the id-memory
$nonemptyComponents = array();
$rowData = parent::_gatherRowData($data, $cache, $id, $nonemptyComponents);
$rootAlias = $this->_rootAlias;
// 2) Hydrate the data of the root entity from the current row
// Check for an existing element
$index = false;
if ($this->_isSimpleQuery || ! isset($this->_identifierMap[$rootAlias][$id[$rootAlias]])) {
$element = $this->_uow->createEntity($this->_rootEntityName, $rowData[$rootAlias]);
$oid = spl_object_hash($element);
$this->_metadataMap[$oid] = $this->_em->getClassMetadata($this->_rootEntityName);
if ($field = $this->_getCustomIndexField($rootAlias)) {
if ($this->_parserResult->isMixedQuery()) {
$result[] = array(
$this->_metadataMap[$oid]->getReflectionProperty($field)
->getValue($element) => $element
);
++$this->_resultCounter;
} else {
$result->set($element, $this->_metadataMap[$oid]
->getReflectionProperty($field)
->getValue($element));
}
} else {
if ($this->_parserResult->isMixedQuery()) {
$result[] = array($element);
++$this->_resultCounter;
} else {
$result->add($element);
}
}
$this->_identifierMap[$rootAlias][$id[$rootAlias]] = $this->getLastKey($result);
} else {
$index = $this->_identifierMap[$rootAlias][$id[$rootAlias]];
}
$this->updateResultPointer($result, $index, $rootAlias, false);
unset($rowData[$rootAlias]);
// end hydrate data of the root component for the current row
// Extract scalar values. They're appended at the end.
if (isset($rowData['scalars'])) {
$scalars = $rowData['scalars'];
unset($rowData['scalars']);
}
// 3) Now hydrate the rest of the data found in the current row, that
// belongs to other (related) entities.
foreach ($rowData as $dqlAlias => $data) {
$index = false;
$map = $this->_queryComponents[$dqlAlias];
$entityName = $map['metadata']->getClassName();
$parent = $map['parent'];
$relationAlias = $map['relation']->getSourceFieldName();
$path = $parent . '.' . $dqlAlias;
// Get a reference to the right element in the result tree.
// This element will get the associated element attached.
if ($this->_parserResult->isMixedQuery() && $parent == $rootAlias) {
$key = key(reset($this->_resultPointers));
// TODO: Exception if $key === null ?
$baseElement =& $this->_resultPointers[$parent][$key];
} else if (isset($this->_resultPointers[$parent])) {
$baseElement =& $this->_resultPointers[$parent];
} else {
unset($this->_resultPointers[$dqlAlias]); // Ticket #1228
continue;
}
$oid = spl_object_hash($baseElement);
// Check the type of the relation (many or single-valued)
if ( ! $map['relation']->isOneToOne()) {
$oneToOne = false;
if (isset($nonemptyComponents[$dqlAlias])) {
$this->initRelatedCollection($baseElement, $relationAlias);
$indexExists = isset($this->_identifierMap[$path][$id[$parent]][$id[$dqlAlias]]);
$index = $indexExists ? $this->_identifierMap[$path][$id[$parent]][$id[$dqlAlias]] : false;
$indexIsValid = $index !== false ? $this->isIndexKeyInUse($baseElement, $relationAlias, $index) : false;
if ( ! $indexExists || ! $indexIsValid) {
$element = $this->getElement($data, $entityName);
if ($field = $this->_getCustomIndexField($dqlAlias)) {
$this->addRelatedIndexedElement($baseElement, $relationAlias, $element, $field);
} else {
$this->addRelatedElement($baseElement, $relationAlias, $element);
}
$this->_identifierMap[$path][$id[$parent]][$id[$dqlAlias]] = $this->getLastKey(
$this->_metadataMap[$oid]
->getReflectionProperty($relationAlias)
->getValue($baseElement));
}
} else if ( ! $this->isFieldSet($baseElement, $relationAlias)) {
$coll = new Doctrine_ORM_Collection($this->_em, $entityName);
$this->_collections[] = $coll;
$this->setRelatedElement($baseElement, $relationAlias, $coll);
}
} else {
$oneToOne = true;
if ( ! isset($nonemptyComponents[$dqlAlias]) &&
! $this->isFieldSet($baseElement, $relationAlias)) {
$this->setRelatedElement($baseElement, $relationAlias, null);
} else if ( ! $this->isFieldSet($baseElement, $relationAlias)) {
$this->setRelatedElement($baseElement, $relationAlias,
$this->getElement($data, $entityName));
}
}
$coll = $this->_metadataMap[$oid]
->getReflectionProperty($relationAlias)
->getValue($baseElement);
if ($coll !== null) {
$this->updateResultPointer($coll, $index, $dqlAlias, $oneToOne);
}
}
// Append scalar values to mixed result sets
if (isset($scalars)) {
foreach ($scalars as $name => $value) {
$result[$this->_resultCounter - 1][$name] = $value;
}
}
}
/** {@inheritdoc} */
protected function _getRowContainer()
{
return new Doctrine_Common_Collections_Collection;
}
}

View File

@ -0,0 +1,36 @@
<?php
/**
* Hydrator that produces flat, rectangular results of scalar data.
* The created result is almost the same as a regular SQL result set, except
* that column names are mapped to field names and data type conversions.
*
* @author robo
* @since 2.0
*/
class Doctrine_ORM_Internal_Hydration_ScalarHydrator extends Doctrine_ORM_Internal_Hydration_AbstractHydrator
{
/** @override */
protected function _hydrateAll($parserResult)
{
$result = array();
$cache = array();
while ($data = $this->_stmt->fetch(PDO::FETCH_ASSOC)) {
$result[] = $this->_gatherScalarRowData($data, $cache);
}
return $result;
}
/** @override */
protected function _hydrateRow(array &$data, array &$cache, &$result)
{
$result[] = $this->_gatherScalarRowData($data, $cache);
}
/** @override */
protected function _getRowContainer()
{
return array();
}
}

View File

@ -0,0 +1,33 @@
<?php
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
/**
* Description of SingleScalarHydrator
*
* @author robo
*/
class Doctrine_ORM_Internal_Hydration_SingleScalarHydrator extends Doctrine_ORM_Internal_Hydration_AbstractHydrator
{
/** @override */
protected function _hydrateAll($parserResult)
{
$cache = array();
$result = $this->_stmt->fetchAll(PDO::FETCH_ASSOC);
//TODO: Let this exception be raised by Query as QueryException
if (count($result) > 1 || count($result[0]) > 1) {
throw Doctrine_ORM_Exceptions_HydrationException::nonUniqueResult();
}
$result = $this->_gatherScalarRowData($result[0], $cache);
return array_shift($result);
}
/** {@inheritdoc} */
protected function _getRowContainer()
{
return array();
}
}

View File

@ -50,7 +50,7 @@
* for the "numRowsInResult * numColumnsInResult" part is crucial to fast hydration.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.phpdoctrine.org
* @link www.doctrine-project.org
* @since 1.0
* @version $Revision: 3192 $
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
@ -290,266 +290,4 @@ class Doctrine_ORM_Internal_Hydration_StandardHydrator extends Doctrine_ORM_Inte
return $result;
}
/**
* Processes a row of the result set.
* Used for identity hydration (HYDRATE_IDENTITY_OBJECT and HYDRATE_IDENTITY_ARRAY).
* Puts the elements of a result row into a new array, grouped by the class
* they belong to. The column names in the result set are mapped to their
* field names during this procedure as well as any necessary conversions on
* the values applied.
*
* @return array An array with all the fields (name => value) of the data row,
* grouped by their component (alias).
* @todo Significant code duplication with _gatherScalarRowData(). Good refactoring
* possible without sacrificing performance?
*/
protected function _gatherRowData(&$data, &$cache, &$id, &$nonemptyComponents)
{
$rowData = array();
foreach ($data as $key => $value) {
// Parse each column name only once. Cache the results.
if ( ! isset($cache[$key])) {
if ($this->_isIgnoredName($key)) continue;
// Cache general information like the column name <-> field name mapping
$e = explode(Doctrine_ORM_Query_ParserRule::SQLALIAS_SEPARATOR, $key);
$columnName = array_pop($e);
$cache[$key]['dqlAlias'] = $this->_tableAliases[
implode(Doctrine_ORM_Query_ParserRule::SQLALIAS_SEPARATOR, $e)
];
$classMetadata = $this->_queryComponents[$cache[$key]['dqlAlias']]['metadata'];
// check whether it's an aggregate value or a regular field
if (isset($this->_queryComponents[$cache[$key]['dqlAlias']]['agg'][$columnName])) {
$fieldName = $this->_queryComponents[$cache[$key]['dqlAlias']]['agg'][$columnName];
$cache[$key]['isScalar'] = true;
} else {
$fieldName = $this->_lookupFieldName($classMetadata, $columnName);
$cache[$key]['isScalar'] = false;
// cache type information
$cache[$key]['type'] = $classMetadata->getTypeOfColumn($columnName);
}
$cache[$key]['fieldName'] = $fieldName;
// cache identifier information
if ($classMetadata->isIdentifier($fieldName)) {
$cache[$key]['isIdentifier'] = true;
} else {
$cache[$key]['isIdentifier'] = false;
}
}
$class = $this->_queryComponents[$cache[$key]['dqlAlias']]['metadata'];
$dqlAlias = $cache[$key]['dqlAlias'];
$fieldName = $cache[$key]['fieldName'];
if ($cache[$key]['isScalar']) {
$rowData['scalars'][$fieldName] = $value;
continue;
}
if ($cache[$key]['isIdentifier']) {
$id[$dqlAlias] .= '|' . $value;
}
if ($cache[$key]['isScalar']) {
$rowData[$dqlAlias][$fieldName] = $value;
} else {
$rowData[$dqlAlias][$fieldName] = $cache[$key]['type']->convertToPHPValue($value);
}
if ( ! isset($nonemptyComponents[$dqlAlias]) && $value !== null) {
$nonemptyComponents[$dqlAlias] = true;
}
}
return $rowData;
}
/**
* Processes a row of the result set.
* Used for HYDRATE_SCALAR. This is a variant of _gatherRowData() that
* simply converts column names to field names and properly prepares the
* values. The resulting row has the same number of elements as before.
*
* @param array $data
* @param array $cache
* @return array The processed row.
* @todo Significant code duplication with _gatherRowData(). Good refactoring
* possible without sacrificing performance?
*/
private function _gatherScalarRowData(&$data, &$cache)
{
$rowData = array();
foreach ($data as $key => $value) {
// Parse each column name only once. Cache the results.
if ( ! isset($cache[$key])) {
if ($this->_isIgnoredName($key)) continue;
// cache general information like the column name <-> field name mapping
$e = explode(Doctrine_ORM_Query_ParserRule::SQLALIAS_SEPARATOR, $key);
$columnName = array_pop($e);
$cache[$key]['dqlAlias'] = $this->_tableAliases[
implode(Doctrine_ORM_Query_ParserRule::SQLALIAS_SEPARATOR, $e)
];
$classMetadata = $this->_queryComponents[$cache[$key]['dqlAlias']]['metadata'];
// check whether it's an aggregate value or a regular field
if (isset($this->_queryComponents[$cache[$key]['dqlAlias']]['agg'][$columnName])) {
$fieldName = $this->_queryComponents[$cache[$key]['dqlAlias']]['agg'][$columnName];
$cache[$key]['isScalar'] = true;
} else {
$fieldName = $this->_lookupFieldName($classMetadata, $columnName);
$cache[$key]['isScalar'] = false;
// cache type information
$cache[$key]['type'] = $classMetadata->getTypeOfColumn($columnName);
}
$cache[$key]['fieldName'] = $fieldName;
}
$class = $this->_queryComponents[$cache[$key]['dqlAlias']]['metadata'];
$dqlAlias = $cache[$key]['dqlAlias'];
$fieldName = $cache[$key]['fieldName'];
if ($cache[$key]['isScalar']) {
$rowData[$dqlAlias . '_' . $fieldName] = $value;
} else {
$rowData[$dqlAlias . '_' . $fieldName] = $cache[$key]['type']->convertToPHPValue($value);
}
}
return $rowData;
}
/**
* Gets the custom field used for indexing for the specified component alias.
*
* @return string The field name of the field used for indexing or NULL
* if the component does not use any custom field indices.
*/
private function _getCustomIndexField($alias)
{
return isset($this->_queryComponents[$alias]['map']) ? $this->_queryComponents[$alias]['map'] : 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.
*
* This is mostly used during hydration, because we want to make the
* conversion to field names while iterating over the result set for best
* performance. By doing this at that point, we can avoid re-iterating over
* the data just to convert the column names to field names.
*
* However, when this is happening, we don't know the real
* class name to instantiate yet (the row data may target a sub-type), hence
* this method looks up the field name in the subclass mappings if it's not
* found on this class mapping.
* This lookup on subclasses is costly but happens only *once* for a column
* during hydration because the hydrator caches effectively.
*
* @return string The field name.
* @throws Doctrine::ORM::Exceptions::ClassMetadataException If the field name could
* not be found.
*/
private function _lookupFieldName($class, $lcColumnName)
{
if ($class->hasLowerColumn($lcColumnName)) {
return $class->getFieldNameForLowerColumnName($lcColumnName);
}
foreach ($class->getSubclasses() as $subClass) {
$subClassMetadata = Doctrine_ORM_Mapping_ClassMetadataFactory::getInstance()
->getMetadataFor($subClass);
if ($subClassMetadata->hasLowerColumn($lcColumnName)) {
return $subClassMetadata->getFieldNameForLowerColumnName($lcColumnName);
}
}
throw new Doctrine_Exception("No field name found for column name '$lcColumnName' during hydration.");
}
/**
* this method performs special data preparation depending on
* the type of the given column
*
* 1. It unserializes array and object typed columns
* 2. Uncompresses gzip typed columns
* 3. Gets the appropriate enum values for enum typed columns
* 4. Initializes special null object pointer for null values (for fast column existence checking purposes)
*
* example:
* <code type='php'>
* $field = 'name';
* $value = null;
* $table->prepareValue($field, $value); // Doctrine_Null
* </code>
*
* @param string $field the name of the field
* @param string $value field value
* @param string $typeHint A hint on the type of the value. If provided, the type lookup
* for the field can be skipped. Used i.e. during hydration to
* improve performance on large and/or complex results.
* @return mixed prepared value
* @todo Remove. Should be handled by the Type classes. No need for this switch stuff.
*/
public function prepareValue(Doctrine_ORM_Mapping_ClassMetadata $class, $fieldName, $value, $typeHint = null)
{
if ($value === $this->_nullObject) {
return $this->_nullObject;
} else if ($value === null) {
return null;
} else {
$type = is_null($typeHint) ? $class->getTypeOf($fieldName) : $typeHint;
switch ($type) {
case 'integer':
case 'string':
case 'enum':
case 'boolean':
// don't do any conversions on primitive types
break;
case 'array':
case 'object':
if (is_string($value)) {
$value = unserialize($value);
if ($value === false) {
throw new Doctrine_Hydrator_Exception('Unserialization of ' . $fieldName . ' failed.');
}
return $value;
}
break;
case 'gzip':
$value = gzuncompress($value);
if ($value === false) {
throw new Doctrine_Hydrator_Exception('Uncompressing of ' . $fieldName . ' failed.');
}
return $value;
break;
}
}
return $value;
}
/** Needed only temporarily until the new parser is ready */
private $_isResultMixed = false;
public function setResultMixed($bool)
{
$this->_isResultMixed = $bool;
}
}

View File

@ -65,9 +65,9 @@ class Doctrine_ORM_Query extends Doctrine_ORM_Query_Abstract
protected $_entityManager;
/**
* @var Doctrine\ORM\Internal\Hydrator The hydrator object used to hydrate query results.
* @var integer The hydration mode.
*/
protected $_hydrator;
protected $_hydrationMode = self::HYDRATE_OBJECT;
/**
* @var Doctrine\ORM\Query\ParserResult The parser result that holds DQL => SQL information.
@ -125,12 +125,11 @@ class Doctrine_ORM_Query extends Doctrine_ORM_Query_Abstract
public function __construct(Doctrine_ORM_EntityManager $entityManager)
{
$this->_entityManager = $entityManager;
$this->_hydrator = new Doctrine_ORM_Internal_Hydration_StandardHydrator($entityManager);
$this->free();
}
/**
* Retrieves the assocated EntityManager to this Doctrine_ORM_Query
* Retrieves the assocated EntityManager to this Query instance.
*
* @return Doctrine\ORM\EntityManager
*/
@ -139,23 +138,14 @@ class Doctrine_ORM_Query extends Doctrine_ORM_Query_Abstract
return $this->_entityManager;
}
/**
* Returns the hydrator associated with this query object
*
* @return Doctrine\ORM\Internal\StandardHydrator The hydrator associated with this query object
*/
public function getHydrator()
{
return $this->_hydrator;
}
/**
* Convenience method to execute using array fetching as hydration mode.
*
* @param string $params
* @return array
*/
public function fetchArray($params = array()) {
public function fetchArray($params = array())
{
return $this->execute($params, self::HYDRATE_ARRAY);
}
@ -187,9 +177,9 @@ class Doctrine_ORM_Query extends Doctrine_ORM_Query_Abstract
/**
* Query the database with DQL (Doctrine Query Language).
*
* @param string $query DQL query
* @param array $params prepared statement parameters
* @param int $hydrationMode Doctrine::FETCH_ARRAY or Doctrine::FETCH_RECORD
* @param string $query The DQL query.
* @param array $params The query parameters.
* @param int $hydrationMode
* @return mixed
*/
public function query($query, $params = array(), $hydrationMode = null)
@ -199,8 +189,7 @@ class Doctrine_ORM_Query extends Doctrine_ORM_Query_Abstract
}
/**
* Builds the sql query from the given parameters and applies things such as
* column aggregation inheritance and limit subqueries if needed
* Gets the SQL query/queries that correspond to this DQL query.
*
* @return mixed The built sql query or an array of all sql queries.
*/
@ -211,8 +200,10 @@ class Doctrine_ORM_Query extends Doctrine_ORM_Query_Abstract
/**
* Parses the DQL query, if necessary, and stores the parser result.
*
* Note: Populates $this->_parserResult as a side-effect.
*
* @return Doctrine_ORM_Query_ParserResult
* @return Doctrine\ORM\Query\ParserResult
*/
public function parse()
{
@ -235,9 +226,13 @@ class Doctrine_ORM_Query extends Doctrine_ORM_Query_Abstract
*/
public function execute($params = array(), $hydrationMode = null)
{
if ($hydrationMode !== null) {
$this->_hydrationMode = $hydrationMode;
}
$params = $this->getParams($params);
// If there is a CacheDriver associated to cache resultsets...
// Check result cache
if ($this->_resultCache && $this->_type === self::SELECT) { // Only executes if "SELECT"
$cacheDriver = $this->getResultCacheDriver();
@ -259,42 +254,23 @@ class Doctrine_ORM_Query extends Doctrine_ORM_Query_Abstract
return $queryResult->getResultSet();
}
}
return $this->_execute($params, $hydrationMode);
}
/**
* _execute
*
* @param string $params Parameters to be sent to query.
* @param int $hydrationMode Method of hydration to be used.
* @return Doctrine_Collection The root collection
*/
protected function _execute($params, $hydrationMode)
{
// preQuery invoking
$this->preQuery();
// Query execution
$stmt = $this->_execute2($params);
// postQuery invoking
$this->postQuery();
$stmt = $this->_execute($params);
if (is_integer($stmt)) {
return $stmt;
}
return $this->_hydrator->hydrateResultSet($stmt, $hydrationMode);
return $this->_em->getHydrator($this->_hydrationMode)->hydrateAll($stmt, $this->_parserResult);
}
/**
* _execute2
* _execute
*
* @param array $params
* @return PDOStatement The executed PDOStatement.
*/
protected function _execute2($params)
protected function _execute(array $params)
{
// If there is a CacheDriver associated to cache queries...
if ($this->_queryCache || $this->_entityManager->getConnection()->getAttribute(Doctrine::ATTR_QUERY_CACHE)) {
@ -320,9 +296,7 @@ class Doctrine_ORM_Query extends Doctrine_ORM_Query_Abstract
$executor = $this->parse()->getSqlExecutor();
}
// Assignments for Hydrator and Enums
$this->_hydrator->setQueryComponents($this->_parserResult->getQueryComponents());
$this->_hydrator->setTableAliasMap($this->_parserResult->getTableAliasMap());
// Assignments for Enums
$this->_setEnumParams($this->_parserResult->getEnumParams());
// Converting parameters
@ -335,7 +309,7 @@ class Doctrine_ORM_Query extends Doctrine_ORM_Query_Abstract
$params = array_merge($params, $params);
}
// Executing the query and assigning PDOStatement
// Executing the query and returning statement
return $executor->execute($this->_conn, $params);
}
@ -354,17 +328,16 @@ class Doctrine_ORM_Query extends Doctrine_ORM_Query_Abstract
/**
* Defines a cache driver to be used for caching result sets.
*
* @param Doctrine_Cache_Interface|null $driver Cache driver
* @return Doctrine_ORM_Query
* @param Doctrine\ORM\Cache\Cache $driver Cache driver
* @return Doctrine\ORM\Query
*/
public function setResultCache($resultCache)
{
if ($resultCache !== null && ! ($resultCache instanceof Doctrine_Cache_Interface)) {
if ($resultCache !== null && ! ($resultCache instanceof Doctrine_ORM_Cache_Cache)) {
throw new Doctrine_ORM_Query_Exception(
'Method setResultCache() accepts only an instance of Doctrine_Cache_Interface or null.'
);
}
$this->_resultCache = $resultCache;
return $this;
@ -377,7 +350,7 @@ class Doctrine_ORM_Query extends Doctrine_ORM_Query_Abstract
*/
public function getResultCache()
{
if ($this->_resultCache instanceof Doctrine_ORM_Cache_Interface) {
if ($this->_resultCache instanceof Doctrine_ORM_Cache_Cache) {
return $this->_resultCache;
} else {
return $this->_entityManager->getConnection()->getResultCacheDriver();
@ -388,7 +361,7 @@ class Doctrine_ORM_Query extends Doctrine_ORM_Query_Abstract
* Defines how long the result cache will be active before expire.
*
* @param integer $timeToLive How long the cache entry is valid
* @return Doctrine_ORM_Query
* @return Doctrine\ORM\Query
*/
public function setResultCacheLifetime($timeToLive)
{
@ -442,7 +415,7 @@ class Doctrine_ORM_Query extends Doctrine_ORM_Query_Abstract
*/
public function setQueryCache($queryCache)
{
if ($queryCache !== null && ! ($queryCache instanceof Doctrine_ORM_Cache_Interface)) {
if ($queryCache !== null && ! ($queryCache instanceof Doctrine_ORM_Cache_Cache)) {
throw new Doctrine_ORM_Query_Exception(
'Method setResultCache() accepts only an instance of Doctrine_ORM_Cache_Interface or null.'
);
@ -460,7 +433,7 @@ class Doctrine_ORM_Query extends Doctrine_ORM_Query_Abstract
*/
public function getQueryCache()
{
if ($this->_queryCache instanceof Doctrine_ORM_Cache_Interface) {
if ($this->_queryCache instanceof Doctrine_ORM_Cache_Cache) {
return $this->_queryCache;
} else {
return $this->_entityManager->getConnection()->getQueryCacheDriver();
@ -522,46 +495,19 @@ class Doctrine_ORM_Query extends Doctrine_ORM_Query_Abstract
*
* @param integer $hydrationMode Doctrine processing mode to be used during hydration process.
* One of the Doctrine::HYDRATE_* constants.
* @return Doctrine_ORM_Query
* @return Doctrine\ORM\Query
*/
public function setHydrationMode($hydrationMode)
{
$this->_hydrator->setHydrationMode($hydrationMode);
$this->_hydrationMode = $hydrationMode;
return $this;
}
/**
* Empty template method to provide Query subclasses with the possibility
* to hook into the query building procedure, doing any custom / specialized
* query building procedures that are neccessary.
*
* @return void
* @deprecated Should be removed. Extending Query is no good solution. Should
* Things like this should be done through listeners.
*/
public function preQuery()
{
}
/**
* Empty template method to provide Query subclasses with the possibility
* to hook into the query building procedure, doing any custom / specialized
* post query procedures (for example logging) that are neccessary.
*
* @return void
* @deprecated Should be removed. Extending Query is no good solution. Should
* Things like this should be done through listeners.
*/
public function postQuery()
{
}
/**
* Gets the list of results for the query.
*
* Alias for execute(array(), $hydrationMode).
*
* @param integer $hydrationMode
* @return mixed
*/
@ -613,23 +559,15 @@ class Doctrine_ORM_Query extends Doctrine_ORM_Query_Abstract
}
/**
* This method is automatically called when this Doctrine_Hydrate is serialized.
* Executes the query and returns an IterableResult that can be iterated over.
* Objects in the result are initialized on-demand.
*
* @return array An array of serialized properties
* @return IterableResult
*/
public function serialize()
public function iterate(array $params = array(), $hydrationMode = self::HYDRATE_OBJECT)
{
$vars = get_object_vars($this);
}
/**
* This method is automatically called everytime a Doctrine_Hydrate object is unserialized.
*
* @param string $serialized Doctrine_Record as serialized string
* @return void
*/
public function unserialize($serialized)
{
return $this->_em->getHydrator($this->_hydrationMode)->iterate(
$this->_execute($params, $hydrationMode), $this->_parserResult
);
}
}

View File

@ -54,6 +54,11 @@ class Doctrine_ORM_Query_ParserResultDummy
{
return $this->_tableToClassAliasMap;
}
public function getTableAliasMap()
{
return $this->_tableToClassAliasMap;
}
public function setTableToClassAliasMap(array $map)
{

View File

@ -68,4 +68,4 @@ class Doctrine_ORM_VirtualProxy
unset($realInstance->$prop);
}
}
?>

View File

@ -6,7 +6,11 @@ if (!defined('PHPUnit_MAIN_METHOD')) {
require_once 'lib/DoctrineTestInit.php';
// Tests
require_once 'Orm/Hydration/BasicHydrationTest.php';
//require_once 'Orm/Hydration/BasicHydrationTest.php';
require_once 'Orm/Hydration/ObjectHydratorTest.php';
require_once 'Orm/Hydration/ArrayHydratorTest.php';
require_once 'Orm/Hydration/ScalarHydratorTest.php';
require_once 'Orm/Hydration/SingleScalarHydratorTest.php';
class Orm_Hydration_AllTests
{
@ -19,7 +23,11 @@ class Orm_Hydration_AllTests
{
$suite = new Doctrine_TestSuite('Doctrine Orm Hydration');
$suite->addTestSuite('Orm_Hydration_BasicHydrationTest');
//$suite->addTestSuite('Orm_Hydration_BasicHydrationTest');
$suite->addTestSuite('Orm_Hydration_ObjectHydratorTest');
$suite->addTestSuite('Orm_Hydration_ArrayHydratorTest');
$suite->addTestSuite('Orm_Hydration_ScalarHydratorTest');
$suite->addTestSuite('Orm_Hydration_SingleScalarHydratorTest');
return $suite;
}

View File

@ -0,0 +1,712 @@
<?php
require_once dirname(__FILE__) . DIRECTORY_SEPARATOR . 'HydrationTest.php';
/**
* Description of ArrayHydratorTest
*
* @author robo
*/
class Orm_Hydration_ArrayHydratorTest extends Orm_Hydration_HydrationTest
{
/**
* Select u.id, u.name from CmsUser u
*/
public function testNewHydrationSimpleEntityQuery()
{
// Faked query components
$queryComponents = array(
'u' => array(
'metadata' => $this->_em->getClassMetadata('CmsUser'),
'parent' => null,
'relation' => null,
'map' => null
)
);
// Faked table alias map
$tableAliasMap = array(
'u' => 'u'
);
// Faked result set
$resultSet = array(
array(
'u__id' => '1',
'u__name' => 'romanb'
),
array(
'u__id' => '2',
'u__name' => 'jwage'
)
);
$stmt = new Doctrine_HydratorMockStatement($resultSet);
$hydrator = new Doctrine_ORM_Internal_Hydration_ArrayHydrator($this->_em);
$result = $hydrator->hydrateAll($stmt, $this->_createParserResult(
$stmt, $queryComponents, $tableAliasMap, Doctrine_ORM_Query::HYDRATE_ARRAY));
$this->assertEquals(2, count($result));
$this->assertTrue(is_array($result));
$this->assertEquals(1, $result[0]['id']);
$this->assertEquals('romanb', $result[0]['name']);
$this->assertEquals(2, $result[1]['id']);
$this->assertEquals('jwage', $result[1]['name']);
}
/**
* select u.id, u.status, p.phonenumber, upper(u.name) nameUpper from User u
* join u.phonenumbers p
* =
* select u.id, u.status, p.phonenumber, upper(u.name) as u__0 from USERS u
* INNER JOIN PHONENUMBERS p ON u.id = p.user_id
*/
public function testNewHydrationMixedQueryFetchJoin()
{
// Faked query components
$queryComponents = array(
'u' => array(
'metadata' => $this->_em->getClassMetadata('CmsUser'),
'parent' => null,
'relation' => null,
'map' => null,
'agg' => array('0' => 'nameUpper')
),
'p' => array(
'metadata' => $this->_em->getClassMetadata('CmsPhonenumber'),
'parent' => 'u',
'relation' => $this->_em->getClassMetadata('CmsUser')->getAssociationMapping('phonenumbers'),
'map' => null
)
);
// Faked table alias map
$tableAliasMap = array(
'u' => 'u',
'p' => 'p'
);
// Faked result set
$resultSet = array(
//row1
array(
'u__id' => '1',
'u__status' => 'developer',
'u__0' => 'ROMANB',
'p__phonenumber' => '42',
),
array(
'u__id' => '1',
'u__status' => 'developer',
'u__0' => 'ROMANB',
'p__phonenumber' => '43',
),
array(
'u__id' => '2',
'u__status' => 'developer',
'u__0' => 'JWAGE',
'p__phonenumber' => '91'
)
);
$stmt = new Doctrine_HydratorMockStatement($resultSet);
$hydrator = new Doctrine_ORM_Internal_Hydration_ArrayHydrator($this->_em);
$result = $hydrator->hydrateAll($stmt, $this->_createParserResult(
$stmt, $queryComponents, $tableAliasMap, Doctrine_ORM_Query::HYDRATE_ARRAY, true));
$this->assertEquals(2, count($result));
$this->assertTrue(is_array($result));
$this->assertTrue(is_array($result[0]));
$this->assertTrue(is_array($result[1]));
// first user => 2 phonenumbers
$this->assertEquals(2, count($result[0][0]['phonenumbers']));
$this->assertEquals('ROMANB', $result[0]['nameUpper']);
// second user => 1 phonenumber
$this->assertEquals(1, count($result[1][0]['phonenumbers']));
$this->assertEquals('JWAGE', $result[1]['nameUpper']);
$this->assertEquals(42, $result[0][0]['phonenumbers'][0]['phonenumber']);
$this->assertEquals(43, $result[0][0]['phonenumbers'][1]['phonenumber']);
$this->assertEquals(91, $result[1][0]['phonenumbers'][0]['phonenumber']);
}
/**
* select u.id, u.status, count(p.phonenumber) numPhones from User u
* join u.phonenumbers p group by u.status, u.id
* =
* select u.id, u.status, count(p.phonenumber) as p__0 from USERS u
* INNER JOIN PHONENUMBERS p ON u.id = p.user_id group by u.id, u.status
*/
public function testNewHydrationMixedQueryNormalJoin()
{
// Faked query components
$queryComponents = array(
'u' => array(
'metadata' => $this->_em->getClassMetadata('CmsUser'),
'parent' => null,
'relation' => null,
'map' => null
),
'p' => array(
'metadata' => $this->_em->getClassMetadata('CmsPhonenumber'),
'parent' => 'u',
'relation' => $this->_em->getClassMetadata('CmsUser')->getAssociationMapping('phonenumbers'),
'map' => null,
'agg' => array('0' => 'numPhones')
)
);
// Faked table alias map
$tableAliasMap = array(
'u' => 'u',
'p' => 'p'
);
// Faked result set
$resultSet = array(
//row1
array(
'u__id' => '1',
'u__status' => 'developer',
'p__0' => '2',
),
array(
'u__id' => '2',
'u__status' => 'developer',
'p__0' => '1',
)
);
$stmt = new Doctrine_HydratorMockStatement($resultSet);
$hydrator = new Doctrine_ORM_Internal_Hydration_ArrayHydrator($this->_em);
$result = $hydrator->hydrateAll($stmt, $this->_createParserResult(
$stmt, $queryComponents, $tableAliasMap, Doctrine_ORM_Query::HYDRATE_ARRAY, true));
$this->assertEquals(2, count($result));
$this->assertTrue(is_array($result));
$this->assertTrue(is_array($result[0]));
$this->assertTrue(is_array($result[1]));
// first user => 2 phonenumbers
$this->assertEquals(2, $result[0]['numPhones']);
// second user => 1 phonenumber
$this->assertEquals(1, $result[1]['numPhones']);
}
/**
* select u.id, u.status, upper(u.name) nameUpper from User u index by u.id
* join u.phonenumbers p indexby p.phonenumber
* =
* select u.id, u.status, upper(u.name) as p__0 from USERS u
* INNER JOIN PHONENUMBERS p ON u.id = p.user_id
*/
public function testNewHydrationMixedQueryFetchJoinCustomIndex()
{
// Faked query components
$queryComponents = array(
'u' => array(
'metadata' => $this->_em->getClassMetadata('CmsUser'),
'parent' => null,
'relation' => null,
'agg' => array('0' => 'nameUpper'),
'map' => 'id'
),
'p' => array(
'metadata' => $this->_em->getClassMetadata('CmsPhonenumber'),
'parent' => 'u',
'relation' => $this->_em->getClassMetadata('CmsUser')->getAssociationMapping('phonenumbers'),
'map' => 'phonenumber'
)
);
// Faked table alias map
$tableAliasMap = array(
'u' => 'u',
'p' => 'p'
);
// Faked result set
$resultSet = array(
//row1
array(
'u__id' => '1',
'u__status' => 'developer',
'u__0' => 'ROMANB',
'p__phonenumber' => '42',
),
array(
'u__id' => '1',
'u__status' => 'developer',
'u__0' => 'ROMANB',
'p__phonenumber' => '43',
),
array(
'u__id' => '2',
'u__status' => 'developer',
'u__0' => 'JWAGE',
'p__phonenumber' => '91'
)
);
$stmt = new Doctrine_HydratorMockStatement($resultSet);
$hydrator = new Doctrine_ORM_Internal_Hydration_ArrayHydrator($this->_em);
$result = $hydrator->hydrateAll($stmt, $this->_createParserResult(
$stmt, $queryComponents, $tableAliasMap, Doctrine_ORM_Query::HYDRATE_ARRAY, true));
$this->assertEquals(2, count($result));
$this->assertTrue(is_array($result));
$this->assertTrue(is_array($result[0]));
$this->assertTrue(is_array($result[1]));
// test the scalar values
$this->assertEquals('ROMANB', $result[0]['nameUpper']);
$this->assertEquals('JWAGE', $result[1]['nameUpper']);
// first user => 2 phonenumbers. notice the custom indexing by user id
$this->assertEquals(2, count($result[0]['1']['phonenumbers']));
// second user => 1 phonenumber. notice the custom indexing by user id
$this->assertEquals(1, count($result[1]['2']['phonenumbers']));
// test the custom indexing of the phonenumbers
$this->assertTrue(isset($result[0]['1']['phonenumbers']['42']));
$this->assertTrue(isset($result[0]['1']['phonenumbers']['43']));
$this->assertTrue(isset($result[1]['2']['phonenumbers']['91']));
}
/**
* select u.id, u.status, p.phonenumber, upper(u.name) nameUpper, a.id, a.topic
* from User u
* join u.phonenumbers p
* join u.articles a
* =
* select u.id, u.status, p.phonenumber, upper(u.name) as u__0, a.id, a.topic
* from USERS u
* inner join PHONENUMBERS p ON u.id = p.user_id
* inner join ARTICLES a ON u.id = a.user_id
*/
public function testNewHydrationMixedQueryMultipleFetchJoin()
{
// Faked query components
$queryComponents = array(
'u' => array(
'metadata' => $this->_em->getClassMetadata('CmsUser'),
'parent' => null,
'relation' => null,
'map' => null,
'agg' => array('0' => 'nameUpper')
),
'p' => array(
'metadata' => $this->_em->getClassMetadata('CmsPhonenumber'),
'parent' => 'u',
'relation' => $this->_em->getClassMetadata('CmsUser')->getAssociationMapping('phonenumbers'),
'map' => null
),
'a' => array(
'metadata' => $this->_em->getClassMetadata('CmsArticle'),
'parent' => 'u',
'relation' => $this->_em->getClassMetadata('CmsUser')->getAssociationMapping('articles'),
'map' => null
),
);
// Faked table alias map
$tableAliasMap = array(
'u' => 'u',
'p' => 'p',
'a' => 'a'
);
// Faked result set
$resultSet = array(
//row1
array(
'u__id' => '1',
'u__status' => 'developer',
'u__0' => 'ROMANB',
'p__phonenumber' => '42',
'a__id' => '1',
'a__topic' => 'Getting things done!'
),
array(
'u__id' => '1',
'u__status' => 'developer',
'u__0' => 'ROMANB',
'p__phonenumber' => '43',
'a__id' => '1',
'a__topic' => 'Getting things done!'
),
array(
'u__id' => '1',
'u__status' => 'developer',
'u__0' => 'ROMANB',
'p__phonenumber' => '42',
'a__id' => '2',
'a__topic' => 'ZendCon'
),
array(
'u__id' => '1',
'u__status' => 'developer',
'u__0' => 'ROMANB',
'p__phonenumber' => '43',
'a__id' => '2',
'a__topic' => 'ZendCon'
),
array(
'u__id' => '2',
'u__status' => 'developer',
'u__0' => 'JWAGE',
'p__phonenumber' => '91',
'a__id' => '3',
'a__topic' => 'LINQ'
),
array(
'u__id' => '2',
'u__status' => 'developer',
'u__0' => 'JWAGE',
'p__phonenumber' => '91',
'a__id' => '4',
'a__topic' => 'PHP6'
),
);
$stmt = new Doctrine_HydratorMockStatement($resultSet);
$hydrator = new Doctrine_ORM_Internal_Hydration_ArrayHydrator($this->_em);
$result = $hydrator->hydrateAll($stmt, $this->_createParserResult(
$stmt, $queryComponents, $tableAliasMap, Doctrine_ORM_Query::HYDRATE_ARRAY, true));
$this->assertEquals(2, count($result));
$this->assertTrue(is_array($result));
$this->assertTrue(is_array($result[0]));
$this->assertTrue(is_array($result[1]));
// first user => 2 phonenumbers, 2 articles
$this->assertEquals(2, count($result[0][0]['phonenumbers']));
$this->assertEquals(2, count($result[0][0]['articles']));
$this->assertEquals('ROMANB', $result[0]['nameUpper']);
// second user => 1 phonenumber, 2 articles
$this->assertEquals(1, count($result[1][0]['phonenumbers']));
$this->assertEquals(2, count($result[1][0]['articles']));
$this->assertEquals('JWAGE', $result[1]['nameUpper']);
$this->assertEquals(42, $result[0][0]['phonenumbers'][0]['phonenumber']);
$this->assertEquals(43, $result[0][0]['phonenumbers'][1]['phonenumber']);
$this->assertEquals(91, $result[1][0]['phonenumbers'][0]['phonenumber']);
$this->assertEquals('Getting things done!', $result[0][0]['articles'][0]['topic']);
$this->assertEquals('ZendCon', $result[0][0]['articles'][1]['topic']);
$this->assertEquals('LINQ', $result[1][0]['articles'][0]['topic']);
$this->assertEquals('PHP6', $result[1][0]['articles'][1]['topic']);
}
/**
* select u.id, u.status, p.phonenumber, upper(u.name) nameUpper, a.id, a.topic,
* c.id, c.topic
* from User u
* join u.phonenumbers p
* join u.articles a
* left join a.comments c
* =
* select u.id, u.status, p.phonenumber, upper(u.name) as u__0, a.id, a.topic,
* c.id, c.topic
* from USERS u
* inner join PHONENUMBERS p ON u.id = p.user_id
* inner join ARTICLES a ON u.id = a.user_id
* left outer join COMMENTS c ON a.id = c.article_id
*/
public function testNewHydrationMixedQueryMultipleDeepMixedFetchJoin()
{
// Faked query components
$queryComponents = array(
'u' => array(
'metadata' => $this->_em->getClassMetadata('CmsUser'),
'parent' => null,
'relation' => null,
'map' => null,
'agg' => array('0' => 'nameUpper')
),
'p' => array(
'metadata' => $this->_em->getClassMetadata('CmsPhonenumber'),
'parent' => 'u',
'relation' => $this->_em->getClassMetadata('CmsUser')->getAssociationMapping('phonenumbers'),
'map' => null
),
'a' => array(
'metadata' => $this->_em->getClassMetadata('CmsArticle'),
'parent' => 'u',
'relation' => $this->_em->getClassMetadata('CmsUser')->getAssociationMapping('articles'),
'map' => null
),
'c' => array(
'metadata' => $this->_em->getClassMetadata('CmsComment'),
'parent' => 'a',
'relation' => $this->_em->getClassMetadata('CmsArticle')->getAssociationMapping('comments'),
'map' => null
),
);
// Faked table alias map
$tableAliasMap = array(
'u' => 'u',
'p' => 'p',
'a' => 'a',
'c' => 'c'
);
// Faked result set
$resultSet = array(
//row1
array(
'u__id' => '1',
'u__status' => 'developer',
'u__0' => 'ROMANB',
'p__phonenumber' => '42',
'a__id' => '1',
'a__topic' => 'Getting things done!',
'c__id' => '1',
'c__topic' => 'First!'
),
array(
'u__id' => '1',
'u__status' => 'developer',
'u__0' => 'ROMANB',
'p__phonenumber' => '43',
'a__id' => '1',
'a__topic' => 'Getting things done!',
'c__id' => '1',
'c__topic' => 'First!'
),
array(
'u__id' => '1',
'u__status' => 'developer',
'u__0' => 'ROMANB',
'p__phonenumber' => '42',
'a__id' => '2',
'a__topic' => 'ZendCon',
'c__id' => null,
'c__topic' => null
),
array(
'u__id' => '1',
'u__status' => 'developer',
'u__0' => 'ROMANB',
'p__phonenumber' => '43',
'a__id' => '2',
'a__topic' => 'ZendCon',
'c__id' => null,
'c__topic' => null
),
array(
'u__id' => '2',
'u__status' => 'developer',
'u__0' => 'JWAGE',
'p__phonenumber' => '91',
'a__id' => '3',
'a__topic' => 'LINQ',
'c__id' => null,
'c__topic' => null
),
array(
'u__id' => '2',
'u__status' => 'developer',
'u__0' => 'JWAGE',
'p__phonenumber' => '91',
'a__id' => '4',
'a__topic' => 'PHP6',
'c__id' => null,
'c__topic' => null
),
);
$stmt = new Doctrine_HydratorMockStatement($resultSet);
$hydrator = new Doctrine_ORM_Internal_Hydration_ArrayHydrator($this->_em);
$result = $hydrator->hydrateAll($stmt, $this->_createParserResult(
$stmt, $queryComponents, $tableAliasMap, Doctrine_ORM_Query::HYDRATE_ARRAY, true));
$this->assertEquals(2, count($result));
$this->assertTrue(is_array($result));
$this->assertTrue(is_array($result[0]));
$this->assertTrue(is_array($result[1]));
// first user => 2 phonenumbers, 2 articles, 1 comment on first article
$this->assertEquals(2, count($result[0][0]['phonenumbers']));
$this->assertEquals(2, count($result[0][0]['articles']));
$this->assertEquals(1, count($result[0][0]['articles'][0]['comments']));
$this->assertEquals('ROMANB', $result[0]['nameUpper']);
// second user => 1 phonenumber, 2 articles, no comments
$this->assertEquals(1, count($result[1][0]['phonenumbers']));
$this->assertEquals(2, count($result[1][0]['articles']));
$this->assertEquals('JWAGE', $result[1]['nameUpper']);
$this->assertEquals(42, $result[0][0]['phonenumbers'][0]['phonenumber']);
$this->assertEquals(43, $result[0][0]['phonenumbers'][1]['phonenumber']);
$this->assertEquals(91, $result[1][0]['phonenumbers'][0]['phonenumber']);
$this->assertEquals('Getting things done!', $result[0][0]['articles'][0]['topic']);
$this->assertEquals('ZendCon', $result[0][0]['articles'][1]['topic']);
$this->assertEquals('LINQ', $result[1][0]['articles'][0]['topic']);
$this->assertEquals('PHP6', $result[1][0]['articles'][1]['topic']);
$this->assertEquals('First!', $result[0][0]['articles'][0]['comments'][0]['topic']);
$this->assertTrue(isset($result[0][0]['articles'][0]['comments']));
// empty comment collections
$this->assertTrue(is_array($result[0][0]['articles'][1]['comments']));
$this->assertEquals(0, count($result[0][0]['articles'][1]['comments']));
$this->assertTrue(is_array($result[1][0]['articles'][0]['comments']));
$this->assertEquals(0, count($result[1][0]['articles'][0]['comments']));
$this->assertTrue(is_array($result[1][0]['articles'][1]['comments']));
$this->assertEquals(0, count($result[1][0]['articles'][1]['comments']));
}
/**
* Tests that the hydrator does not rely on a particular order of the rows
* in the result set.
*
* DQL:
* select c.id, c.position, c.name, b.id, b.position
* from ForumCategory c inner join c.boards b
* order by c.position asc, b.position asc
*
* Checks whether the boards are correctly assigned to the categories.
*
* The 'evil' result set that confuses the object population is displayed below.
*
* c.id | c.position | c.name | boardPos | b.id | b.category_id (just for clarity)
* 1 | 0 | First | 0 | 1 | 1
* 2 | 0 | Second | 0 | 2 | 2 <--
* 1 | 0 | First | 1 | 3 | 1
* 1 | 0 | First | 2 | 4 | 1
*/
public function testNewHydrationEntityQueryCustomResultSetOrder()
{
// Faked query components
$queryComponents = array(
'c' => array(
'metadata' => $this->_em->getClassMetadata('ForumCategory'),
'parent' => null,
'relation' => null,
'map' => null
),
'b' => array(
'metadata' => $this->_em->getClassMetadata('ForumBoard'),
'parent' => 'c',
'relation' => $this->_em->getClassMetadata('ForumCategory')->getAssociationMapping('boards'),
'map' => null
),
);
// Faked table alias map
$tableAliasMap = array(
'c' => 'c',
'b' => 'b'
);
// Faked result set
$resultSet = array(
array(
'c__id' => '1',
'c__position' => '0',
'c__name' => 'First',
'b__id' => '1',
'b__position' => '0',
//'b__category_id' => '1'
),
array(
'c__id' => '2',
'c__position' => '0',
'c__name' => 'Second',
'b__id' => '2',
'b__position' => '0',
//'b__category_id' => '2'
),
array(
'c__id' => '1',
'c__position' => '0',
'c__name' => 'First',
'b__id' => '3',
'b__position' => '1',
//'b__category_id' => '1'
),
array(
'c__id' => '1',
'c__position' => '0',
'c__name' => 'First',
'b__id' => '4',
'b__position' => '2',
//'b__category_id' => '1'
)
);
$stmt = new Doctrine_HydratorMockStatement($resultSet);
$hydrator = new Doctrine_ORM_Internal_Hydration_ArrayHydrator($this->_em);
$result = $hydrator->hydrateAll($stmt, $this->_createParserResult(
$stmt, $queryComponents, $tableAliasMap, Doctrine_ORM_Query::HYDRATE_ARRAY));
$this->assertEquals(2, count($result));
$this->assertTrue(is_array($result));
$this->assertTrue(is_array($result[0]));
$this->assertTrue(is_array($result[1]));
$this->assertTrue(isset($result[0]['boards']));
$this->assertEquals(3, count($result[0]['boards']));
$this->assertTrue(isset($result[1]['boards']));
$this->assertEquals(1, count($result[1]['boards']));
}
public function testResultIteration() {
// Faked query components
$queryComponents = array(
'u' => array(
'metadata' => $this->_em->getClassMetadata('CmsUser'),
'parent' => null,
'relation' => null,
'map' => null
)
);
// Faked table alias map
$tableAliasMap = array(
'u' => 'u'
);
// Faked result set
$resultSet = array(
array(
'u__id' => '1',
'u__name' => 'romanb'
),
array(
'u__id' => '2',
'u__name' => 'jwage'
)
);
$stmt = new Doctrine_HydratorMockStatement($resultSet);
$hydrator = new Doctrine_ORM_Internal_Hydration_ArrayHydrator($this->_em);
$iterableResult = $hydrator->iterate($stmt, $this->_createParserResult(
$stmt, $queryComponents, $tableAliasMap, Doctrine_ORM_Query::HYDRATE_ARRAY));
$rowNum = 0;
while (($row = $iterableResult->next()) !== false) {
$this->assertEquals(1, count($row));
$this->assertTrue(is_array($row[0]));
if ($rowNum == 0) {
$this->assertEquals(1, $row[0]['id']);
$this->assertEquals('romanb', $row[0]['name']);
} else if ($rowNum == 1) {
$this->assertEquals(2, $row[0]['id']);
$this->assertEquals('jwage', $row[0]['name']);
}
++$rowNum;
}
}
}

View File

@ -686,7 +686,7 @@ class Orm_Hydration_BasicHydrationTest extends Doctrine_OrmTestCase
$stmt = new Doctrine_HydratorMockStatement($resultSet);
$hydrator = new Doctrine_ORM_Internal_Hydration_StandardHydrator($this->_em);
$result = $hydrator->hydrateResultSet($this->_createParserResult(
$stmt, $queryComponents, $tableAliasMap, $hydrationMode, true));
if ($hydrationMode == Doctrine_ORM_Query::HYDRATE_ARRAY) {

View File

@ -0,0 +1,34 @@
<?php
require_once 'lib/DoctrineTestInit.php';
require_once 'lib/mocks/Doctrine_HydratorMockStatement.php';
/**
* Description of HydrationTest
*
* @author robo
*/
class Orm_Hydration_HydrationTest extends Doctrine_OrmTestCase
{
protected $_em;
protected function setUp()
{
parent::setUp();
$this->_em = $this->_getTestEntityManager();
}
/** Helper method */
protected function _createParserResult($stmt, $queryComponents, $tableToClassAliasMap,
$hydrationMode, $isMixedQuery = false)
{
$parserResult = new Doctrine_ORM_Query_ParserResultDummy();
$parserResult->setDatabaseStatement($stmt);
$parserResult->setHydrationMode($hydrationMode);
$parserResult->setQueryComponents($queryComponents);
$parserResult->setTableToClassAliasMap($tableToClassAliasMap);
$parserResult->setMixedQuery($isMixedQuery);
return $parserResult;
}
}

View File

@ -0,0 +1,720 @@
<?php
require_once dirname(__FILE__) . DIRECTORY_SEPARATOR . 'HydrationTest.php';
/**
* Description of ObjectHydratorTest
*
* @author robo
*/
class Orm_Hydration_ObjectHydratorTest extends Orm_Hydration_HydrationTest
{
/**
* Select u.id, u.name from CmsUser u
*/
public function testNewHydrationSimpleEntityQuery()
{
// Faked query components
$queryComponents = array(
'u' => array(
'metadata' => $this->_em->getClassMetadata('CmsUser'),
'parent' => null,
'relation' => null,
'map' => null
)
);
// Faked table alias map
$tableAliasMap = array(
'u' => 'u'
);
// Faked result set
$resultSet = array(
array(
'u__id' => '1',
'u__name' => 'romanb'
),
array(
'u__id' => '2',
'u__name' => 'jwage'
)
);
$stmt = new Doctrine_HydratorMockStatement($resultSet);
$hydrator = new Doctrine_ORM_Internal_Hydration_ObjectHydrator($this->_em);
$result = $hydrator->hydrateAll($stmt, $this->_createParserResult(
$stmt, $queryComponents, $tableAliasMap, Doctrine_ORM_Query::HYDRATE_OBJECT));
$this->assertEquals(2, count($result));
$this->assertTrue($result instanceof Doctrine_ORM_Collection);
$this->assertTrue($result[0] instanceof CmsUser);
$this->assertTrue($result[1] instanceof CmsUser);
$this->assertEquals(1, $result[0]->id);
$this->assertEquals('romanb', $result[0]->name);
$this->assertEquals(2, $result[1]->id);
$this->assertEquals('jwage', $result[1]->name);
}
/**
* select u.id, u.status, p.phonenumber, upper(u.name) nameUpper from User u
* join u.phonenumbers p
* =
* select u.id, u.status, p.phonenumber, upper(u.name) as u__0 from USERS u
* INNER JOIN PHONENUMBERS p ON u.id = p.user_id
*/
public function testNewHydrationMixedQueryFetchJoin()
{
// Faked query components
$queryComponents = array(
'u' => array(
'metadata' => $this->_em->getClassMetadata('CmsUser'),
'parent' => null,
'relation' => null,
'map' => null,
'agg' => array('0' => 'nameUpper')
),
'p' => array(
'metadata' => $this->_em->getClassMetadata('CmsPhonenumber'),
'parent' => 'u',
'relation' => $this->_em->getClassMetadata('CmsUser')->getAssociationMapping('phonenumbers'),
'map' => null
)
);
// Faked table alias map
$tableAliasMap = array(
'u' => 'u',
'p' => 'p'
);
// Faked result set
$resultSet = array(
//row1
array(
'u__id' => '1',
'u__status' => 'developer',
'u__0' => 'ROMANB',
'p__phonenumber' => '42',
),
array(
'u__id' => '1',
'u__status' => 'developer',
'u__0' => 'ROMANB',
'p__phonenumber' => '43',
),
array(
'u__id' => '2',
'u__status' => 'developer',
'u__0' => 'JWAGE',
'p__phonenumber' => '91'
)
);
$stmt = new Doctrine_HydratorMockStatement($resultSet);
$hydrator = new Doctrine_ORM_Internal_Hydration_ObjectHydrator($this->_em);
$result = $hydrator->hydrateAll($stmt, $this->_createParserResult(
$stmt, $queryComponents, $tableAliasMap, Doctrine_ORM_Query::HYDRATE_OBJECT, true));
$this->assertEquals(2, count($result));
$this->assertTrue(is_array($result));
$this->assertTrue(is_array($result[0]));
$this->assertTrue(is_array($result[1]));
$this->assertTrue($result[0][0] instanceof CmsUser);
$this->assertTrue($result[0][0]->phonenumbers instanceof Doctrine_ORM_Collection);
$this->assertTrue($result[0][0]->phonenumbers[0] instanceof CmsPhonenumber);
$this->assertTrue($result[0][0]->phonenumbers[1] instanceof CmsPhonenumber);
$this->assertTrue($result[1][0] instanceof CmsUser);
$this->assertTrue($result[1][0]->phonenumbers instanceof Doctrine_ORM_Collection);
// first user => 2 phonenumbers
$this->assertEquals(2, count($result[0][0]->phonenumbers));
$this->assertEquals('ROMANB', $result[0]['nameUpper']);
// second user => 1 phonenumber
$this->assertEquals(1, count($result[1][0]->phonenumbers));
$this->assertEquals('JWAGE', $result[1]['nameUpper']);
$this->assertEquals(42, $result[0][0]->phonenumbers[0]->phonenumber);
$this->assertEquals(43, $result[0][0]->phonenumbers[1]->phonenumber);
$this->assertEquals(91, $result[1][0]->phonenumbers[0]->phonenumber);
}
/**
* select u.id, u.status, count(p.phonenumber) numPhones from User u
* join u.phonenumbers p group by u.status, u.id
* =
* select u.id, u.status, count(p.phonenumber) as p__0 from USERS u
* INNER JOIN PHONENUMBERS p ON u.id = p.user_id group by u.id, u.status
*/
public function testNewHydrationMixedQueryNormalJoin()
{
// Faked query components
$queryComponents = array(
'u' => array(
'metadata' => $this->_em->getClassMetadata('CmsUser'),
'parent' => null,
'relation' => null,
'map' => null
),
'p' => array(
'metadata' => $this->_em->getClassMetadata('CmsPhonenumber'),
'parent' => 'u',
'relation' => $this->_em->getClassMetadata('CmsUser')->getAssociationMapping('phonenumbers'),
'map' => null,
'agg' => array('0' => 'numPhones')
)
);
// Faked table alias map
$tableAliasMap = array(
'u' => 'u',
'p' => 'p'
);
// Faked result set
$resultSet = array(
//row1
array(
'u__id' => '1',
'u__status' => 'developer',
'p__0' => '2',
),
array(
'u__id' => '2',
'u__status' => 'developer',
'p__0' => '1',
)
);
$stmt = new Doctrine_HydratorMockStatement($resultSet);
$hydrator = new Doctrine_ORM_Internal_Hydration_ObjectHydrator($this->_em);
$result = $hydrator->hydrateAll($stmt, $this->_createParserResult(
$stmt, $queryComponents, $tableAliasMap, Doctrine_ORM_Query::HYDRATE_OBJECT, true));
$this->assertEquals(2, count($result));
$this->assertTrue(is_array($result));
$this->assertTrue(is_array($result[0]));
$this->assertTrue(is_array($result[1]));
// first user => 2 phonenumbers
$this->assertEquals(2, $result[0]['numPhones']);
// second user => 1 phonenumber
$this->assertEquals(1, $result[1]['numPhones']);
$this->assertTrue($result[0][0] instanceof CmsUser);
$this->assertTrue($result[1][0] instanceof CmsUser);
}
/**
* select u.id, u.status, upper(u.name) nameUpper from User u index by u.id
* join u.phonenumbers p indexby p.phonenumber
* =
* select u.id, u.status, upper(u.name) as p__0 from USERS u
* INNER JOIN PHONENUMBERS p ON u.id = p.user_id
*/
public function testNewHydrationMixedQueryFetchJoinCustomIndex()
{
// Faked query components
$queryComponents = array(
'u' => array(
'metadata' => $this->_em->getClassMetadata('CmsUser'),
'parent' => null,
'relation' => null,
'agg' => array('0' => 'nameUpper'),
'map' => 'id'
),
'p' => array(
'metadata' => $this->_em->getClassMetadata('CmsPhonenumber'),
'parent' => 'u',
'relation' => $this->_em->getClassMetadata('CmsUser')->getAssociationMapping('phonenumbers'),
'map' => 'phonenumber'
)
);
// Faked table alias map
$tableAliasMap = array(
'u' => 'u',
'p' => 'p'
);
// Faked result set
$resultSet = array(
//row1
array(
'u__id' => '1',
'u__status' => 'developer',
'u__0' => 'ROMANB',
'p__phonenumber' => '42',
),
array(
'u__id' => '1',
'u__status' => 'developer',
'u__0' => 'ROMANB',
'p__phonenumber' => '43',
),
array(
'u__id' => '2',
'u__status' => 'developer',
'u__0' => 'JWAGE',
'p__phonenumber' => '91'
)
);
$stmt = new Doctrine_HydratorMockStatement($resultSet);
$hydrator = new Doctrine_ORM_Internal_Hydration_ObjectHydrator($this->_em);
$result = $hydrator->hydrateAll($stmt, $this->_createParserResult(
$stmt, $queryComponents, $tableAliasMap, Doctrine_ORM_Query::HYDRATE_OBJECT, true));
$this->assertEquals(2, count($result));
$this->assertTrue(is_array($result));
$this->assertTrue(is_array($result[0]));
$this->assertTrue(is_array($result[1]));
// test the scalar values
$this->assertEquals('ROMANB', $result[0]['nameUpper']);
$this->assertEquals('JWAGE', $result[1]['nameUpper']);
$this->assertTrue($result[0]['1'] instanceof CmsUser);
$this->assertTrue($result[1]['2'] instanceof CmsUser);
$this->assertTrue($result[0]['1']->phonenumbers instanceof Doctrine_ORM_Collection);
// first user => 2 phonenumbers. notice the custom indexing by user id
$this->assertEquals(2, count($result[0]['1']->phonenumbers));
// second user => 1 phonenumber. notice the custom indexing by user id
$this->assertEquals(1, count($result[1]['2']->phonenumbers));
// test the custom indexing of the phonenumbers
$this->assertTrue(isset($result[0]['1']->phonenumbers['42']));
$this->assertTrue(isset($result[0]['1']->phonenumbers['43']));
$this->assertTrue(isset($result[1]['2']->phonenumbers['91']));
}
/**
* select u.id, u.status, p.phonenumber, upper(u.name) nameUpper, a.id, a.topic
* from User u
* join u.phonenumbers p
* join u.articles a
* =
* select u.id, u.status, p.phonenumber, upper(u.name) as u__0, a.id, a.topic
* from USERS u
* inner join PHONENUMBERS p ON u.id = p.user_id
* inner join ARTICLES a ON u.id = a.user_id
*/
public function testNewHydrationMixedQueryMultipleFetchJoin()
{
// Faked query components
$queryComponents = array(
'u' => array(
'metadata' => $this->_em->getClassMetadata('CmsUser'),
'parent' => null,
'relation' => null,
'map' => null,
'agg' => array('0' => 'nameUpper')
),
'p' => array(
'metadata' => $this->_em->getClassMetadata('CmsPhonenumber'),
'parent' => 'u',
'relation' => $this->_em->getClassMetadata('CmsUser')->getAssociationMapping('phonenumbers'),
'map' => null
),
'a' => array(
'metadata' => $this->_em->getClassMetadata('CmsArticle'),
'parent' => 'u',
'relation' => $this->_em->getClassMetadata('CmsUser')->getAssociationMapping('articles'),
'map' => null
),
);
// Faked table alias map
$tableAliasMap = array(
'u' => 'u',
'p' => 'p',
'a' => 'a'
);
// Faked result set
$resultSet = array(
//row1
array(
'u__id' => '1',
'u__status' => 'developer',
'u__0' => 'ROMANB',
'p__phonenumber' => '42',
'a__id' => '1',
'a__topic' => 'Getting things done!'
),
array(
'u__id' => '1',
'u__status' => 'developer',
'u__0' => 'ROMANB',
'p__phonenumber' => '43',
'a__id' => '1',
'a__topic' => 'Getting things done!'
),
array(
'u__id' => '1',
'u__status' => 'developer',
'u__0' => 'ROMANB',
'p__phonenumber' => '42',
'a__id' => '2',
'a__topic' => 'ZendCon'
),
array(
'u__id' => '1',
'u__status' => 'developer',
'u__0' => 'ROMANB',
'p__phonenumber' => '43',
'a__id' => '2',
'a__topic' => 'ZendCon'
),
array(
'u__id' => '2',
'u__status' => 'developer',
'u__0' => 'JWAGE',
'p__phonenumber' => '91',
'a__id' => '3',
'a__topic' => 'LINQ'
),
array(
'u__id' => '2',
'u__status' => 'developer',
'u__0' => 'JWAGE',
'p__phonenumber' => '91',
'a__id' => '4',
'a__topic' => 'PHP6'
),
);
$stmt = new Doctrine_HydratorMockStatement($resultSet);
$hydrator = new Doctrine_ORM_Internal_Hydration_ObjectHydrator($this->_em);
$result = $hydrator->hydrateAll($stmt, $this->_createParserResult(
$stmt, $queryComponents, $tableAliasMap, Doctrine_ORM_Query::HYDRATE_OBJECT, true));
$this->assertEquals(2, count($result));
$this->assertTrue(is_array($result));
$this->assertTrue(is_array($result[0]));
$this->assertTrue(is_array($result[1]));
$this->assertTrue($result[0][0] instanceof CmsUser);
$this->assertTrue($result[0][0]->phonenumbers instanceof Doctrine_ORM_Collection);
$this->assertTrue($result[0][0]->phonenumbers[0] instanceof CmsPhonenumber);
$this->assertTrue($result[0][0]->phonenumbers[1] instanceof CmsPhonenumber);
$this->assertTrue($result[0][0]->articles instanceof Doctrine_ORM_Collection);
$this->assertTrue($result[0][0]->articles[0] instanceof CmsArticle);
$this->assertTrue($result[0][0]->articles[1] instanceof CmsArticle);
$this->assertTrue($result[1][0] instanceof CmsUser);
$this->assertTrue($result[1][0]->phonenumbers instanceof Doctrine_ORM_Collection);
$this->assertTrue($result[1][0]->phonenumbers[0] instanceof CmsPhonenumber);
$this->assertTrue($result[1][0]->articles[0] instanceof CmsArticle);
$this->assertTrue($result[1][0]->articles[1] instanceof CmsArticle);
}
/**
* select u.id, u.status, p.phonenumber, upper(u.name) nameUpper, a.id, a.topic,
* c.id, c.topic
* from User u
* join u.phonenumbers p
* join u.articles a
* left join a.comments c
* =
* select u.id, u.status, p.phonenumber, upper(u.name) as u__0, a.id, a.topic,
* c.id, c.topic
* from USERS u
* inner join PHONENUMBERS p ON u.id = p.user_id
* inner join ARTICLES a ON u.id = a.user_id
* left outer join COMMENTS c ON a.id = c.article_id
*/
public function testNewHydrationMixedQueryMultipleDeepMixedFetchJoin()
{
// Faked query components
$queryComponents = array(
'u' => array(
'metadata' => $this->_em->getClassMetadata('CmsUser'),
'parent' => null,
'relation' => null,
'map' => null,
'agg' => array('0' => 'nameUpper')
),
'p' => array(
'metadata' => $this->_em->getClassMetadata('CmsPhonenumber'),
'parent' => 'u',
'relation' => $this->_em->getClassMetadata('CmsUser')->getAssociationMapping('phonenumbers'),
'map' => null
),
'a' => array(
'metadata' => $this->_em->getClassMetadata('CmsArticle'),
'parent' => 'u',
'relation' => $this->_em->getClassMetadata('CmsUser')->getAssociationMapping('articles'),
'map' => null
),
'c' => array(
'metadata' => $this->_em->getClassMetadata('CmsComment'),
'parent' => 'a',
'relation' => $this->_em->getClassMetadata('CmsArticle')->getAssociationMapping('comments'),
'map' => null
),
);
// Faked table alias map
$tableAliasMap = array(
'u' => 'u',
'p' => 'p',
'a' => 'a',
'c' => 'c'
);
// Faked result set
$resultSet = array(
//row1
array(
'u__id' => '1',
'u__status' => 'developer',
'u__0' => 'ROMANB',
'p__phonenumber' => '42',
'a__id' => '1',
'a__topic' => 'Getting things done!',
'c__id' => '1',
'c__topic' => 'First!'
),
array(
'u__id' => '1',
'u__status' => 'developer',
'u__0' => 'ROMANB',
'p__phonenumber' => '43',
'a__id' => '1',
'a__topic' => 'Getting things done!',
'c__id' => '1',
'c__topic' => 'First!'
),
array(
'u__id' => '1',
'u__status' => 'developer',
'u__0' => 'ROMANB',
'p__phonenumber' => '42',
'a__id' => '2',
'a__topic' => 'ZendCon',
'c__id' => null,
'c__topic' => null
),
array(
'u__id' => '1',
'u__status' => 'developer',
'u__0' => 'ROMANB',
'p__phonenumber' => '43',
'a__id' => '2',
'a__topic' => 'ZendCon',
'c__id' => null,
'c__topic' => null
),
array(
'u__id' => '2',
'u__status' => 'developer',
'u__0' => 'JWAGE',
'p__phonenumber' => '91',
'a__id' => '3',
'a__topic' => 'LINQ',
'c__id' => null,
'c__topic' => null
),
array(
'u__id' => '2',
'u__status' => 'developer',
'u__0' => 'JWAGE',
'p__phonenumber' => '91',
'a__id' => '4',
'a__topic' => 'PHP6',
'c__id' => null,
'c__topic' => null
),
);
$stmt = new Doctrine_HydratorMockStatement($resultSet);
$hydrator = new Doctrine_ORM_Internal_Hydration_ObjectHydrator($this->_em);
$result = $hydrator->hydrateAll($stmt, $this->_createParserResult(
$stmt, $queryComponents, $tableAliasMap, Doctrine_ORM_Query::HYDRATE_OBJECT, true));
$this->assertEquals(2, count($result));
$this->assertTrue(is_array($result));
$this->assertTrue(is_array($result[0]));
$this->assertTrue(is_array($result[1]));
$this->assertTrue($result[0][0] instanceof CmsUser);
$this->assertTrue($result[1][0] instanceof CmsUser);
// phonenumbers
$this->assertTrue($result[0][0]->phonenumbers instanceof Doctrine_ORM_Collection);
$this->assertTrue($result[0][0]->phonenumbers[0] instanceof CmsPhonenumber);
$this->assertTrue($result[0][0]->phonenumbers[1] instanceof CmsPhonenumber);
$this->assertTrue($result[1][0]->phonenumbers instanceof Doctrine_ORM_Collection);
$this->assertTrue($result[1][0]->phonenumbers[0] instanceof CmsPhonenumber);
// articles
$this->assertTrue($result[0][0]->articles instanceof Doctrine_ORM_Collection);
$this->assertTrue($result[0][0]->articles[0] instanceof CmsArticle);
$this->assertTrue($result[0][0]->articles[1] instanceof CmsArticle);
$this->assertTrue($result[1][0]->articles[0] instanceof CmsArticle);
$this->assertTrue($result[1][0]->articles[1] instanceof CmsArticle);
// article comments
$this->assertTrue($result[0][0]->articles[0]->comments instanceof Doctrine_ORM_Collection);
$this->assertTrue($result[0][0]->articles[0]->comments[0] instanceof CmsComment);
// empty comment collections
$this->assertTrue($result[0][0]->articles[1]->comments instanceof Doctrine_ORM_Collection);
$this->assertEquals(0, count($result[0][0]->articles[1]->comments));
$this->assertTrue($result[1][0]->articles[0]->comments instanceof Doctrine_ORM_Collection);
$this->assertEquals(0, count($result[1][0]->articles[0]->comments));
$this->assertTrue($result[1][0]->articles[1]->comments instanceof Doctrine_ORM_Collection);
$this->assertEquals(0, count($result[1][0]->articles[1]->comments));
}
/**
* Tests that the hydrator does not rely on a particular order of the rows
* in the result set.
*
* DQL:
* select c.id, c.position, c.name, b.id, b.position
* from ForumCategory c inner join c.boards b
* order by c.position asc, b.position asc
*
* Checks whether the boards are correctly assigned to the categories.
*
* The 'evil' result set that confuses the object population is displayed below.
*
* c.id | c.position | c.name | boardPos | b.id | b.category_id (just for clarity)
* 1 | 0 | First | 0 | 1 | 1
* 2 | 0 | Second | 0 | 2 | 2 <--
* 1 | 0 | First | 1 | 3 | 1
* 1 | 0 | First | 2 | 4 | 1
*/
public function testNewHydrationEntityQueryCustomResultSetOrder()
{
// Faked query components
$queryComponents = array(
'c' => array(
'metadata' => $this->_em->getClassMetadata('ForumCategory'),
'parent' => null,
'relation' => null,
'map' => null
),
'b' => array(
'metadata' => $this->_em->getClassMetadata('ForumBoard'),
'parent' => 'c',
'relation' => $this->_em->getClassMetadata('ForumCategory')->getAssociationMapping('boards'),
'map' => null
),
);
// Faked table alias map
$tableAliasMap = array(
'c' => 'c',
'b' => 'b'
);
// Faked result set
$resultSet = array(
array(
'c__id' => '1',
'c__position' => '0',
'c__name' => 'First',
'b__id' => '1',
'b__position' => '0',
//'b__category_id' => '1'
),
array(
'c__id' => '2',
'c__position' => '0',
'c__name' => 'Second',
'b__id' => '2',
'b__position' => '0',
//'b__category_id' => '2'
),
array(
'c__id' => '1',
'c__position' => '0',
'c__name' => 'First',
'b__id' => '3',
'b__position' => '1',
//'b__category_id' => '1'
),
array(
'c__id' => '1',
'c__position' => '0',
'c__name' => 'First',
'b__id' => '4',
'b__position' => '2',
//'b__category_id' => '1'
)
);
$stmt = new Doctrine_HydratorMockStatement($resultSet);
$hydrator = new Doctrine_ORM_Internal_Hydration_ObjectHydrator($this->_em);
$result = $hydrator->hydrateAll($stmt, $this->_createParserResult(
$stmt, $queryComponents, $tableAliasMap, Doctrine_ORM_Query::HYDRATE_OBJECT));
$this->assertEquals(2, count($result));
$this->assertTrue($result instanceof Doctrine_ORM_Collection);
$this->assertTrue($result[0] instanceof ForumCategory);
$this->assertTrue($result[1] instanceof ForumCategory);
$this->assertEquals(1, $result[0]->getId());
$this->assertEquals(2, $result[1]->getId());
$this->assertTrue(isset($result[0]->boards));
$this->assertEquals(3, count($result[0]->boards));
$this->assertTrue(isset($result[1]->boards));
$this->assertEquals(1, count($result[1]->boards));
}
public function testResultIteration() {
// Faked query components
$queryComponents = array(
'u' => array(
'metadata' => $this->_em->getClassMetadata('CmsUser'),
'parent' => null,
'relation' => null,
'map' => null
)
);
// Faked table alias map
$tableAliasMap = array(
'u' => 'u'
);
// Faked result set
$resultSet = array(
array(
'u__id' => '1',
'u__name' => 'romanb'
),
array(
'u__id' => '2',
'u__name' => 'jwage'
)
);
$stmt = new Doctrine_HydratorMockStatement($resultSet);
$hydrator = new Doctrine_ORM_Internal_Hydration_ObjectHydrator($this->_em);
$iterableResult = $hydrator->iterate($stmt, $this->_createParserResult(
$stmt, $queryComponents, $tableAliasMap, Doctrine_ORM_Query::HYDRATE_OBJECT));
$rowNum = 0;
while (($row = $iterableResult->next()) !== false) {
$this->assertEquals(1, count($row));
$this->assertTrue($row[0] instanceof CmsUser);
if ($rowNum == 0) {
$this->assertEquals(1, $row[0]->id);
$this->assertEquals('romanb', $row[0]->name);
} else if ($rowNum == 1) {
$this->assertEquals(2, $row[0]->id);
$this->assertEquals('jwage', $row[0]->name);
}
++$rowNum;
}
}
}

View File

@ -0,0 +1,59 @@
<?php
require_once dirname(__FILE__) . DIRECTORY_SEPARATOR . 'HydrationTest.php';
/**
* Description of ScalarHydratorTest
*
* @author robo
*/
class Orm_Hydration_ScalarHydratorTest extends Orm_Hydration_HydrationTest
{
/**
* Select u.id, u.name from CmsUser u
*/
public function testNewHydrationSimpleEntityQuery()
{
// Faked query components
$queryComponents = array(
'u' => array(
'metadata' => $this->_em->getClassMetadata('CmsUser'),
'parent' => null,
'relation' => null,
'map' => null
)
);
// Faked table alias map
$tableAliasMap = array(
'u' => 'u'
);
// Faked result set
$resultSet = array(
array(
'u__id' => '1',
'u__name' => 'romanb'
),
array(
'u__id' => '2',
'u__name' => 'jwage'
)
);
$stmt = new Doctrine_HydratorMockStatement($resultSet);
$hydrator = new Doctrine_ORM_Internal_Hydration_ScalarHydrator($this->_em);
$result = $hydrator->hydrateAll($stmt, $this->_createParserResult(
$stmt, $queryComponents, $tableAliasMap, Doctrine_ORM_Query::HYDRATE_SCALAR));
$this->assertTrue(is_array($result));
$this->assertEquals(2, count($result));
$this->assertEquals('romanb', $result[0]['u_name']);
$this->assertEquals(1, $result[0]['u_id']);
$this->assertEquals('jwage', $result[1]['u_name']);
$this->assertEquals(2, $result[1]['u_id']);
}
}

View File

@ -0,0 +1,93 @@
<?php
require_once dirname(__FILE__) . DIRECTORY_SEPARATOR . 'HydrationTest.php';
/**
* Description of SingleScalarHydratorTest
*
* @author robo
*/
class Orm_Hydration_SingleScalarHydratorTest extends Orm_Hydration_HydrationTest
{
/** Result set provider for the HYDRATE_SINGLE_SCALAR tests */
public static function singleScalarResultSetProvider() {
return array(
// valid
array('name' => 'result1',
'resultSet' => array(
array(
'u__name' => 'romanb'
)
)),
// valid
array('name' => 'result2',
'resultSet' => array(
array(
'u__id' => '1'
)
)),
// invalid
array('name' => 'result3',
'resultSet' => array(
array(
'u__id' => '1',
'u__name' => 'romanb'
)
)),
// invalid
array('name' => 'result4',
'resultSet' => array(
array(
'u__id' => '1'
),
array(
'u__id' => '2'
)
)),
);
}
/**
* select u.name from CmsUser u where u.id = 1
*
* @dataProvider singleScalarResultSetProvider
*/
public function testHydrateSingleScalar($name, $resultSet)
{
// Faked query components
$queryComponents = array(
'u' => array(
'metadata' => $this->_em->getClassMetadata('CmsUser'),
'parent' => null,
'relation' => null,
'map' => null
)
);
// Faked table alias map
$tableAliasMap = array(
'u' => 'u'
);
$stmt = new Doctrine_HydratorMockStatement($resultSet);
$hydrator = new Doctrine_ORM_Internal_Hydration_SingleScalarHydrator($this->_em);
if ($name == 'result1') {
$result = $hydrator->hydrateAll($stmt, $this->_createParserResult(
$stmt, $queryComponents, $tableAliasMap, Doctrine_ORM_Query::HYDRATE_SINGLE_SCALAR));
$this->assertEquals('romanb', $result);
} else if ($name == 'result2') {
$result = $hydrator->hydrateAll($stmt, $this->_createParserResult(
$stmt, $queryComponents, $tableAliasMap, Doctrine_ORM_Query::HYDRATE_SINGLE_SCALAR));
$this->assertEquals(1, $result);
} else if ($name == 'result3' || $name == 'result4') {
try {
$result = $hydrator->hydrateall($stmt, $this->_createParserResult(
$stmt, $queryComponents, $tableAliasMap, Doctrine_ORM_Query::HYDRATE_SINGLE_SCALAR));
$this->fail();
} catch (Doctrine_ORM_Exceptions_HydrationException $ex) {}
}
}
}