Filina Consulting, Inc

Forcing Doctrine to Persist

August 12th, 2015

I stumbled upon a case where Doctrine would refuse to persist my entity due to a very special circumstance. Here's the context: I'm using a generic method in my Repository to fetch a Post. I'm selecting partial fields like this:

SELECT PARTIAL post.{id, name} ...

If the entity is found, then I proceed to delete it by setting is_deleted to true, persisting and then flushing:

$post->is_deleted = true;
$this->getEntityManager()->persist($post);
$this->getEntityManager()->flush();

It doesn't save the entity. It skips it entirely. The column is mapped though. The problem is that the UnitOfWork checks the new data against the originally loaded data, then skips the fields that were not originally loaded. This seems to be the intended behavior. This in turn causes the whole entity to be skipped. I even tried selecting the column as HIDDEN (a DQL keyword), still no luck. In contrast, if I were to update the "name" property, it would have saved, because it was loaded in the SELECT.

Thankfully, the UnitOfWork has a public method that deals with it:

$this->getEntityManager()->getUnitOfWork()->setOriginalEntityProperty(spl_object_hash($post), 'is_deleted', false);

The hash refers to the specific instance, then you provide the mapped property name and the value. The value must be different from the one that you're setting later, otherwise it will also be skipped. Call this just before setting/persisting/flushing and you're in business.

Comments

webDEVILopers August 13th, 2015 At first I thought PARTIALS could be the cause. But at the bottom line it is the way Doctrine detects changes with the UnitOfWork:
http://doctrine-orm.readthedocs.org/en/latest/reference/unitofwork.html#how-doctrine-detects-changes

The same scenario makes lifecycle callbacks like `preUpdate` fail too btw.

Interesting fact that HIDDEN didn't work either. So the column doesn't get mapped at all and is only used for the DQL to execute the query?
Anna August 13th, 2015 HIDDEN's purpose is generally to compute a value to be used as sorting. Example: ... AS score HIDDEN ... ORDER BY score. So it makes sense that it won't be in the original data.

I think the reason partials have this update issue is because they were meant for reading operations, but not for writing back. Although it would make sense to *not* load all the data if you're overriding everything anyway, like when you receive data through an API. Perhaps ->persist($entity, $force_update) or something of the sort would be useful.

Or perhaps the Doctrine team has a better idea of how to merge JSON data into an entity.
nimasdj August 21st, 2015 When data is not changed why do you need to persist it again?
Anna August 21st, 2015 Data is changed, but Doctrine does not see it as changed. It's all in the article.
Mike Simonson September 13th, 2015 When you say The value must be different from the one that you’re setting later, otherwise it will also be skipped.

Do you mean that you are setting the value of is_deleted on that entity to false then calling $post->is_deleted = true; and that the change in the property value will trigger the fact that it will be taken into account for the persist call ?
Anna September 14th, 2015 Yes. I set it to false using setOriginalEntityProperty, then set it to true normally. Note that setting it to false normally, without going through the setOriginalEntityProperty method, will cause the entity to not be saved.
Tgabi333 September 14th, 2015 If you call $post->setIsDeleted(true) would load the full entity and after that doctrine would catch the change, isn't it?
Anna September 16th, 2015 It doesn't load the full entity when you do that. In any case, when loading a partial, it makes no sense to then load the full entity. That would defy the point of loading a partial.


This page is protected by reCAPTCHA and the Google
Privacy Policy and Terms of Service apply.

Phone: +1 514-918-7866 | E-mail: me@afilina.com