diff --git a/lib/Doctrine/ORM/UnitOfWork.php b/lib/Doctrine/ORM/UnitOfWork.php index 8a3d582f7..e53f38c52 100644 --- a/lib/Doctrine/ORM/UnitOfWork.php +++ b/lib/Doctrine/ORM/UnitOfWork.php @@ -746,7 +746,7 @@ class UnitOfWork implements PropertyChangedListener $hasPostUpdateListeners = $this->evm->hasListeners(Events::postUpdate); foreach ($this->entityUpdates as $oid => $entity) { - if (get_class($entity) == $className || $entity instanceof Proxy && $entity instanceof $className) { + if (get_class($entity) == $className || $entity instanceof Proxy && get_parent_class($entity) == $className) { if ($hasPreUpdateLifecycleCallbacks) { $class->invokeLifecycleCallbacks(Events::preUpdate, $entity); @@ -788,7 +788,7 @@ class UnitOfWork implements PropertyChangedListener $hasListeners = $this->evm->hasListeners(Events::postRemove); foreach ($this->entityDeletions as $oid => $entity) { - if (get_class($entity) == $className || $entity instanceof Proxy && $entity instanceof $className) { + if (get_class($entity) == $className || $entity instanceof Proxy && get_parent_class($entity) == $className) { $persister->delete($entity); unset( $this->entityDeletions[$oid], diff --git a/tests/Doctrine/Tests/ORM/Functional/ReferenceProxyTest.php b/tests/Doctrine/Tests/ORM/Functional/ReferenceProxyTest.php index 9fcb90a5e..3b6e1f546 100644 --- a/tests/Doctrine/Tests/ORM/Functional/ReferenceProxyTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/ReferenceProxyTest.php @@ -112,4 +112,22 @@ class ReferenceProxyTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->_em->getUnitOfWork()->initializeObject($entity); $this->assertTrue($entity->__isInitialized__, "Should be initialized after called UnitOfWork::initializeObject()"); } + + /** + * @group DDC-1163 + */ + public function testInitializeChangeAndFlushProxy() + { + $id = $this->createProduct(); + + /* @var $entity Doctrine\Tests\Models\ECommerce\ECommerceProduct */ + $entity = $this->_em->getReference('Doctrine\Tests\Models\ECommerce\ECommerceProduct' , $id); + $entity->setName('Doctrine 2 Cookbook'); + + $this->_em->flush(); + $this->_em->clear(); + + $entity = $this->_em->getReference('Doctrine\Tests\Models\ECommerce\ECommerceProduct' , $id); + $this->assertEquals('Doctrine 2 Cookbook', $entity->getName()); + } } diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1163Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1163Test.php new file mode 100644 index 000000000..7142ff211 --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1163Test.php @@ -0,0 +1,215 @@ +_em->getConnection()->getConfiguration()->setSQLLogger(new \Doctrine\DBAL\Logging\EchoSQLLogger); + $this->_schemaTool->createSchema(array( + $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC1163Product'), + $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC1163SpecialProduct'), + $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC1163ProxyHolder'), + $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC1163Tag'), + )); + } + + public function testIssue() + { + $this->createSpecialProductAndProxyHolderReferencingIt(); + $this->_em->clear(); + + $this->createProxyForSpecialProduct(); + + $this->setPropertyAndAssignTagToSpecialProduct(); + + // fails + $this->_em->flush(); + } + + private function createSpecialProductAndProxyHolderReferencingIt() + { + $specialProduct = new DDC1163SpecialProduct(); + $this->_em->persist($specialProduct); + + $proxyHolder = new DDC1163ProxyHolder(); + $this->_em->persist($proxyHolder); + + $proxyHolder->setSpecialProduct($specialProduct); + + $this->_em->flush(); + + $this->productId = $specialProduct->getId(); + $this->proxyHolderId = $proxyHolder->getId(); + } + + /** + * We want Doctrine to instantiate a lazy-load proxy for the previously created + * 'SpecialProduct' and register it. + * + * When Doctrine loads the 'ProxyHolder', it will do just that because the 'ProxyHolder' + * references the 'SpecialProduct'. + */ + private function createProxyForSpecialProduct() + { + /* @var $proxyHolder ProxyHolder */ + $proxyHolder = $this->_em->find(__NAMESPACE__ . '\\DDC1163ProxyHolder', $this->proxyHolderId); + + $this->assertInstanceOf(__NAMESPACE__.'\\DDC1163SpecialProduct', $proxyHolder->getSpecialProduct()); + } + + private function setPropertyAndAssignTagToSpecialProduct() + { + /* @var $specialProduct SpecialProduct */ + $specialProduct = $this->_em->find(__NAMESPACE__ . '\\DDC1163SpecialProduct', $this->productId); + + $this->assertInstanceOf(__NAMESPACE__.'\\DDC1163SpecialProduct', $specialProduct); + $this->assertInstanceOf('Doctrine\ORM\Proxy\Proxy', $specialProduct); + + $specialProduct->setSubclassProperty('foobar'); + + // this screams violation of law of demeter ;) + $this->assertEquals( + __NAMESPACE__.'\\DDC1163SpecialProduct', + $this->_em->getUnitOfWork()->getEntityPersister(get_class($specialProduct))->getClassMetadata()->name + ); + + $tag = new DDC1163Tag('Foo'); + $this->_em->persist($tag); + $tag->setProduct($specialProduct); + } +} + +/** + * @Entity + */ +class DDC1163ProxyHolder +{ + + /** + * @var int + * @Column(name="id", type="integer") + * @Id + * @GeneratedValue(strategy="AUTO") + */ + private $id; + /** + * @var SpecialProduct + * @OneToOne(targetEntity="DDC1163SpecialProduct") + */ + private $specialProduct; + + public function getId() + { + return $this->id; + } + + public function setSpecialProduct(DDC1163SpecialProduct $specialProduct) + { + $this->specialProduct = $specialProduct; + } + + public function getSpecialProduct() + { + return $this->specialProduct; + } + +} + +/** + * @Entity + * @InheritanceType("JOINED") + * @DiscriminatorColumn(name="type", type="string") + * @DiscriminatorMap({"special" = "DDC1163SpecialProduct"}) + */ +abstract class DDC1163Product +{ + + /** + * @var int + * @Column(name="id", type="integer") + * @Id + * @GeneratedValue(strategy="AUTO") + */ + protected $id; + + public function getId() + { + return $this->id; + } + +} + +/** + * @Entity + */ +class DDC1163SpecialProduct extends DDC1163Product +{ + + /** + * @var string + * @Column(name="subclass_property", type="string", nullable=true) + */ + private $subclassProperty; + + /** + * @param string $value + */ + public function setSubclassProperty($value) + { + $this->subclassProperty = $value; + } + +} + +/** + * @Entity + */ +class DDC1163Tag +{ + + /** + * @var int + * @Column(name="id", type="integer") + * @Id + * @GeneratedValue(strategy="AUTO") + */ + private $id; + /** + * @var string + * @Column(name="name", type="string") + */ + private $name; + /** + * @var Product + * @ManyToOne(targetEntity="DDC1163Product", inversedBy="tags") + * @JoinColumns({ + * @JoinColumn(name="product_id", referencedColumnName="id") + * }) + */ + private $product; + + /** + * @param string $name + */ + public function __construct($name) + { + $this->name = $name; + } + + /** + * @param Product $product + */ + public function setProduct(DDC1163Product $product) + { + $this->product = $product; + } + +} \ No newline at end of file