我有一个实体用户,它实现了AdvancedUserInterface。该实体具有UserProperties类的字段属性,它被映射为对象类型。
我的实体:
class User implements AdvancedUserInterface, \Serializable {
<....>
/**
* @var UserProperties
*
* @ORM\Column(name="properties", type="object", nullable=true, options={"default":null})
*/
private $properties;
<....>
}
属性类:
class UserProperties {
public $isEmailVisible = false;
public $isNameVisible = false;
}
如果数据库中的属性值为 null ,并且我对设置属性的实体对象进行了一些更改 - 它工作正常。但是,如果我对数据库进行了一些更改并且属性字段为非null(已存在序列化的UserProperties对象) - 则不会保存更改(但是用户实体上的所有其他更改都是)。
我做错了什么?
答案 0 :(得分:6)
Doctrine的跟踪策略检查对象是否已更改,在您的情况下为User
类。但是,在检查$this->properties
是否已更改时,所有Doctrine都会检查它是否仍指向内存中的同一对象(!)。
如果要强制它更新存储对象,可以将其所有属性复制到新对象实例(new UserProperties
,然后将其重新分配给$this->properties
),克隆它,或将学说跟踪政策更改为NOTIFY
(请参阅http://doctrine-orm.readthedocs.org/projects/doctrine-orm/en/latest/reference/change-tracking-policies.html)。
但是,最后一个会要求你更改对象的所有setter并实际实现通知机制(如Doctrine docs所示),所以当我的代码中弹出这个问题时,我只是重新创建了存储的对象(这是我的第一个)建议,因为它很简单)。
但我确实认为这是一种意想不到的行为,所以我在Doctrine问题跟踪器中打开了一个票据/问题,所以至少有一个文档会对此发出警告。
答案 1 :(得分:6)
问题在于Doctrine不检查序列化对象的值是否已更改,它只检查对象是否相同(相同的内存指针)。
如果您在Doctrine中使用对象序列化器,则需要始终创建对象的新实例以进行保存。
即
$user = $form->getData();
$properties = new UserProperties();
$properties->setProperty1($oldValue1);
$properties->setProperty2($newValue2);
...
$user->setProperties($properties);
$em->persist($properties);
$em->flush();
当然,您应该能够创建对象的副本并仅分配更改的值。
$properties = clone $user->getProperties();
$properties->setProperty1($newValue);
$em->persist($properties);
$em->flush();