Add hint MERGE_ENTITY to merge fields retrieved by many queries
Switch branches/tags
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
EventSubscriber
.gitignore
QueryHint.php
README.md
changelog.md
composer.json

README.md

version doctrine php Lines Total Downloads SensionLabsInsight Scrutinizer

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'

Changelog

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) :

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