How to Force Doctrine to Update Array Type Fields

How to force Doctrine to update array type fields?

Doctrine uses identical operator (===) to compare changes between old and new values. The operator used on the same object (or array of objects) with different data always return true. There is the other way to solve this issue, you can clone an object that needs to be changed.

$items = $myEntityObject->getItems();
$items[0] = clone $items[0];
$items[0]->setSomething(123);
$myEntityObject->setItems($items);

// ...

Or change the setItems() method (We need to clone only one object to persist the whole array)

public function setItems(array $items) 
{
if (!empty($items) && $items === $this->items) {
reset($items);
$items[key($items)] = clone current($items);
}
$this->items = $items;
}

Regarding the second question:

Does someone know how to preserve default tracking policy for other fields and use NotifyPropertyChanged just for the field that stores array?

You cannot set tracking policy just for a one field.

Doctrine Array type field not being updated

As I found out this can be solved (in an unelegant way in my opinion but until something better comes around I want to state this) by combining two answers from another post on SO:

https://stackoverflow.com/a/13231876/6294605

https://stackoverflow.com/a/59898632/6294605

This means combining a preflush-hook in your entity class with a "fake-change" of the array in question.

Remark that doing the fake-change in a setter/adder/remover didn't work for me as those are not being called when editing an existing entity. In this case only setters of the changed objects inside the array will be called thus making Doctrine not recognize there was a change to the array itself as no deep-check seems to be made.

Another thing that was not stated in the other thread I wanna point out:

don't forget to annotate your entity class with

@ORM\HasLifecycleCallbacks

or else your preflush-hook will not be executed.

How to update doctrine object type field

Tracking policy of Doctrine checks whether or not an object has changed, in your case - User class. However, when checking if $this->properties has changed all Doctrine does is check if it still points to the same object in memory (!).

If you want to force it to update stored object, you can either copy all it's properties to a new object instance (new UserProperties and then reassign it to $this->properties), clone it, or change Doctrine tracking policy to NOTIFY (see Doctrine docs).

Last one however, will require you to change all setters of the object and actually implement notification mechanism (as shown in Doctrine docs) so when this issue popped up in my code, I just recreated the object stored (which is my first suggestion, as it's simple).

I do think however, that this is kind of unexpected behaviour, so I'd open a ticket/issue in Doctrine issue tracker, so that at least a documentation warns about this.

Changes on ArrayCollection's items are done, but not persisted in database

FINAL UPDATE

Ok so i finally got it to work using a trick.

The main problem was the behaviour of Doctrine changes detection towards the Array type.

As quoted above, doctrine does : Array === Array to check if there is changes to be made.
Kinda weird because when you ADD or REMOVE an element from your ArrayCollection, for Doctrine the collection is still equivalent to the precedent state.
There should be a way to test equivalences between 2 objects like in Java .equals to remove those ambiguities.

Anyway what i did to solve it :

I store my collection to an initial state :

$decisionCollection = $myPresence->getDecisions();

I place my setters & modify my collection accordingly :

foreach ($myPresence->getDecisions() as $key => $decision) {
if ($decision->getTokenId() == $token) {

if ($dayPart === 'morning') {
$decision->setMorning($newValue);
} elseif ($dayPart === 'afternoon') {
$decision->setAfternoon($newValue);
}

break;
}
}

And then the Trick :

$myPresence->unsetDecisions();
$this->em->flush();

I unset completly my collection ($this->collection = NULL) & flush a first time.

It triggers the Update since Array !== NULL

And then i set my collection with the updated one that contains the setted objects :

$myPresence->setDecisions($decisionCollection);
$myPresence->setLastEditDate(new \DateTime());

And flush a last time to keep the new changes.

Thanks everyone for your time & your comments !



Related Topics



Leave a reply



Submit