First commit of new extensible hydrator structure. Cleanup to follow.
This commit is contained in:
parent
1843539fa2
commit
60fb69dd03
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
||||
}
|
||||
|
||||
?>
|
@ -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()
|
||||
{
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
208
lib/Doctrine/ORM/Internal/Hydration/ArrayHydrator.php
Normal file
208
lib/Doctrine/ORM/Internal/Hydration/ArrayHydrator.php
Normal 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();
|
||||
}
|
||||
}
|
||||
|
28
lib/Doctrine/ORM/Internal/Hydration/IterableResult.php
Normal file
28
lib/Doctrine/ORM/Internal/Hydration/IterableResult.php
Normal 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();
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
371
lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php
Normal file
371
lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php
Normal 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;
|
||||
}
|
||||
}
|
||||
|
36
lib/Doctrine/ORM/Internal/Hydration/ScalarHydrator.php
Normal file
36
lib/Doctrine/ORM/Internal/Hydration/ScalarHydrator.php
Normal 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();
|
||||
}
|
||||
}
|
||||
|
33
lib/Doctrine/ORM/Internal/Hydration/SingleScalarHydrator.php
Normal file
33
lib/Doctrine/ORM/Internal/Hydration/SingleScalarHydrator.php
Normal 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();
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -54,6 +54,11 @@ class Doctrine_ORM_Query_ParserResultDummy
|
||||
{
|
||||
return $this->_tableToClassAliasMap;
|
||||
}
|
||||
|
||||
public function getTableAliasMap()
|
||||
{
|
||||
return $this->_tableToClassAliasMap;
|
||||
}
|
||||
|
||||
public function setTableToClassAliasMap(array $map)
|
||||
{
|
||||
|
@ -68,4 +68,4 @@ class Doctrine_ORM_VirtualProxy
|
||||
unset($realInstance->$prop);
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
712
tests/Orm/Hydration/ArrayHydratorTest.php
Normal file
712
tests/Orm/Hydration/ArrayHydratorTest.php
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
34
tests/Orm/Hydration/HydrationTest.php
Normal file
34
tests/Orm/Hydration/HydrationTest.php
Normal 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;
|
||||
}
|
||||
}
|
||||
|
720
tests/Orm/Hydration/ObjectHydratorTest.php
Normal file
720
tests/Orm/Hydration/ObjectHydratorTest.php
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
59
tests/Orm/Hydration/ScalarHydratorTest.php
Normal file
59
tests/Orm/Hydration/ScalarHydratorTest.php
Normal 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']);
|
||||
}
|
||||
}
|
||||
|
93
tests/Orm/Hydration/SingleScalarHydratorTest.php
Normal file
93
tests/Orm/Hydration/SingleScalarHydratorTest.php
Normal 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) {}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user