doctrine-entity-merger
When you use PARTIAL in DQL, you retrive only fields you need, instead of all Entity fields.
But, if you execute 2 PARTIAL on same entity, but not same fields, you will have this problem :
class FooEntity
{
protected $id;
protected $name;
protected $description;
}
$foo1 = $repository
->createQueryBuilder('foo')
->select('PARTIAL foo.{id, name}')
->where('site.id = 1')
->getQuery()
->getSingleResult();
var_dump($foo1->getDescription()); // null, that's fine, description is not in PARTIAL
$foo2 = $repository
->createQueryBuilder('foo')
->select('PARTIAL foo.{id, name, description}')
->where('site.id = 1')
->getQuery()
->getSingleResult();
// $foo1 is same object as $foo2, cause Doctrine know first query hydrated $foo1
// so, when you ask same entity (same id in query) with 2nd query, Doctrine will execute SQL,
// but will not hydrate a new entity
// UnitOfWork will return instance of Foo who is already hydrated, with first query
var_dump(spl_object_hash($foo1) === spl_object_hash($foo2)); // true
// but, as Doctrine return $foo1 in 2nd query, your new field description will not be defined in $foo1
var_dump($foo1->getDescription()); // null, but we want it, cause it's defined in PARTIAL 2nd query
You can use steevanb\DoctrineEntityMerger\QueryHint::MERGE_ENTITY to define description in $foo1 :
use steevanb\DoctrineEntityMerger\QueryHint;
$foo1 = $repository
->createQueryBuilder('foo')
->select('PARTIAL foo.{id, name}')
->where('site.id = 1')
->getQuery()
->getSingleResult();
var_dump($foo1->getName()); // 'My name' for example
var_dump($foo1->getDescription()); // null, that's fine, description is not in PARTIAL
$foo1->setName('New name');
var_dump($foo1->getName()); // 'New name'
$foo2 = $repository
->createQueryBuilder('foo')
->select('PARTIAL foo.{id, description}')
->where('site.id = 1')
->getQuery()
->setHint(QueryHint::MERGE_ENTITY, true)
->getSingleResult();
var_dump($foo1->getName()); // 'New name', MERGE_ENTITY will not change Foo::$name value if it was already defined in another query before
var_dump($foo1->getDescription()); // 'My description'
Installation
As doctrine-entity-merger use steevanb/doctrine-events, see how to install it (composer dependecy is added here, you don't need to add it for steevanb/doctrine-events) :
Add it to your composer.json :
{
"require": {
"steevanb/doctrine-entity-merger": "^1.0.5",
}
}
Add EntityMergerSubscriber :
$entityManager->getEventManager()->addEventSubscriber(
new steevanb\DoctrineEntityMerger\EventSubscriber\EntityMergerSubscriber()
);
If you want to add MERGE_ENTITY hint to all of your queries, you can do this :
$entityManager->getConfiguration()->setDefaultQueryHint(
steevanb\DoctrineEntityMerger\QueryHint\QueryHint::MERGE_ENTITY,
true
);
For example, if you are on a Symfony project, you can add it in AppKernel :
# app/AppKernel.php
use Doctrine\ORM\EntityManagerInterface;
use steevanb\DoctrineEntityMerger\QueryHint;
use steevanb\DoctrineEntityMerger\EventSubscriber\EntityMergerSubscriber;
class AppKernel
{
public function boot()
{
parent::boot();
foreach ($this->getContainer()->get('doctrine')->getManagers() as $manager) {
if ($manager instanceof EntityManagerInterface) {
// add hint MERGE_ENTITY to all your queries
$manager->getConfiguration()->setDefaultQueryHint(QueryHint::MERGE_ENTITY, true);
// add listener, who use steevanb/doctrine-events to change UnitOfWork::createEntity()
// to take into account MERGE_ENTITY hint
$manager->getEventManager()->addEventSubscriber(new EntityMergerSubscriber());
}
}
}
}