1
0
mirror of synced 2024-12-05 03:06:05 +03:00

Merge pull request #1302 from goetas/pr1301

Store column values of not cache-able associations
This commit is contained in:
Marco Pivetta 2015-03-17 23:27:04 +00:00
commit 1add0a04fa
10 changed files with 467 additions and 1 deletions

View File

@ -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;
}

View File

@ -0,0 +1,43 @@
<?php
namespace Doctrine\Tests\Models\Cache;
use Doctrine\Common\Collections\ArrayCollection;
/**
* @Entity
* @Table("cache_action")
*/
class Action
{
const CLASSNAME = __CLASS__;
/**
* @Id
* @GeneratedValue
* @Column(type="integer")
*/
public $id;
/**
* @Column
*/
public $name;
/**
* @OneToMany(targetEntity="Token", cascade={"persist", "remove"}, mappedBy="action")
*/
public $tokens;
public function __construct($name)
{
$this->name = $name;
$this->tokens = new ArrayCollection();
}
public function addToken(Token $token)
{
$this->tokens[] = $token;
$token->action = $this;
}
}

View File

@ -0,0 +1,29 @@
<?php
namespace Doctrine\Tests\Models\Cache;
/**
* @Entity
* @Table("cache_client")
*/
class Client
{
const CLASSNAME = __CLASS__;
/**
* @Id
* @GeneratedValue
* @Column(type="integer")
*/
public $id;
/**
* @Column(unique=true)
*/
public $name;
public function __construct($name)
{
$this->name = $name;
}
}

View File

@ -0,0 +1,68 @@
<?php
namespace Doctrine\Tests\Models\Cache;
use Doctrine\Common\Collections\ArrayCollection;
/**
* @Entity
* @Table("cache_complex_action")
*/
class ComplexAction
{
const CLASSNAME = __CLASS__;
/**
* @Column
*/
public $name;
/**
* @Id
* @OneToOne(targetEntity="Action", cascade={"persist", "remove"})
* @JoinColumn(name="action1_id", referencedColumnName="id")
*/
public $action1;
/**
* @Id
* @OneToOne(targetEntity="Action", cascade={"persist", "remove"})
* @JoinColumn(name="action2_id", referencedColumnName="id")
*/
public $action2;
/**
* @OneToMany(targetEntity="Token", cascade={"persist", "remove"}, mappedBy="complexAction")
*/
public $tokens;
public function __construct(Action $action1, Action $action2, $name)
{
$this->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;
}
}

View File

@ -0,0 +1,43 @@
<?php
namespace Doctrine\Tests\Models\Cache;
/**
* @Entity
* @Table("cache_login")
*/
class Login
{
const CLASSNAME = __CLASS__;
/**
* @Id
* @GeneratedValue
* @Column(type="integer")
*/
public $id;
/**
* @Column
*/
public $name;
/**
* @ManyToOne(targetEntity="Token", cascade={"persist", "remove"}, inversedBy="logins")
* @JoinColumn(name="token_id", referencedColumnName="token")
*/
public $token;
public function __construct($name)
{
$this->name = $name;
}
/**
* @return Token
*/
public function getToken()
{
return $this->token;
}
}

View File

@ -0,0 +1,95 @@
<?php
namespace Doctrine\Tests\Models\Cache;
use Doctrine\Common\Collections\ArrayCollection;
/**
* @Entity
* @Cache("READ_ONLY")
* @Table("cache_token")
*/
class Token
{
const CLASSNAME = __CLASS__;
/**
* @Id
* @Column(type="string")
*/
public $token;
/**
* @Column(type="date")
*/
public $expiresAt;
/**
* @OneToOne(targetEntity="Client")
*/
public $client;
/**
* @OneToMany(targetEntity="Login", cascade={"persist", "remove"}, mappedBy="token")
* @var array
*/
public $logins;
/**
* @ManyToOne(targetEntity="Action", cascade={"persist", "remove"}, inversedBy="tokens")
* @JoinColumn(name="action_id", referencedColumnName="id")
* @var array
*/
public $action;
/**
* @ManyToOne(targetEntity="ComplexAction", cascade={"persist", "remove"}, inversedBy="tokens")
* @JoinColumns({
* @JoinColumn(name="complex_action1_id", referencedColumnName="action1_id"),
* @JoinColumn(name="complex_action2_id", referencedColumnName="action2_id")
* })
* @var ComplexAction
*/
public $complexAction;
public function __construct($token, Client $client = null)
{
$this->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;
}
}

View File

@ -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());
}
}

View File

@ -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());
}
}

View File

@ -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());
}
}

View File

@ -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'])) {