diff --git a/lib/Doctrine/ORM/PersistentCollection.php b/lib/Doctrine/ORM/PersistentCollection.php index 2c5f374e8..93e875c5c 100644 --- a/lib/Doctrine/ORM/PersistentCollection.php +++ b/lib/Doctrine/ORM/PersistentCollection.php @@ -571,6 +571,15 @@ final class PersistentCollection implements \Doctrine\Common\Collections\Collect $this->_initialize(); return $this->_coll->partition($p); } + + /** + * {@inheritdoc} + */ + public function toArray() + { + $this->_initialize(); + return $this->_coll->toArray(); + } /** * {@inheritdoc} @@ -637,11 +646,6 @@ final class PersistentCollection implements \Doctrine\Common\Collections\Collect return $this->remove($offset); } - public function toArray() - { - return $this->_coll->toArray(); - } - public function key() { return $this->_coll->key(); diff --git a/lib/Doctrine/ORM/Proxy/Proxy.php b/lib/Doctrine/ORM/Proxy/Proxy.php index 904e7d4ef..853f9c1f0 100644 --- a/lib/Doctrine/ORM/Proxy/Proxy.php +++ b/lib/Doctrine/ORM/Proxy/Proxy.php @@ -27,7 +27,4 @@ namespace Doctrine\ORM\Proxy; * @author Roman Borschel * @since 2.0 */ -interface Proxy -{ - function __isInitialized__(); -} \ No newline at end of file +interface Proxy {} \ No newline at end of file diff --git a/lib/Doctrine/ORM/Proxy/ProxyFactory.php b/lib/Doctrine/ORM/Proxy/ProxyFactory.php index 9a2812f27..b9e53d611 100644 --- a/lib/Doctrine/ORM/Proxy/ProxyFactory.php +++ b/lib/Doctrine/ORM/Proxy/ProxyFactory.php @@ -253,26 +253,25 @@ namespace { class extends \ implements \Doctrine\ORM\Proxy\Proxy { private $_entityPersister; private $_identifier; - private $_loaded = false; + public $__isInitialized__ = false; public function __construct($entityPersister, $identifier) { $this->_entityPersister = $entityPersister; $this->_identifier = $identifier; } private function _load() { - if ( ! $this->_loaded) { - $this->_loaded = true; + if (!$this->__isInitialized__) { + $this->__isInitialized__ = true; $this->_entityPersister->load($this->_identifier, $this); unset($this->_entityPersister); unset($this->_identifier); } } - public function __isInitialized__() { return $this->_loaded; } public function __sleep() { - if (!$this->_loaded) { + if (!$this->__isInitialized__) { throw new \RuntimeException("Not fully loaded proxy can not be serialized."); } diff --git a/lib/Doctrine/ORM/UnitOfWork.php b/lib/Doctrine/ORM/UnitOfWork.php index c970cec31..eb1cbc6c7 100644 --- a/lib/Doctrine/ORM/UnitOfWork.php +++ b/lib/Doctrine/ORM/UnitOfWork.php @@ -38,8 +38,7 @@ use Doctrine\Common\Collections\ArrayCollection, * @since 2.0 * @version $Revision$ * @author Roman Borschel - * @internal This class contains performance-critical code. Work with care and - * regularly run the ORM performance tests. + * @internal This class contains performance-critical code. */ class UnitOfWork implements PropertyChangedListener { @@ -401,7 +400,7 @@ class UnitOfWork implements PropertyChangedListener foreach ($entitiesToProcess as $entity) { // Ignore uninitialized proxy objects - if (/* $entity is readOnly || */ $entity instanceof Proxy && ! $entity->__isInitialized__()) { + if (/* $entity is readOnly || */ $entity instanceof Proxy && ! $entity->__isInitialized__) { continue; } // Only MANAGED entities that are NOT SCHEDULED FOR INSERTION are processed here. @@ -560,7 +559,7 @@ class UnitOfWork implements PropertyChangedListener // Look through the entities, and in any of their associations, for transient // enities, recursively. ("Persistence by reachability") if ($assoc->isOneToOne()) { - if ($value instanceof Proxy && ! $value->__isInitialized__()) { + if ($value instanceof Proxy && ! $value->__isInitialized__) { return; // Ignore uninitialized proxy objects } $value = array($value); @@ -1732,7 +1731,12 @@ class UnitOfWork implements PropertyChangedListener if (isset($this->_identityMap[$class->rootEntityName][$idHash])) { $entity = $this->_identityMap[$class->rootEntityName][$idHash]; $oid = spl_object_hash($entity); - $overrideLocalValues = isset($hints[Query::HINT_REFRESH]); + if ($entity instanceof Proxy && ! $entity->__isInitialized__) { + $entity->__isInitialized__ = true; + $overrideLocalValues = true; + } else { + $overrideLocalValues = isset($hints[Query::HINT_REFRESH]); + } } else { //$entity = clone $class->prototype; $entity = new $className; diff --git a/tests/Doctrine/Tests/ORM/Functional/DefaultValuesTest.php b/tests/Doctrine/Tests/ORM/Functional/DefaultValuesTest.php index 0878e1aea..71edf4dc2 100644 --- a/tests/Doctrine/Tests/ORM/Functional/DefaultValuesTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/DefaultValuesTest.php @@ -34,7 +34,7 @@ class DefaultValuesTest extends \Doctrine\Tests\OrmFunctionalTestCase $user2 = $this->_em->getReference(get_class($user), $userId); $this->_em->flush(); - $this->assertFalse($user2->__isInitialized__()); + $this->assertFalse($user2->__isInitialized__); $a = new DefaultValueAddress; $a->country = 'de'; @@ -46,7 +46,7 @@ class DefaultValuesTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->_em->persist($a); $this->_em->flush(); - $this->assertFalse($user2->__isInitialized__()); + $this->assertFalse($user2->__isInitialized__); $this->_em->clear(); $a2 = $this->_em->find(get_class($a), $a->id); diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC237Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC237Test.php new file mode 100644 index 000000000..877b3b861 --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC237Test.php @@ -0,0 +1,112 @@ +_schemaTool->createSchema(array( + $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC237EntityX'), + $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC237EntityY'), + $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC237EntityZ') + )); + } + + public function testUninitializedProxyIsInitializedOnFetchJoin() + { + $x = new DDC237EntityX; + $y = new DDC237EntityY; + $z = new DDC237EntityZ; + + $x->data = 'X'; + $y->data = 'Y'; + $z->data = 'Z'; + + $x->y = $y; + $z->y = $y; + + $this->_em->persist($x); + $this->_em->persist($y); + $this->_em->persist($z); + + $this->_em->flush(); + $this->_em->clear(); + + $x2 = $this->_em->find(get_class($x), $x->id); // proxy injected for Y + $this->assertTrue($x2->y instanceof \Doctrine\ORM\Proxy\Proxy); + $this->assertFalse($x2->y->__isInitialized__); + + // proxy for Y is in identity map + + $z2 = $this->_em->createQuery('select z,y from ' . get_class($z) . ' z join z.y y where z.id = ?1') + ->setParameter(1, $z->id) + ->getSingleResult(); + $this->assertTrue($z2->y instanceof \Doctrine\ORM\Proxy\Proxy); + $this->assertTrue($z2->y->__isInitialized__); + $this->assertEquals('Y', $z2->y->data); + $this->assertEquals($y->id, $z2->y->id); + + // since the Y is the same, the instance from the identity map is + // used, even if it is a proxy. + + $this->assertNotSame($x, $x2); + $this->assertNotSame($z, $z2); + $this->assertSame($z2->y, $x2->y); + $this->assertTrue($z2->y instanceof \Doctrine\ORM\Proxy\Proxy); + + } +} + + +/** + * @Entity @Table(name="ddc237_x") + */ +class DDC237EntityX +{ + /** + * @Id @Column(type="integer") @GeneratedValue(strategy="AUTO") + */ + public $id; + /** + * @Column(type="string") + */ + public $data; + /** + * @OneToOne(targetEntity="DDC237EntityY") + * @JoinColumn(name="y_id", referencedColumnName="id") + */ + public $y; +} + + +/** @Entity @Table(name="ddc237_y") */ +class DDC237EntityY +{ + /** + * @Id @Column(type="integer") @GeneratedValue(strategy="AUTO") + */ + public $id; + /** + * @Column(type="string") + */ + public $data; +} + +/** @Entity @Table(name="ddc237_z") */ +class DDC237EntityZ +{ + /** @Id @Column(type="integer") @GeneratedValue(strategy="AUTO") */ + public $id; + /** @Column(type="string") */ + public $data; + + /** + * @OneToOne(targetEntity="DDC237EntityY") + * @JoinColumn(name="y_id", referencedColumnName="id") + */ + public $y; +} \ No newline at end of file