From 5b5fb2b732842ff78a5ad60bb0d84c54b9e40fea Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Thu, 15 Dec 2011 23:00:01 +0100 Subject: [PATCH] DDC-1514 - Fix complex self-referencing + proxy hydration problem. --- .../ORM/Internal/Hydration/ObjectHydrator.php | 8 +- .../ORM/Functional/Ticket/DDC1514Test.php | 111 ++++++++++++++++++ 2 files changed, 117 insertions(+), 2 deletions(-) create mode 100644 tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1514Test.php diff --git a/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php b/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php index 36dbfa46a..f7de28538 100644 --- a/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php +++ b/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php @@ -26,7 +26,8 @@ use PDO, Doctrine\ORM\Event\LifecycleEventArgs, Doctrine\ORM\Events, Doctrine\Common\Collections\ArrayCollection, - Doctrine\Common\Collections\Collection; + Doctrine\Common\Collections\Collection, + Doctrine\ORM\Proxy\Proxy; /** * The ObjectHydrator constructs an object graph out of an SQL result set. @@ -358,6 +359,7 @@ class ObjectHydrator extends AbstractHydrator continue; } + $parentClass = $this->_ce[$this->_rsm->aliasMap[$parentAlias]]; $oid = spl_object_hash($parentObject); $relationField = $this->_rsm->relationMap[$dqlAlias]; @@ -412,7 +414,9 @@ class ObjectHydrator extends AbstractHydrator } else { // PATH B: Single-valued association $reflFieldValue = $reflField->getValue($parentObject); - if ( ! $reflFieldValue || isset($this->_hints[Query::HINT_REFRESH])) { + if ( ! $reflFieldValue || isset($this->_hints[Query::HINT_REFRESH]) || ($reflFieldValue instanceof Proxy && !$reflFieldValue->__isInitialized__)) { + // we only need to take action if this value is null, + // we refresh the entity or its an unitialized proxy. if (isset($nonemptyComponents[$dqlAlias])) { $element = $this->_getEntity($data, $dqlAlias); $reflField->setValue($parentObject, $element); diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1514Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1514Test.php new file mode 100644 index 000000000..c905d0048 --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1514Test.php @@ -0,0 +1,111 @@ +_schemaTool->createSchema(array( + $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC1514EntityA'), + $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC1514EntityB'), + $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC1514EntityC'), + )); + } catch (\Exception $ignored) { + } + } + + public function testIssue() + { + $a1 = new DDC1514EntityA(); + $a1->title = "foo"; + + $a2 = new DDC1514EntityA(); + $a2->title = "bar"; + + $b1 = new DDC1514EntityB(); + $b1->entityAFrom = $a1; + $b1->entityATo = $a2; + + $b2 = new DDC1514EntityB(); + $b2->entityAFrom = $a2; + $b2->entityATo = $a1; + + $c = new DDC1514EntityC(); + $c->title = "baz"; + $a2->entityC = $c; + + $this->_em->persist($a1); + $this->_em->persist($a2); + $this->_em->persist($b1); + $this->_em->persist($b2); + $this->_em->persist($c); + $this->_em->flush(); + $this->_em->clear(); + + $dql = "SELECT a, b, ba, c FROM " . __NAMESPACE__ . "\DDC1514EntityA AS a LEFT JOIN a.entitiesB AS b LEFT JOIN b.entityATo AS ba LEFT JOIN a.entityC AS c"; + $results = $this->_em->createQuery($dql)->getResult(); + + $this->assertEquals($c->title, $results[1]->entityC->title); + } +} + +/** + * @Entity + */ +class DDC1514EntityA +{ + /** @Id @Column(type="integer") @GeneratedValue */ + public $id; + /** @Column */ + public $title; + /** @ManyToMany(targetEntity="DDC1514EntityB", mappedBy="entityAFrom") */ + public $entitiesB; + /** @ManyToOne(targetEntity="DDC1514EntityC") */ + public $entityC; + + public function __construct() + { + $this->entitiesB = new ArrayCollection(); + } +} + +/** + * @Entity + */ +class DDC1514EntityB +{ + /** @Id @Column(type="integer") @GeneratedValue */ + public $id; + + /** + * @ManyToOne(targetEntity="DDC1514EntityA", inversedBy="entitiesB") + */ + public $entityAFrom; + /** + * @ManyToOne(targetEntity="DDC1514EntityA") + */ + public $entityATo; +} + +/** + * @Entity + */ +class DDC1514EntityC +{ + /** @Id @Column(type="integer") @GeneratedValue */ + public $id; + /** @Column */ + public $title; +}