Commit 59fff29c authored by romanb's avatar romanb

[2.0] Some hydration simplifications and cleanups.

parent 4f644901
...@@ -19,15 +19,6 @@ Url: http://www.symfony-project.org ...@@ -19,15 +19,6 @@ Url: http://www.symfony-project.org
Copyright: Fabien Potencier Copyright: Fabien Potencier
License: MIT - see LICENSE file License: MIT - see LICENSE file
Spyc
----
Doctrine contains a port of the Spyc software
Url: http://spyc.sourceforge.net/
Copyright: 2005-2006 Chris Wanstrath
License: MIT - http://www.opensource.org/licenses/mit-license.php
Zend Framework Zend Framework
-------------- --------------
......
...@@ -66,9 +66,9 @@ class DoctrineException extends \Exception ...@@ -66,9 +66,9 @@ class DoctrineException extends \Exception
if ($method && $class) { if ($method && $class) {
return new self("The method '$method' is not implemented in class '$class'."); return new self("The method '$method' is not implemented in class '$class'.");
} else if ($method && ! $class) { } else if ($method && ! $class) {
return new self($method); return new self($method);
} else { } else {
return new self('Functionality is not implemented.'); return new self('Functionality is not implemented.');
} }
} }
......
...@@ -92,6 +92,10 @@ final class Events ...@@ -92,6 +92,10 @@ final class Events
* into the current EntityManager from the database or after the refresh operation * into the current EntityManager from the database or after the refresh operation
* has been applied to it. * has been applied to it.
* *
* Note that the postLoad event occurs for an entity before any associations have been
* initialized. Therefore it is not safe to access associations in a postLoad callback
* or event handler.
*
* This is an entity lifecycle event. * This is an entity lifecycle event.
* *
* @var string * @var string
......
...@@ -162,7 +162,10 @@ abstract class AbstractHydrator ...@@ -162,7 +162,10 @@ abstract class AbstractHydrator
/** /**
* Gets the row container used during row-by-row hydration through {@link iterate()}. * Gets the row container used during row-by-row hydration through {@link iterate()}.
*/ */
abstract protected function _getRowContainer(); protected function _getRowContainer()
{
return array();
}
/** /**
* Processes a row of the result set. * Processes a row of the result set.
......
...@@ -228,10 +228,4 @@ class ArrayHydrator extends AbstractHydrator ...@@ -228,10 +228,4 @@ class ArrayHydrator extends AbstractHydrator
} }
return $this->_ce[$className]; return $this->_ce[$className];
} }
/** {@inheritdoc} */
protected function _getRowContainer()
{
return array();
}
} }
\ No newline at end of file
...@@ -8,4 +8,10 @@ class HydrationException extends \Doctrine\Common\DoctrineException ...@@ -8,4 +8,10 @@ class HydrationException extends \Doctrine\Common\DoctrineException
{ {
return new self("The result returned by the query was not unique."); return new self("The result returned by the query was not unique.");
} }
public static function parentObjectOfRelationNotFound($alias, $parentAlias)
{
return new self("The parent object of entity result with alias '$alias' was not found."
. " The parent alias is '$parentAlias'.");
}
} }
\ No newline at end of file
...@@ -21,8 +21,7 @@ ...@@ -21,8 +21,7 @@
namespace Doctrine\ORM\Internal\Hydration; namespace Doctrine\ORM\Internal\Hydration;
use Doctrine\DBAL\Connection, use Doctrine\ORM\PersistentCollection,
Doctrine\ORM\PersistentCollection,
Doctrine\ORM\Query, Doctrine\ORM\Query,
Doctrine\Common\Collections\ArrayCollection, Doctrine\Common\Collections\ArrayCollection,
Doctrine\Common\Collections\Collection; Doctrine\Common\Collections\Collection;
...@@ -35,29 +34,25 @@ use Doctrine\DBAL\Connection, ...@@ -35,29 +34,25 @@ use Doctrine\DBAL\Connection,
*/ */
class ObjectHydrator extends AbstractHydrator class ObjectHydrator extends AbstractHydrator
{ {
/* /* Local ClassMetadata cache to avoid going to the EntityManager all the time.
* These two properties maintain their values between hydration runs. * This local cache is maintained between hydration runs and not cleared.
*/ */
/* Local ClassMetadata cache to avoid going to the EntityManager all the time. */
private $_ce = array(); private $_ce = array();
/*
* The following parts are reinitialized on every hydration run. /* The following parts are reinitialized on every hydration run. */
*/
private $_isSimpleQuery = false;
private $_allowPartialObjects = false; private $_allowPartialObjects = false;
private $_identifierMap = array(); private $_identifierMap;
private $_resultPointers = array(); private $_resultPointers;
private $_idTemplate = array(); private $_idTemplate;
private $_resultCounter; private $_resultCounter;
private $_rootAliases = array();
private $_fetchedAssociations; private $_fetchedAssociations;
/** Memory for initialized collections. */ private $_rootAliases = array();
private $_initializedCollections = array(); private $_initializedCollections = array();
/** @override */ /** @override */
protected function _prepare() protected function _prepare()
{ {
$this->_isSimpleQuery = count($this->_rsm->aliasMap) <= 1;
$this->_allowPartialObjects = $this->_em->getConfiguration()->getAllowPartialObjects() $this->_allowPartialObjects = $this->_em->getConfiguration()->getAllowPartialObjects()
|| isset($this->_hints[Query::HINT_FORCE_PARTIAL_LOAD]); || isset($this->_hints[Query::HINT_FORCE_PARTIAL_LOAD]);
...@@ -77,7 +72,8 @@ class ObjectHydrator extends AbstractHydrator ...@@ -77,7 +72,8 @@ class ObjectHydrator extends AbstractHydrator
$this->_ce[$className] = $class; $this->_ce[$className] = $class;
} }
// Remember which associations are "fetch joined" // Remember which associations are "fetch joined", so that we know where to inject
// collection stubs or proxies and where not.
if (isset($this->_rsm->relationMap[$dqlAlias])) { if (isset($this->_rsm->relationMap[$dqlAlias])) {
$targetClassName = $this->_rsm->aliasMap[$this->_rsm->parentAliasMap[$dqlAlias]]; $targetClassName = $this->_rsm->aliasMap[$this->_rsm->parentAliasMap[$dqlAlias]];
$targetClass = $this->_em->getClassMetadata($targetClassName); $targetClass = $this->_em->getClassMetadata($targetClassName);
...@@ -96,6 +92,11 @@ class ObjectHydrator extends AbstractHydrator ...@@ -96,6 +92,11 @@ class ObjectHydrator extends AbstractHydrator
} }
} }
/**
* {@inheritdoc}
*
* @override
*/
protected function _cleanup() protected function _cleanup()
{ {
parent::_cleanup(); parent::_cleanup();
...@@ -112,7 +113,7 @@ class ObjectHydrator extends AbstractHydrator ...@@ -112,7 +113,7 @@ class ObjectHydrator extends AbstractHydrator
{ {
$result = array(); $result = array();
$cache = array(); $cache = array();
while ($data = $this->_stmt->fetch(Connection::FETCH_ASSOC)) { while ($data = $this->_stmt->fetch(\Doctrine\DBAL\Connection::FETCH_ASSOC)) {
$this->_hydrateRow($data, $cache, $result); $this->_hydrateRow($data, $cache, $result);
} }
...@@ -131,7 +132,7 @@ class ObjectHydrator extends AbstractHydrator ...@@ -131,7 +132,7 @@ class ObjectHydrator extends AbstractHydrator
* @param object $entity The entity to which the collection belongs. * @param object $entity The entity to which the collection belongs.
* @param string $name The name of the field on the entity that holds the collection. * @param string $name The name of the field on the entity that holds the collection.
*/ */
private function initRelatedCollection($entity, $name) private function _initRelatedCollection($entity, $name)
{ {
$oid = spl_object_hash($entity); $oid = spl_object_hash($entity);
$class = $this->_ce[get_class($entity)]; $class = $this->_ce[get_class($entity)];
...@@ -155,7 +156,7 @@ class ObjectHydrator extends AbstractHydrator ...@@ -155,7 +156,7 @@ class ObjectHydrator extends AbstractHydrator
* @param $dqlAlias The DQL alias of the entity's class. * @param $dqlAlias The DQL alias of the entity's class.
* @return object The entity. * @return object The entity.
*/ */
private function getEntity(array $data, $dqlAlias) private function _getEntity(array $data, $dqlAlias)
{ {
$className = $this->_rsm->aliasMap[$dqlAlias]; $className = $this->_rsm->aliasMap[$dqlAlias];
if (isset($this->_rsm->discriminatorColumns[$dqlAlias])) { if (isset($this->_rsm->discriminatorColumns[$dqlAlias])) {
...@@ -232,7 +233,7 @@ class ObjectHydrator extends AbstractHydrator ...@@ -232,7 +233,7 @@ class ObjectHydrator extends AbstractHydrator
*/ */
protected function _hydrateRow(array &$data, array &$cache, array &$result) protected function _hydrateRow(array &$data, array &$cache, array &$result)
{ {
// 1) Initialize // Initialize
$id = $this->_idTemplate; // initialize the id-memory $id = $this->_idTemplate; // initialize the id-memory
$nonemptyComponents = array(); $nonemptyComponents = array();
$rowData = $this->_gatherRowData($data, $cache, $id, $nonemptyComponents); $rowData = $this->_gatherRowData($data, $cache, $id, $nonemptyComponents);
...@@ -253,17 +254,14 @@ class ObjectHydrator extends AbstractHydrator ...@@ -253,17 +254,14 @@ class ObjectHydrator extends AbstractHydrator
$parent = $this->_rsm->parentAliasMap[$dqlAlias]; $parent = $this->_rsm->parentAliasMap[$dqlAlias];
// Get a reference to the right element in the result tree. // Get a reference to the right object to which the joined result belongs.
// This element will get the associated element attached.
if ($this->_rsm->isMixed && isset($this->_rootAliases[$parent])) { if ($this->_rsm->isMixed && isset($this->_rootAliases[$parent])) {
$first = reset($this->_resultPointers); $first = reset($this->_resultPointers);
// TODO: Exception if key($first) === null ?
$baseElement = $this->_resultPointers[$parent][key($first)]; $baseElement = $this->_resultPointers[$parent][key($first)];
} else if (isset($this->_resultPointers[$parent])) { } else if (isset($this->_resultPointers[$parent])) {
$baseElement = $this->_resultPointers[$parent]; $baseElement = $this->_resultPointers[$parent];
} else { } else {
unset($this->_resultPointers[$dqlAlias]); // Ticket #1228 throw HydrationException::parentObjectOfRelationNotFound($dqlAlias, $parent);
continue;
} }
$parentClass = get_class($baseElement); $parentClass = get_class($baseElement);
...@@ -278,16 +276,15 @@ class ObjectHydrator extends AbstractHydrator ...@@ -278,16 +276,15 @@ class ObjectHydrator extends AbstractHydrator
// Collection-valued association // Collection-valued association
if (isset($nonemptyComponents[$dqlAlias])) { if (isset($nonemptyComponents[$dqlAlias])) {
if ( ! isset($this->_initializedCollections[$oid . $relationField])) { if ( ! isset($this->_initializedCollections[$oid . $relationField])) {
$reflFieldValue = $this->initRelatedCollection($baseElement, $relationField); $reflFieldValue = $this->_initRelatedCollection($baseElement, $relationField);
} }
$path = $parent . '.' . $dqlAlias; $indexExists = isset($this->_identifierMap[$dqlAlias][$id[$dqlAlias]]);
$indexExists = isset($this->_identifierMap[$path][$id[$parent]][$id[$dqlAlias]]); $index = $indexExists ? $this->_identifierMap[$dqlAlias][$id[$dqlAlias]] : false;
$index = $indexExists ? $this->_identifierMap[$path][$id[$parent]][$id[$dqlAlias]] : false; $indexIsValid = $index !== false ? isset($reflFieldValue[$index]) : false;
$indexIsValid = $index !== false ? $reflFieldValue->containsKey($index) : false;
if ( ! $indexExists || ! $indexIsValid) { if ( ! $indexExists || ! $indexIsValid) {
$element = $this->getEntity($data, $dqlAlias); $element = $this->_getEntity($data, $dqlAlias);
// If it's a bi-directional many-to-many, also initialize the reverse collection. // If it's a bi-directional many-to-many, also initialize the reverse collection.
if ($relation->isManyToMany()) { if ($relation->isManyToMany()) {
...@@ -295,45 +292,42 @@ class ObjectHydrator extends AbstractHydrator ...@@ -295,45 +292,42 @@ class ObjectHydrator extends AbstractHydrator
$inverseFieldName = $this->_ce[$entityName]->inverseMappings[$relation->sourceEntityName][$relationField]->sourceFieldName; $inverseFieldName = $this->_ce[$entityName]->inverseMappings[$relation->sourceEntityName][$relationField]->sourceFieldName;
// Only initialize reverse collection if it is not yet initialized. // Only initialize reverse collection if it is not yet initialized.
if ( ! isset($this->_initializedCollections[spl_object_hash($element) . $inverseFieldName])) { if ( ! isset($this->_initializedCollections[spl_object_hash($element) . $inverseFieldName])) {
$this->initRelatedCollection($element, $inverseFieldName); $this->_initRelatedCollection($element, $inverseFieldName);
} }
} else if ($relation->mappedByFieldName) { } else if ($relation->mappedByFieldName) {
// Only initialize reverse collection if it is not yet initialized. // Only initialize reverse collection if it is not yet initialized.
if ( ! isset($this->_initializedCollections[spl_object_hash($element) . $relation->mappedByFieldName])) { if ( ! isset($this->_initializedCollections[spl_object_hash($element) . $relation->mappedByFieldName])) {
$this->initRelatedCollection($element, $relation->mappedByFieldName); $this->_initRelatedCollection($element, $relation->mappedByFieldName);
} }
} }
} }
if (isset($this->_rsm->indexByMap[$dqlAlias])) { if (isset($this->_rsm->indexByMap[$dqlAlias])) {
$field = $this->_rsm->indexByMap[$dqlAlias]; $field = $this->_rsm->indexByMap[$dqlAlias];
$indexValue = $this->_ce[$entityName] $indexValue = $this->_ce[$entityName]->reflFields[$field]->getValue($element);
->reflFields[$field]
->getValue($element);
$reflFieldValue->hydrateSet($indexValue, $element); $reflFieldValue->hydrateSet($indexValue, $element);
$this->_identifierMap[$dqlAlias][$id[$dqlAlias]] = $indexValue;
} else { } else {
$reflFieldValue->hydrateAdd($element); $reflFieldValue->hydrateAdd($element);
$reflFieldValue->last();
$this->_identifierMap[$dqlAlias][$id[$dqlAlias]] = $reflFieldValue->key();
} }
$reflFieldValue->last();
$this->_identifierMap[$path][$id[$parent]][$id[$dqlAlias]] = $reflFieldValue->key();
} }
// Update result pointer
$this->_resultPointers[$dqlAlias] = $index === false ? $element : $reflFieldValue[$index];
} else if ( ! $reflFieldValue) { } else if ( ! $reflFieldValue) {
$coll = new PersistentCollection($this->_em, $this->_ce[$entityName], new ArrayCollection); $coll = new PersistentCollection($this->_em, $this->_ce[$entityName], new ArrayCollection);
$reflField->setValue($baseElement, $coll); $reflField->setValue($baseElement, $coll);
$reflFieldValue = $coll;
$this->_uow->setOriginalEntityProperty($oid, $relationField, $coll); $this->_uow->setOriginalEntityProperty($oid, $relationField, $coll);
} }
// Update result pointer
$this->_resultPointers[$dqlAlias] = $index === false ?
$reflFieldValue->last() : $reflFieldValue[$index];
} else { } else {
// Single-valued association // Single-valued association
$reflFieldValue = $reflField->getValue($baseElement); $reflFieldValue = $reflField->getValue($baseElement);
if ( ! $reflFieldValue) { if ( ! $reflFieldValue) {
if (isset($nonemptyComponents[$dqlAlias])) { if (isset($nonemptyComponents[$dqlAlias])) {
$element = $this->getEntity($data, $dqlAlias); $element = $this->_getEntity($data, $dqlAlias);
$reflField->setValue($baseElement, $element); $reflField->setValue($baseElement, $element);
$this->_uow->setOriginalEntityProperty($oid, $relationField, $element); $this->_uow->setOriginalEntityProperty($oid, $relationField, $element);
$targetClass = $this->_ce[$relation->targetEntityName]; $targetClass = $this->_ce[$relation->targetEntityName];
...@@ -351,10 +345,9 @@ class ObjectHydrator extends AbstractHydrator ...@@ -351,10 +345,9 @@ class ObjectHydrator extends AbstractHydrator
$targetClass->reflFields[$relation->mappedByFieldName]->setValue($element, $baseElement); $targetClass->reflFields[$relation->mappedByFieldName]->setValue($element, $baseElement);
} }
} }
} // else leave $reflFieldValue null for single-valued associations
} else {
// Update result pointer // Update result pointer
if ($reflFieldValue !== null) {
$this->_resultPointers[$dqlAlias] = $reflFieldValue; $this->_resultPointers[$dqlAlias] = $reflFieldValue;
} }
} }
...@@ -362,36 +355,34 @@ class ObjectHydrator extends AbstractHydrator ...@@ -362,36 +355,34 @@ class ObjectHydrator extends AbstractHydrator
// Its a root result element // Its a root result element
$this->_rootAliases[$dqlAlias] = true; // Mark as root alias $this->_rootAliases[$dqlAlias] = true; // Mark as root alias
if ($this->_isSimpleQuery || ! isset($this->_identifierMap[$dqlAlias][$id[$dqlAlias]])) { if ( ! isset($this->_identifierMap[$dqlAlias][$id[$dqlAlias]])) {
$element = $this->getEntity($rowData[$dqlAlias], $dqlAlias); $element = $this->_getEntity($rowData[$dqlAlias], $dqlAlias);
if (isset($this->_rsm->indexByMap[$dqlAlias])) { if (isset($this->_rsm->indexByMap[$dqlAlias])) {
$field = $this->_rsm->indexByMap[$dqlAlias]; $field = $this->_rsm->indexByMap[$dqlAlias];
$key = $this->_ce[$entityName]->reflFields[$field]->getValue($element);
if ($this->_rsm->isMixed) { if ($this->_rsm->isMixed) {
$result[] = array( $element = array($key => $element);
$this->_ce[$entityName] $result[] = $element;
->reflFields[$field]
->getValue($element) => $element
);
++$this->_resultCounter; ++$this->_resultCounter;
end($result);
$this->_identifierMap[$dqlAlias][$id[$dqlAlias]] = key($result);
} else { } else {
$result[$this->_ce[$entityName] $result[$key] = $element;
->reflFields[$field] $this->_identifierMap[$dqlAlias][$id[$dqlAlias]] = $key;
->getValue($element)] = $element;
} }
} else { } else {
if ($this->_rsm->isMixed) { if ($this->_rsm->isMixed) {
$result[] = array($element); $element = array(0 => $element);
++$this->_resultCounter; ++$this->_resultCounter;
} else {
$result[] = $element;
} }
$result[] = $element;
end($result);
$this->_identifierMap[$dqlAlias][$id[$dqlAlias]] = key($result);
} }
$last = end($result);
$this->_identifierMap[$dqlAlias][$id[$dqlAlias]] = key($result);
// Update result pointer // Update result pointer
$this->_resultPointers[$dqlAlias] = $last; $this->_resultPointers[$dqlAlias] = $element;
} else { } else {
// Update result pointer // Update result pointer
$index = $this->_identifierMap[$dqlAlias][$id[$dqlAlias]]; $index = $this->_identifierMap[$dqlAlias][$id[$dqlAlias]];
...@@ -407,10 +398,4 @@ class ObjectHydrator extends AbstractHydrator ...@@ -407,10 +398,4 @@ class ObjectHydrator extends AbstractHydrator
} }
} }
} }
/** {@inheritdoc} */
protected function _getRowContainer()
{
return array();
}
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment