From 913e58e3850b251fa6c218feaa34e50d6ee9f770 Mon Sep 17 00:00:00 2001 From: "Roman S. Borschel" Date: Tue, 20 Jul 2010 14:20:13 +0200 Subject: [PATCH] [DDC-167] Implemented. --- lib/Doctrine/ORM/EntityManager.php | 46 +++++++++++++++++-- lib/Doctrine/ORM/Mapping/ClassMetadata.php | 10 ++-- .../Doctrine/Tests/ORM/EntityManagerTest.php | 8 ++++ .../ORM/Functional/BasicFunctionalTest.php | 24 ++++++++++ .../ORM/Query/SelectSqlGenerationTest.php | 2 +- 5 files changed, 77 insertions(+), 13 deletions(-) diff --git a/lib/Doctrine/ORM/EntityManager.php b/lib/Doctrine/ORM/EntityManager.php index 116485fc9..397ff8938 100644 --- a/lib/Doctrine/ORM/EntityManager.php +++ b/lib/Doctrine/ORM/EntityManager.php @@ -330,12 +330,10 @@ class EntityManager /** * Gets a reference to the entity identified by the given type and identifier - * without actually loading it. - * - * If partial objects are allowed, this method will return a partial object that only - * has its identifier populated. Otherwise a proxy is returned that automatically - * loads itself on first access. + * without actually loading it, if the entity is not yet loaded. * + * @param string $entityName The name of the entity type. + * @param mixed $identifier The entity identifier. * @return object The entity reference. */ public function getReference($entityName, $identifier) @@ -355,6 +353,44 @@ class EntityManager return $entity; } + /** + * Gets a partial reference to the entity identified by the given type and identifier + * without actually loading it, if the entity is not yet loaded. + * + * The returned reference may be a partial object if the entity is not yet loaded/managed. + * If it is a partial object it will not initialize the rest of the entity state on access. + * Thus you can only ever safely access the identifier of an entity obtained through + * this method. + * + * The use-cases for partial references involve maintaining bidirectional associations + * without loading one side of the association or to update an entity without loading it. + * Note, however, that in the latter case the original (persistent) entity data will + * never be visible to the application (especially not event listeners) as it will + * never be loaded in the first place. + * + * @param string $entityName The name of the entity type. + * @param mixed $identifier The entity identifier. + * @return object The (partial) entity reference. + */ + public function getPartialReference($entityName, $identifier) + { + $class = $this->metadataFactory->getMetadataFor($entityName); + + // Check identity map first, if its already in there just return it. + if ($entity = $this->unitOfWork->tryGetById($identifier, $class->rootEntityName)) { + return $entity; + } + if ( ! is_array($identifier)) { + $identifier = array($class->identifier[0] => $identifier); + } + + $entity = $class->newInstance(); + $class->setIdentifierValues($entity, $identifier); + $this->unitOfWork->registerManaged($entity, $identifier, array()); + + return $entity; + } + /** * Clears the EntityManager. All entities that are currently managed * by this EntityManager become detached. diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadata.php b/lib/Doctrine/ORM/Mapping/ClassMetadata.php index 64be31b5b..de3cad029 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadata.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadata.php @@ -158,14 +158,10 @@ class ClassMetadata extends ClassMetadataInfo * @param mixed $id * @todo Rename to assignIdentifier() */ - public function setIdentifierValues($entity, $id) + public function setIdentifierValues($entity, array $id) { - if ($this->isIdentifierComposite) { - foreach ($id as $idField => $idValue) { - $this->reflFields[$idField]->setValue($entity, $idValue); - } - } else { - $this->reflFields[$this->identifier[0]]->setValue($entity, $id); + foreach ($id as $idField => $idValue) { + $this->reflFields[$idField]->setValue($entity, $idValue); } } diff --git a/tests/Doctrine/Tests/ORM/EntityManagerTest.php b/tests/Doctrine/Tests/ORM/EntityManagerTest.php index f69ed6be7..ad5f41de7 100644 --- a/tests/Doctrine/Tests/ORM/EntityManagerTest.php +++ b/tests/Doctrine/Tests/ORM/EntityManagerTest.php @@ -76,6 +76,14 @@ class EntityManagerTest extends \Doctrine\Tests\OrmTestCase $this->assertType('\Doctrine\ORM\Query', $this->_em->createQuery()); } + public function testGetPartialReference() + { + $user = $this->_em->getPartialReference('Doctrine\Tests\Models\CMS\CmsUser', 42); + $this->assertTrue($this->_em->contains($user)); + $this->assertEquals(42, $user->id); + $this->assertNull($user->getName()); + } + public function testCreateQuery() { $q = $this->_em->createQuery('SELECT 1'); diff --git a/tests/Doctrine/Tests/ORM/Functional/BasicFunctionalTest.php b/tests/Doctrine/Tests/ORM/Functional/BasicFunctionalTest.php index 58f922a2e..84155eee0 100644 --- a/tests/Doctrine/Tests/ORM/Functional/BasicFunctionalTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/BasicFunctionalTest.php @@ -744,6 +744,30 @@ class BasicFunctionalTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->assertEquals(0, $this->_em->getConnection()->fetchColumn("select count(*) from cms_users_groups")); } + + public function testGetPartialReferenceToUpdateObjectWithoutLoadingIt() + { + //$this->_em->getConnection()->getConfiguration()->setSQLLogger(new \Doctrine\DBAL\Logging\EchoSQLLogger); + $user = new CmsUser(); + $user->username = "beberlei"; + $user->name = "Benjamin E."; + $user->status = 'active'; + $this->_em->persist($user); + $this->_em->flush(); + $userId = $user->id; + $this->_em->clear(); + + $user = $this->_em->getPartialReference('Doctrine\Tests\Models\CMS\CmsUser', $userId); + $this->assertTrue($this->_em->contains($user)); + $this->assertNull($user->getName()); + $this->assertEquals($userId, $user->id); + + $user->name = 'Stephan'; + $this->_em->flush(); + $this->_em->clear(); + + $this->assertEquals('Stephan', $this->_em->find(get_class($user), $userId)->name); + } //DRAFT OF EXPECTED/DESIRED BEHAVIOR /*public function testPersistentCollectionContainsDoesNeverInitialize() diff --git a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php index 21cea7f08..97cf04985 100644 --- a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php +++ b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php @@ -387,7 +387,7 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase // Tough one: Many-many self-referencing ("friends") with class table inheritance $q3 = $this->_em->createQuery('SELECT p FROM Doctrine\Tests\Models\Company\CompanyPerson p WHERE :param MEMBER OF p.friends'); $person = new \Doctrine\Tests\Models\Company\CompanyPerson; - $this->_em->getClassMetadata(get_class($person))->setIdentifierValues($person, 101); + $this->_em->getClassMetadata(get_class($person))->setIdentifierValues($person, array('id' => 101)); $q3->setParameter('param', $person); $this->assertEquals( 'SELECT c0_.id AS id0, c0_.name AS name1, c1_.title AS title2, c1_.car_id AS car_id3, c2_.salary AS salary4, c2_.department AS department5, c0_.discr AS discr6, c0_.spouse_id AS spouse_id7 FROM company_persons c0_ LEFT JOIN company_managers c1_ ON c0_.id = c1_.id LEFT JOIN company_employees c2_ ON c0_.id = c2_.id WHERE EXISTS (SELECT 1 FROM company_persons_friends c3_ INNER JOIN company_persons c4_ ON c3_.friend_id = c4_.id WHERE c3_.person_id = c0_.id AND c4_.id = ?)',