diff --git a/lib/Doctrine/ORM/Cache/DefaultEntityHydrator.php b/lib/Doctrine/ORM/Cache/DefaultEntityHydrator.php index ad54dc245..b5fc84219 100644 --- a/lib/Doctrine/ORM/Cache/DefaultEntityHydrator.php +++ b/lib/Doctrine/ORM/Cache/DefaultEntityHydrator.php @@ -80,27 +80,39 @@ class DefaultEntityHydrator implements EntityHydrator continue; } - if (! ($assoc['type'] & ClassMetadata::TO_ONE)) { + if ( ! ($assoc['type'] & ClassMetadata::TO_ONE)) { unset($data[$name]); + continue; } if ( ! isset($assoc['cache'])) { $targetClassMetadata = $this->em->getClassMetadata($assoc['targetEntity']); - $associationIds = $this->identifierFlattener->flattenIdentifier($targetClassMetadata, $targetClassMetadata->getIdentifierValues($data[$name])); + $owningAssociation = ( ! $assoc['isOwningSide']) + ? $targetClassMetadata->associationMappings[$assoc['mappedBy']] + : $assoc; + $associationIds = $this->identifierFlattener->flattenIdentifier( + $targetClassMetadata, + $targetClassMetadata->getIdentifierValues($data[$name]) + ); + unset($data[$name]); foreach ($associationIds as $fieldName => $fieldValue) { - if (isset($targetClassMetadata->associationMappings[$fieldName])){ - $targetAssoc = $targetClassMetadata->associationMappings[$fieldName]; + if (isset($targetClassMetadata->fieldMappings[$fieldName])) { + $fieldMapping = $targetClassMetadata->fieldMappings[$fieldName]; - foreach($assoc['targetToSourceKeyColumns'] as $referencedColumn => $localColumn) { - if (isset($targetAssoc['sourceToTargetKeyColumns'][$referencedColumn])) { - $data[$localColumn] = $fieldValue; - } + $data[$owningAssociation['targetToSourceKeyColumns'][$fieldMapping['columnName']]] = $fieldValue; + + continue; + } + + $targetAssoc = $targetClassMetadata->associationMappings[$fieldName]; + + foreach($assoc['targetToSourceKeyColumns'] as $referencedColumn => $localColumn) { + if (isset($targetAssoc['sourceToTargetKeyColumns'][$referencedColumn])) { + $data[$localColumn] = $fieldValue; } - } else { - $data[$assoc['targetToSourceKeyColumns'][$targetClassMetadata->fieldMappings[$fieldName]['columnName']]] = $fieldValue; } } diff --git a/tests/Doctrine/Tests/Models/Cache/Address.php b/tests/Doctrine/Tests/Models/Cache/Address.php new file mode 100644 index 000000000..fa5363560 --- /dev/null +++ b/tests/Doctrine/Tests/Models/Cache/Address.php @@ -0,0 +1,35 @@ +location = $location; + } +} diff --git a/tests/Doctrine/Tests/Models/Cache/Person.php b/tests/Doctrine/Tests/Models/Cache/Person.php new file mode 100644 index 000000000..9cbe00c57 --- /dev/null +++ b/tests/Doctrine/Tests/Models/Cache/Person.php @@ -0,0 +1,35 @@ +name = $name; + } +} diff --git a/tests/Doctrine/Tests/ORM/Functional/SecondLevelCacheAbstractTest.php b/tests/Doctrine/Tests/ORM/Functional/SecondLevelCacheAbstractTest.php index e4672b9f2..320bbbb2f 100644 --- a/tests/Doctrine/Tests/ORM/Functional/SecondLevelCacheAbstractTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/SecondLevelCacheAbstractTest.php @@ -2,6 +2,8 @@ namespace Doctrine\Tests\ORM\Functional; +use Doctrine\Tests\Models\Cache\Address; +use Doctrine\Tests\Models\Cache\Person; use Doctrine\Tests\OrmFunctionalTestCase; use Doctrine\Tests\Models\Cache\Country; @@ -25,6 +27,8 @@ use Doctrine\Tests\Models\Cache\AttractionLocationInfo; */ abstract class SecondLevelCacheAbstractTest extends OrmFunctionalTestCase { + protected $people = array(); + protected $addresses = array(); protected $countries = array(); protected $states = array(); protected $cities = array(); @@ -224,6 +228,37 @@ abstract class SecondLevelCacheAbstractTest extends OrmFunctionalTestCase $this->_em->flush(); } + protected function loadFixturesPersonWithAddress() + { + $person1 = new Person('Guilherme Blanco'); + $address1 = new Address('Canada'); + + $person1->address = $address1; + $address1->person = $person1; + + $person2 = new Person('Marco Pivetta'); + $address2 = new Address('Germany'); + + $person2->address = $address2; + $address2->person = $person2; + + $this->people[] = $person1; + $this->people[] = $person2; + + $this->addresses[] = $address1; + $this->addresses[] = $address2; + + foreach ($this->people as $person) { + $this->_em->persist($person); + } + + foreach ($this->addresses as $address) { + $this->_em->persist($address); + } + + $this->_em->flush(); + } + protected function getEntityRegion($className) { return $this->cache->getEntityCacheRegion($className)->getName(); diff --git a/tests/Doctrine/Tests/ORM/Functional/SecondLevelCacheOneToOneTest.php b/tests/Doctrine/Tests/ORM/Functional/SecondLevelCacheOneToOneTest.php index 8b5ffa6f8..e84be7e07 100644 --- a/tests/Doctrine/Tests/ORM/Functional/SecondLevelCacheOneToOneTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/SecondLevelCacheOneToOneTest.php @@ -2,7 +2,11 @@ namespace Doctrine\Tests\ORM\Functional; +use Doctrine\Tests\Models\Cache\Action; +use Doctrine\Tests\Models\Cache\Address; use Doctrine\Tests\Models\Cache\Client; +use Doctrine\Tests\Models\Cache\ComplexAction; +use Doctrine\Tests\Models\Cache\Person; use Doctrine\Tests\Models\Cache\Token; use Doctrine\Tests\Models\Cache\Traveler; use Doctrine\Tests\Models\Cache\TravelerProfile; @@ -189,6 +193,67 @@ class SecondLevelCacheOneToOneTest extends SecondLevelCacheAbstractTest $this->assertEquals($queryCount, $this->getCurrentQueryCount()); } + public function testInverseSidePutAndLoadOneToOneBidirectionalRelation() + { + $this->loadFixturesPersonWithAddress(); + + $this->_em->clear(); + + $this->cache->evictEntityRegion(Person::CLASSNAME); + $this->cache->evictEntityRegion(Address::CLASSNAME); + + $entity1 = $this->addresses[0]->person; + $entity2 = $this->addresses[1]->person; + + $this->assertFalse($this->cache->containsEntity(Person::CLASSNAME, $entity1->id)); + $this->assertFalse($this->cache->containsEntity(Person::CLASSNAME, $entity2->id)); + $this->assertFalse($this->cache->containsEntity(Address::CLASSNAME, $entity1->address->id)); + $this->assertFalse($this->cache->containsEntity(Address::CLASSNAME, $entity2->address->id)); + + $p1 = $this->_em->find(Person::CLASSNAME, $entity1->id); + $p2 = $this->_em->find(Person::CLASSNAME, $entity2->id); + + $this->assertEquals($entity1->id, $p1->id); + $this->assertEquals($entity1->name, $p1->name); + $this->assertEquals($entity1->address->id, $p1->address->id); + $this->assertEquals($entity1->address->location, $p1->address->location); + + $this->assertEquals($entity2->id, $p2->id); + $this->assertEquals($entity2->name, $p2->name); + $this->assertEquals($entity2->address->id, $p2->address->id); + $this->assertEquals($entity2->address->location, $p2->address->location); + + $this->assertTrue($this->cache->containsEntity(Person::CLASSNAME, $entity1->id)); + $this->assertTrue($this->cache->containsEntity(Person::CLASSNAME, $entity2->id)); + // The inverse side its not cached + $this->assertFalse($this->cache->containsEntity(Address::CLASSNAME, $entity1->address->id)); + $this->assertFalse($this->cache->containsEntity(Address::CLASSNAME, $entity2->address->id)); + + $this->_em->clear(); + + $queryCount = $this->getCurrentQueryCount(); + + $p3 = $this->_em->find(Person::CLASSNAME, $entity1->id); + $p4 = $this->_em->find(Person::CLASSNAME, $entity2->id); + + $this->assertInstanceOf(Person::CLASSNAME, $p3); + $this->assertInstanceOf(Person::CLASSNAME, $p4); + $this->assertInstanceOf(Address::CLASSNAME, $p3->address); + $this->assertInstanceOf(Address::CLASSNAME, $p4->address); + + $this->assertEquals($entity1->id, $p3->id); + $this->assertEquals($entity1->name, $p3->name); + $this->assertEquals($entity1->address->id, $p3->address->id); + $this->assertEquals($entity1->address->location, $p3->address->location); + + $this->assertEquals($entity2->id, $p4->id); + $this->assertEquals($entity2->name, $p4->name); + $this->assertEquals($entity2->address->id, $p4->address->id); + $this->assertEquals($entity2->address->location, $p4->address->location); + + $this->assertEquals($queryCount + 2, $this->getCurrentQueryCount()); + } + public function testPutAndLoadNonCacheableOneToOne() { $this->assertNull($this->cache->getEntityCacheRegion(Client::CLASSNAME)); diff --git a/tests/Doctrine/Tests/OrmFunctionalTestCase.php b/tests/Doctrine/Tests/OrmFunctionalTestCase.php index 2d7ed2b6c..d54c9cdf3 100644 --- a/tests/Doctrine/Tests/OrmFunctionalTestCase.php +++ b/tests/Doctrine/Tests/OrmFunctionalTestCase.php @@ -189,6 +189,8 @@ abstract class OrmFunctionalTestCase extends OrmTestCase 'Doctrine\Tests\Models\Cache\Token', 'Doctrine\Tests\Models\Cache\Login', 'Doctrine\Tests\Models\Cache\Client', + 'Doctrine\Tests\Models\Cache\Person', + 'Doctrine\Tests\Models\Cache\Address', 'Doctrine\Tests\Models\Cache\Action', 'Doctrine\Tests\Models\Cache\ComplexAction', 'Doctrine\Tests\Models\Cache\AttractionInfo',