如何强制原则更新数组类型字段?

时间:2021-08-13 06:48:01

I have a Doctrine entity with array type field:

我有一个带有数组类型字段的原则实体:

/**
 * @ORM\Table()
 */
class MyEntity
{
    (...)

    /**
     * @var array $items
     * 
     * @ORM\Column( type="array" ) 
     */
    private $items;

    /**
     * @param SomeItem $item 
     */
    public function addItem(SomeItem $item)
    {
        $this->items[] = $item;
    }

    (...)
}

If I add element to the array, this code works properly:

如果我向数组中添加元素,此代码可以正常工作:

$myEntityObject->addItems(new SomeItem()); 
$EntityManager->persist($myEntityObject);
$EntityManager->flush();

$myEntityObject is saved to the database with correct data (array is serialized, and deserialized when querying database).

$myEntityObject使用正确的数据保存到数据库中(数组序列化,查询数据库时反序列化)。

Unfortunately, when I change one of the object inside array without changing size of that array, Doctrine does nothing if I'm trying to save changes to the database.

不幸的是,当我在数组中更改一个对象而不更改该数组的大小时,如果我试图保存对数据库的更改,Doctrine将不起作用。

$items = $myEntityObject->getItems();
$items[0]->setSomething(123);
$myEntityObject->setItems($items);
$EntityManager->persist($myEntityObject);
$EntityManager->flush();
print_r($myEntityObject);

Although, print_r in the last line of that code displays changed object's data, Doctrine doesn't know that something was changed inside the array if array size didn't changed. Is there any way to force Doctrine to save changes made in that field (or gently inform it about changes in that field that needs to be saved) ?

尽管在代码的最后一行print_r显示了修改后的对象的数据,但是Doctrine不知道如果数组大小没有更改的话,数组内部是否发生了更改。有没有办法强迫教条去保存在那个领域中所做的改变(或者委婉地告诉它那个领域中需要保存的改变)?


Just found in documentation a way to solve my issue:

刚在文件中找到一种解决我问题的方法:

http://docs.doctrine-project.org/en/latest/reference/change-tracking-policies.html

http://docs.doctrine-project.org/en/latest/reference/change-tracking-policies.html

It requires a lot of changes in the code, but it works. Does someone know how to preserve default tracking policy for other fields and use NotifyPropertyChanged just for the field that stores array?

它需要在代码中做很多修改,但是它可以工作。是否有人知道如何保存其他字段的默认跟踪策略,并且只对存储数组的字段使用NotifyPropertyChanged ?

2 个解决方案

#1


21  

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.

Doctrine使用相同的操作符(==)来比较新旧值之间的变化。在具有不同数据的同一对象(或对象数组)上使用的操作符总是返回true。还有另一种方法可以解决这个问题,您可以克隆一个需要修改的对象。

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

或者更改setItems()方法(我们只需要克隆一个对象就可以持久化整个数组)

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?

是否有人知道如何保存其他字段的默认跟踪策略,并且只对存储数组的字段使用NotifyPropertyChanged ?

You cannot set tracking policy just for a one field.

不能仅为一个字段设置跟踪策略。

#2


1  

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

我修改代码的方法是使用createQueryBuilder并创建更新查询。这样的教义是不能说不的

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.

注意:在我的情况下,没有设置的是$元素,我在之前的查询中取消了所有匹配项,而doctrine没有看到它,因此拒绝更新该元素。

#1


21  

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.

Doctrine使用相同的操作符(==)来比较新旧值之间的变化。在具有不同数据的同一对象(或对象数组)上使用的操作符总是返回true。还有另一种方法可以解决这个问题,您可以克隆一个需要修改的对象。

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

或者更改setItems()方法(我们只需要克隆一个对象就可以持久化整个数组)

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?

是否有人知道如何保存其他字段的默认跟踪策略,并且只对存储数组的字段使用NotifyPropertyChanged ?

You cannot set tracking policy just for a one field.

不能仅为一个字段设置跟踪策略。

#2


1  

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

我修改代码的方法是使用createQueryBuilder并创建更新查询。这样的教义是不能说不的

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.

注意:在我的情况下,没有设置的是$元素,我在之前的查询中取消了所有匹配项,而doctrine没有看到它,因此拒绝更新该元素。