How to force Doctrine to update array type fields? How to force Doctrine to update array type fields? symfony symfony

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.


The way I fixed this on my code was to use createQueryBuilder and just create the update query. This way doctrine has no way of saying no :)

So I went from this

$em          = $this->getDoctrine()->getManager();$matchEntity = $em->getReference('MyBundleBundle:Match', $match_id);$matchEntity->setElement($element);$matchEntity->setTeamHomeColour($data['team_a_colour']);$matchEntity->setTeamAwayColour($data['team_b_colour']);

To this:

$repository = $this->getDoctrine()->getRepository('MyBundleBundle:Match');$query      = $repository->createQueryBuilder('u')    ->update()    ->set('u.element', ':element')    ->set('u.teamHomeColour', ':thomecolour')    ->set('u.teamAwayColour', ':tawaycolour')    ->where('u.matchId = :match')    ->setParameter('element', $element)    ->setParameter('thomecolour', $data['team_a_colour'])    ->setParameter('tawaycolour', $data['team_b_colour'])    ->setParameter('match', $matchEntity)    ->getQuery();$query->execute();

Its a few more lines of code but there is no cloning or any other sort of magic. Just tell doctrine directly to do a damn update! Hope this helps.

Note: In my situation it was the $element that wasn't being set. I unset all matches in a previous query and doctrine just didn't see it and so refused to update the element.


I know this is a very old question, but still relevant, and I wanted to add something more to the answer of @VadimAshikhman (you saved me hours of work, by the way...).

As I didn't want to do specific stuff in the controller, I added a Doctrine PreFlush callback on my Entity, where I clone the array/object for which changes are not detected:

/** * @ORM\Table() */class MyEntity{    (...)    /**     * @var array $items     *      * @ORM\Column( type="array" )      */    private $items;    (...)    /**     * Always clone the array/object before flushing,     * because changes are not detected inside the array/object.     * It ensures changes are always persisted to the db.     *      * @ORM\PreFlush()     */    public function cloneOnPreFlush() {        $clone = clone $this->items;        $this->items = $clone;    }}

In your Controller, a normal $em->flush() will trigger the PreFlush callback. The only minor drawback is that your Entity will always be updated in the database on a flush, even if there is no change to it.