diff --git a/lib/Doctrine/ORM/Cache/DefaultEntityHydrator.php b/lib/Doctrine/ORM/Cache/DefaultEntityHydrator.php index 1190b38fd..200af9c0d 100644 --- a/lib/Doctrine/ORM/Cache/DefaultEntityHydrator.php +++ b/lib/Doctrine/ORM/Cache/DefaultEntityHydrator.php @@ -25,6 +25,7 @@ use Doctrine\Common\Util\ClassUtils; use Doctrine\ORM\Query; use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\Utility\IdentifierFlattener; /** * Default hydrator cache for entities @@ -44,6 +45,13 @@ class DefaultEntityHydrator implements EntityHydrator */ private $uow; + /** + * The IdentifierFlattener used for manipulating identifiers + * + * @var \Doctrine\ORM\Utility\IdentifierFlattener + */ + private $identifierFlattener; + /** * @var array */ @@ -56,6 +64,7 @@ class DefaultEntityHydrator implements EntityHydrator { $this->em = $em; $this->uow = $em->getUnitOfWork(); + $this->identifierFlattener = new IdentifierFlattener($em->getUnitOfWork(), $em->getMetadataFactory()); } /** @@ -72,8 +81,31 @@ class DefaultEntityHydrator implements EntityHydrator continue; } - if ( ! isset($assoc['cache']) || ! ($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])); + unset($data[$name]); + + foreach ($associationIds as $fieldName => $fieldValue) { + + if (isset($targetClassMetadata->associationMappings[$fieldName])){ + $targetAssoc = $targetClassMetadata->associationMappings[$fieldName]; + + foreach($assoc['targetToSourceKeyColumns'] as $referencedColumn => $localColumn) { + + if (isset($targetAssoc['sourceToTargetKeyColumns'][$referencedColumn])) { + $data[$localColumn] = $fieldValue; + } + } + }else{ + $data[$assoc['targetToSourceKeyColumns'][$targetClassMetadata->columnNames[$fieldName]]] = $fieldValue; + } + } continue; } diff --git a/tests/Doctrine/Tests/Models/Cache/Action.php b/tests/Doctrine/Tests/Models/Cache/Action.php new file mode 100644 index 000000000..25609abed --- /dev/null +++ b/tests/Doctrine/Tests/Models/Cache/Action.php @@ -0,0 +1,43 @@ +name = $name; + $this->tokens = new ArrayCollection(); + } + + public function addToken(Token $token) + { + $this->tokens[] = $token; + $token->action = $this; + } +} diff --git a/tests/Doctrine/Tests/Models/Cache/Client.php b/tests/Doctrine/Tests/Models/Cache/Client.php new file mode 100644 index 000000000..70544256b --- /dev/null +++ b/tests/Doctrine/Tests/Models/Cache/Client.php @@ -0,0 +1,29 @@ +name = $name; + } +} diff --git a/tests/Doctrine/Tests/Models/Cache/ComplexAction.php b/tests/Doctrine/Tests/Models/Cache/ComplexAction.php new file mode 100644 index 000000000..46d0414d3 --- /dev/null +++ b/tests/Doctrine/Tests/Models/Cache/ComplexAction.php @@ -0,0 +1,68 @@ +name = $name; + $this->action1 = $action1; + $this->action2 = $action2; + $this->tokens = new ArrayCollection(); + } + + public function addToken(Token $token) + { + $this->tokens[] = $token; + $token->complexAction = $this; + } + + /** + * @return Action + */ + public function getAction1() + { + return $this->action1; + } + + /** + * @return Action + */ + public function getAction2() + { + return $this->action2; + } +} diff --git a/tests/Doctrine/Tests/Models/Cache/Login.php b/tests/Doctrine/Tests/Models/Cache/Login.php new file mode 100644 index 000000000..8de766d38 --- /dev/null +++ b/tests/Doctrine/Tests/Models/Cache/Login.php @@ -0,0 +1,43 @@ +name = $name; + } + + /** + * @return Token + */ + public function getToken() + { + return $this->token; + } +} diff --git a/tests/Doctrine/Tests/Models/Cache/Token.php b/tests/Doctrine/Tests/Models/Cache/Token.php new file mode 100644 index 000000000..f6c712e36 --- /dev/null +++ b/tests/Doctrine/Tests/Models/Cache/Token.php @@ -0,0 +1,95 @@ +logins = new ArrayCollection(); + $this->token = $token; + $this->client = $client; + $this->expiresAt = new \DateTime(date('Y-m-d H:i:s', strtotime("+7 day"))); + } + + /** + * @param Login $login + */ + public function addLogin(Login $login) + { + $this->logins[] = $login; + $login->token = $this; + } + + /** + * @return Client + */ + public function getClient() + { + return $this->client; + } + + /** + * @return Action + */ + public function getAction() + { + return $this->action; + } + + /** + * @return ComplexAction + */ + public function getComplexAction() + { + return $this->complexAction; + } + +} diff --git a/tests/Doctrine/Tests/ORM/Functional/SecondLevelCacheManyToOneTest.php b/tests/Doctrine/Tests/ORM/Functional/SecondLevelCacheManyToOneTest.php index 9135b508a..8c0225e7f 100644 --- a/tests/Doctrine/Tests/ORM/Functional/SecondLevelCacheManyToOneTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/SecondLevelCacheManyToOneTest.php @@ -2,9 +2,12 @@ namespace Doctrine\Tests\ORM\Functional; +use Doctrine\Tests\Models\Cache\ComplexAction; use Doctrine\Tests\Models\Cache\Country; use Doctrine\Tests\Models\Cache\State; use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\Tests\Models\Cache\Token; +use Doctrine\Tests\Models\Cache\Action; /** * @group DDC-2183 @@ -140,4 +143,86 @@ class SecondLevelCacheManyToOneTest extends SecondLevelCacheAbstractTest $this->assertEquals($queryCount + 2, $this->getCurrentQueryCount()); } + + public function testPutAndLoadNonCacheableManyToOne() + { + $this->assertNull($this->cache->getEntityCacheRegion(Action::CLASSNAME)); + $this->assertInstanceOf('Doctrine\ORM\Cache\Region', $this->cache->getEntityCacheRegion(Token::CLASSNAME)); + + $token = new Token('token-hash'); + $action = new Action('exec'); + $action->addToken($token); + + $this->_em->persist($token); + + $this->_em->flush(); + $this->_em->clear(); + + $this->assertTrue($this->cache->containsEntity(Token::CLASSNAME, $token->token)); + $this->assertFalse($this->cache->containsEntity(Token::CLASSNAME, $action->id)); + + $queryCount = $this->getCurrentQueryCount(); + $entity = $this->_em->find(Token::CLASSNAME, $token->token); + + $this->assertInstanceOf(Token::CLASSNAME, $entity); + $this->assertEquals('token-hash', $entity->token); + + $this->assertInstanceOf(Action::CLASSNAME, $entity->getAction()); + $this->assertEquals('exec', $entity->getAction()->name); + + $this->assertEquals($queryCount + 1, $this->getCurrentQueryCount()); + } + + public function testPutAndLoadNonCacheableCompositeManyToOne() + { + $this->assertNull($this->cache->getEntityCacheRegion(Action::CLASSNAME)); + $this->assertNull($this->cache->getEntityCacheRegion(ComplexAction::CLASSNAME)); + $this->assertInstanceOf('Doctrine\ORM\Cache\Region', $this->cache->getEntityCacheRegion(Token::CLASSNAME)); + + $token = new Token('token-hash'); + + $action1 = new Action('login'); + $action2 = new Action('logout'); + $action3 = new Action('rememberme'); + + $complexAction = new ComplexAction($action1, $action3, 'login,rememberme'); + + $complexAction->addToken($token); + + $token->action = $action2; + + $this->_em->persist($token); + + $this->_em->flush(); + $this->_em->clear(); + + $this->assertTrue($this->cache->containsEntity(Token::CLASSNAME, $token->token)); + $this->assertFalse($this->cache->containsEntity(Action::CLASSNAME, $action1->id)); + $this->assertFalse($this->cache->containsEntity(Action::CLASSNAME, $action2->id)); + $this->assertFalse($this->cache->containsEntity(Action::CLASSNAME, $action3->id)); + + $queryCount = $this->getCurrentQueryCount(); + /** + * @var $entity Token + */ + $entity = $this->_em->find(Token::CLASSNAME, $token->token); + + $this->assertInstanceOf(Token::CLASSNAME, $entity); + $this->assertEquals('token-hash', $entity->token); + + $this->assertEquals($queryCount, $this->getCurrentQueryCount()); + + $this->assertInstanceOf(Action::CLASSNAME, $entity->getAction()); + $this->assertInstanceOf(ComplexAction::CLASSNAME, $entity->getComplexAction()); + $this->assertEquals($queryCount, $this->getCurrentQueryCount()); + + $this->assertInstanceOf(Action::CLASSNAME, $entity->getComplexAction()->getAction1()); + $this->assertInstanceOf(Action::CLASSNAME, $entity->getComplexAction()->getAction2()); + $this->assertEquals($queryCount + 1, $this->getCurrentQueryCount()); + + $this->assertEquals('login', $entity->getComplexAction()->getAction1()->name); + $this->assertEquals($queryCount + 2, $this->getCurrentQueryCount()); + $this->assertEquals('rememberme', $entity->getComplexAction()->getAction2()->name); + $this->assertEquals($queryCount + 3, $this->getCurrentQueryCount()); + } } \ No newline at end of file diff --git a/tests/Doctrine/Tests/ORM/Functional/SecondLevelCacheOneToManyTest.php b/tests/Doctrine/Tests/ORM/Functional/SecondLevelCacheOneToManyTest.php index 4b978b1d6..3e4833dc8 100644 --- a/tests/Doctrine/Tests/ORM/Functional/SecondLevelCacheOneToManyTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/SecondLevelCacheOneToManyTest.php @@ -3,7 +3,9 @@ namespace Doctrine\Tests\ORM\Functional; use Doctrine\Tests\Models\Cache\City; +use Doctrine\Tests\Models\Cache\Login; use Doctrine\Tests\Models\Cache\State; +use Doctrine\Tests\Models\Cache\Token; use Doctrine\Tests\Models\Cache\Travel; use Doctrine\Tests\Models\Cache\Traveler; @@ -381,4 +383,33 @@ class SecondLevelCacheOneToManyTest extends SecondLevelCacheAbstractTest $this->assertEquals(4, $result->getTravels()->count()); } + + public function testPutAndLoadNonCacheableOneToMany() + { + $this->assertNull($this->cache->getEntityCacheRegion(Login::CLASSNAME)); + $this->assertInstanceOf('Doctrine\ORM\Cache\Region', $this->cache->getEntityCacheRegion(Token::CLASSNAME)); + + $l1 = new Login('session1'); + $l2 = new Login('session2'); + $token = new Token('token-hash'); + $token->addLogin($l1); + $token->addLogin($l2); + + $this->_em->persist($token); + $this->_em->flush(); + $this->_em->clear(); + + $this->assertTrue($this->cache->containsEntity(Token::CLASSNAME, $token->token)); + + $queryCount = $this->getCurrentQueryCount(); + + $entity = $this->_em->find(Token::CLASSNAME, $token->token); + + $this->assertInstanceOf(Token::CLASSNAME, $entity); + $this->assertEquals('token-hash', $entity->token); + $this->assertEquals($queryCount, $this->getCurrentQueryCount()); + + $this->assertCount(2, $entity->logins); + $this->assertEquals($queryCount + 1, $this->getCurrentQueryCount()); + } } diff --git a/tests/Doctrine/Tests/ORM/Functional/SecondLevelCacheOneToOneTest.php b/tests/Doctrine/Tests/ORM/Functional/SecondLevelCacheOneToOneTest.php index 1f982261c..8b5ffa6f8 100644 --- a/tests/Doctrine/Tests/ORM/Functional/SecondLevelCacheOneToOneTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/SecondLevelCacheOneToOneTest.php @@ -2,6 +2,8 @@ namespace Doctrine\Tests\ORM\Functional; +use Doctrine\Tests\Models\Cache\Client; +use Doctrine\Tests\Models\Cache\Token; use Doctrine\Tests\Models\Cache\Traveler; use Doctrine\Tests\Models\Cache\TravelerProfile; use Doctrine\Tests\Models\Cache\TravelerProfileInfo; @@ -187,4 +189,32 @@ class SecondLevelCacheOneToOneTest extends SecondLevelCacheAbstractTest $this->assertEquals($queryCount, $this->getCurrentQueryCount()); } + public function testPutAndLoadNonCacheableOneToOne() + { + $this->assertNull($this->cache->getEntityCacheRegion(Client::CLASSNAME)); + $this->assertInstanceOf('Doctrine\ORM\Cache\Region', $this->cache->getEntityCacheRegion(Token::CLASSNAME)); + + $client = new Client('FabioBatSilva'); + $token = new Token('token-hash', $client); + + $this->_em->persist($client); + $this->_em->persist($token); + $this->_em->flush(); + $this->_em->clear(); + + $queryCount = $this->getCurrentQueryCount(); + + $this->assertTrue($this->cache->containsEntity(Token::CLASSNAME, $token->token)); + $this->assertFalse($this->cache->containsEntity(Client::CLASSNAME, $client->id)); + + $entity = $this->_em->find(Token::CLASSNAME, $token->token); + + $this->assertInstanceOf(Token::CLASSNAME, $entity); + $this->assertInstanceOf(Client::CLASSNAME, $entity->getClient()); + $this->assertEquals('token-hash', $entity->token); + $this->assertEquals($queryCount, $this->getCurrentQueryCount()); + + $this->assertEquals('FabioBatSilva', $entity->getClient()->name); + $this->assertEquals($queryCount + 1, $this->getCurrentQueryCount()); + } } \ No newline at end of file diff --git a/tests/Doctrine/Tests/OrmFunctionalTestCase.php b/tests/Doctrine/Tests/OrmFunctionalTestCase.php index ae9f4b43b..6feb8ab97 100644 --- a/tests/Doctrine/Tests/OrmFunctionalTestCase.php +++ b/tests/Doctrine/Tests/OrmFunctionalTestCase.php @@ -180,6 +180,11 @@ abstract class OrmFunctionalTestCase extends OrmTestCase 'Doctrine\Tests\Models\Cache\Beach', 'Doctrine\Tests\Models\Cache\Bar', 'Doctrine\Tests\Models\Cache\Flight', + 'Doctrine\Tests\Models\Cache\Token', + 'Doctrine\Tests\Models\Cache\Login', + 'Doctrine\Tests\Models\Cache\Client', + 'Doctrine\Tests\Models\Cache\Action', + 'Doctrine\Tests\Models\Cache\ComplexAction', 'Doctrine\Tests\Models\Cache\AttractionInfo', 'Doctrine\Tests\Models\Cache\AttractionContactInfo', 'Doctrine\Tests\Models\Cache\AttractionLocationInfo' @@ -418,6 +423,11 @@ abstract class OrmFunctionalTestCase extends OrmTestCase $conn->executeUpdate('DELETE FROM cache_city'); $conn->executeUpdate('DELETE FROM cache_state'); $conn->executeUpdate('DELETE FROM cache_country'); + $conn->executeUpdate('DELETE FROM cache_login'); + $conn->executeUpdate('DELETE FROM cache_complex_action'); + $conn->executeUpdate('DELETE FROM cache_token'); + $conn->executeUpdate('DELETE FROM cache_action'); + $conn->executeUpdate('DELETE FROM cache_client'); } if (isset($this->_usedModelSets['ddc3346'])) {