我有一个带有数组类型字段的教义实体:
/**
* @ORM'Table()
*/
class MyEntity
{
(...)
/**
* @var array $items
*
* @ORM'Column( type="array" )
*/
private $items;
/**
* @param SomeItem $item
*/
public function addItem(SomeItem $item)
{
$this->items[] = $item;
}
(...)
}
如果我将元素添加到数组中,则此代码可以正常工作:
$myEntityObject->addItems(new SomeItem());
$EntityManager->persist($myEntityObject);
$EntityManager->flush();
$myEntityObject
与正确的数据一起保存到数据库中(数组被序列化,并在查询数据库时反序列化)。
不幸的是,当我在不更改该数组大小的情况下更改数组中的一个对象时,如果我尝试将更改保存到数据库,Doctrine 将不执行任何操作。
$items = $myEntityObject->getItems();
$items[0]->setSomething(123);
$myEntityObject->setItems($items);
$EntityManager->persist($myEntityObject);
$EntityManager->flush();
print_r($myEntityObject);
尽管print_r
代码的最后一行显示更改的对象的数据,但如果数组大小没有更改,Doctrine 不知道数组内部是否发生了更改。有没有办法迫使教义保存该领域所做的更改(或温和地告知它该领域需要保存的更改)?
刚刚在文档中找到了解决我问题的方法:
http://docs.doctrine-project.org/en/latest/reference/change-tracking-policies.html
它需要对代码进行大量更改,但它可以工作。是否有人知道如何保留其他字段的默认跟踪策略,并仅对存储数组的字段使用 NotifyPropertyChanged?
运算符(===)来比较旧值和新值之间的变化。在具有不同数据的同一对象(或对象数组)上使用的运算符始终返回 true。还有另一种方法可以解决此问题,您可以克隆需要更改的对象。
$items = $myEntityObject->getItems();
$items[0] = clone $items[0];
$items[0]->setSomething(123);
$myEntityObject->setItems($items);
// ...
或者更改setItems()
方法(我们只需要克隆一个对象来持久化整个数组)
public function setItems(array $items)
{
if (!empty($items) && $items === $this->items) {
reset($items);
$items[key($items)] = clone current($items);
}
$this->items = $items;
}
关于第二个问题:
是否有人知道如何保留其他字段的默认跟踪策略,并仅对存储数组的字段使用 NotifyPropertyChanged?
不能只为一个字段设置跟踪策略。
我在代码上解决此问题的方法是使用 createQueryBuilder 并创建更新查询。这样,教义就没有办法说不:)
所以我从这里出发
$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']);
对此:
$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();
它还有几行代码,但没有克隆或任何其他类型的魔法。直接告诉教义做该死的更新!希望这有帮助。
注意:在我的情况下,未设置的是$element。我在之前的查询中取消设置了所有匹配项,而学说只是没有看到它,因此拒绝更新元素。
这是一个非常古老的问题,但仍然相关,我想在@VadimAshikhman的答案中添加更多内容(顺便说一下,您为我节省了数小时的工作时间......
由于我不想在控制器中执行特定操作,因此我在实体上添加了一个 Doctrine PreFlush 回调,在该回调中克隆未检测到更改的数组/对象:
/**
* @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;
}
}
在控制器中,正常$em->flush()
将触发 PreFlush 回调。唯一的小缺点是,即使实体没有更改,您的实体也将始终在刷新时在数据库中更新。