Question

I'm working in a project that use Doctrine 2 in Symfony 2 and I use MEMCACHE to store doctrine's results. I have a problem with objects that are retrieved from MEMCACHE.

I found this post similar, but this approach not resolves my problem: Doctrine detaching, caching, and merging

This is the scenario

/**
 * This is in entity ContestRegistry
 * @var contest
 * 
 * @ORM\ManyToOne(targetEntity="Contest", inversedBy="usersRegistered")
 * @ORM\JoinColumn(name="contest_id", referencedColumnName="id", onDelete="CASCADE"))
 *
 */
protected $contest;

and in other entity

 /**
 * @var usersRegistered
 * 
 * @ORM\OneToMany(targetEntity="ContestRegistry", mappedBy="contest")
 *
 */
protected $usersRegistered;

Now imagine that Contest is in cache and I want to save a ContestRegistry entry. So I retrieve the object contest in cache as follows:

$contest = $cacheDriver->fetch($key);
$contest = $this->getEntityManager()->merge($contest);
return $contest;

And as last operation I do:

$contestRegistry = new ContestRegistry();
$contestRegistry->setContest($contest);
$this->entityManager->persist($contestRegistry);
$this->entityManager->flush();

My problem is that doctrine saves the new entity correctly, but also it makes an update on the entity Contest and it updates the column updated. The real problem is that it makes an update query for every entry, I just want to add a reference to the entity. How I can make it possible? Any help would be appreciated.

Was it helpful?

Solution

Why

When an entity is merged back into the EntityManager, it will be marked as dirty. This means that when a flush is performed, the entity will be updated in the database. This seems reasonable to me, because when you make an entity managed, you actually want the EntityManager to manage it ;)

In your case you only need the entity for an association with another entity, so you don't really need it to be managed. I therefor suggest a different approach.

Use a reference

So don't merge $contest back into the EntityManager, but grab a reference to it:

$contest    = $cacheDriver->fetch($key);
$contestRef = $em->getReference('Contest', $contest->getId());

$contestRegistry = new ContestRegistry();
$contestRegistry->setContest($contestRef);

$em->persist($contestRegistry);
$em->flush();

That reference will be a Proxy (unless it's already managed), and won't be loaded from the db at all (not even when flushing the EntityManager).

Result Cache

In stead of using you own caching mechanisms, you could use Doctrine's result cache. It caches the query results in order to prevent a trip to the database, but (if I'm not mistaken) still hydrates those results. This prevents a lot of issues that you can get with caching entities themselves.

OTHER TIPS

What you want to achieve is called partial update. You should use something like this instead

/**
 * Partially updates an entity
 *
 * @param Object $entity The entity to update
 * @param Request $request
 */
protected function partialUpdate($entity, $request)
{
    $parameters = $request->request->all();

    $accessor = PropertyAccess::createPropertyAccessor();
    foreach ($parameters as $key => $parameter) {
        $accessor->setValue($entity, $key, $parameter);
    }
}

Merge requires the whole entity to be 100% fullfilled with data. I haven't checked the behavior with children (many to one, one to one, and so on) relations yet.

Partial update is usually used on PATCH (or PUT) on a Rest API.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top