我在我的Symfony2项目中有一个一对一的关系,其中Question
引用了一个Video
- 两者都有一个创建和更新的Gedmo\Timestampable行为,基本上按预期工作。但有点太多了:
使用附加的Video
反序列化Question
时(仅作为 ID 以避免视频元数据中的其他更改(,Video
文档始终在created
和updated
字段上获得更新。这似乎不对。我可能理解为什么更新的字段会获得新日期 - 即使实际上对象本身没有任何变化,但为什么要创建?
这是我的代码(简单(:
课堂问题:
<?php
/**
* Class Question
*
* @SerializerAccessorOrder("alphabetical")
* @MongoDBDocument(
* collection="Quiz",
* repositoryClass="MyNamespaceBundleQuizBundleRepositoryQuestionRepository",
* )
* @package MyNamespaceBundleQuizBundleDocument
*/
class Question
{
/**
* @var MongoId
* @MongoDBId(strategy="auto")
* @SerializerType("string")
* @SerializerGroups({
* "quiz_admin_list",
* "quiz_admin_detail"
* })
*/
protected $id;
/**
* @var DateTime
*
* @AssertDate(
* message = "quiz:constraints.model.question.created.invalid"
* )
* @SerializerType("DateTime<'U'>")
* @SerializerAccessor(getter="getCreated", setter="setCreatedEmpty")
* @SerializerGroups({
* "quiz_admin_list",
* "quiz_admin_detail"
* })
* @GedmoTimestampable(on="create")
* @MongoDBDate
*/
protected $created;
/**
* @var DateTime
*
* @AssertDate(
* message = "quiz:constraints.model.question.updated.invalid"
* )
* @SerializerType("DateTime<'U'>")
* @SerializerAccessor(getter="getUpdated", setter="setUpdatedEmpty")
* @SerializerGroups({
* "quiz_admin_list",
* "quiz_admin_detail"
* })
* @GedmoTimestampable(on="update")
* @MongoDBDate
*/
protected $updated;
/**
* @var Video
*
* @SerializerType("MyNamespaceBundleCoreMediaAdminBundleDocumentVideo")
* @SerializerGroups({
* "quiz_admin_list",
* "quiz_admin_detail"
* })
* @MongoDBReferenceOne(
* targetDocument="MyNamespaceBundleCoreMediaAdminBundleDocumentVideo",
* cascade={"all"}
* )
*/
protected $answerVideo;
}
课堂视频:
<?php
/**
* Class Video
* @SerializerAccessorOrder("alphabetical")
* @MongoDBDocument(
* collection="CoreMediaAdminVideo",
* repositoryClass="MyNamespaceBundleCoreMediaAdminBundleRepositoryVideoRepository",
* )
* @VichUploadable
* @package MyNamespaceBundleCoreMediaAdminBundleDocument
*/
class Video
{
/**
* @MongoDBId(strategy="auto")
* @SerializerType("string")
* @SerializerGroups({
* "core_media_list",
* "core_media_search",
* "core_media_video_list",
* "core_media_video_detail"
* })
*/
protected $id;
/**
* @VichUploadableField(
* mapping = "core_media_admin_video",
* fileNameProperty = "fileName"
* )
* @SerializerExclude
* @var File $file
*/
protected $file;
/**
* @MongoDBField(type="string")
* @SerializerType("string")
* @SerializerGroups({
* "core_media_list",
* "core_media_search",
* "core_media_video_list",
* "core_media_video_detail"
* })
*/
protected $mimeType;
/**
* @var String
*
* @AssertNotBlank(
* message = "core.media.admin:constraints.model.base.title.not_blank"
* )
* @SerializerType("string")
* @SerializerGroups({
* "core_media_list",
* "core_media_search",
* "core_media_video_list",
* "core_media_video_detail"
* })
* @MongoDBField(type="string")
*/
protected $title;
/**
* @var DateTime
*
* @AssertDate(
* message = "core.media.admin:constraints.model.base.date.invalid"
* )
* @SerializerType("DateTime<'U'>")
* @SerializerAccessor(getter="getCreated", setter="setCreatedEmpty")
* @SerializerGroups({
* "core_media_list",
* "core_media_search",
* "core_media_video_list",
* "core_media_video_detail"
* })
* @GedmoTimestampable(on="create")
* @MongoDBDate
*/
protected $created;
/**
* @var DateTime
*
* @AssertDate(
* message = "core.media.admin:constraints.model.base.date.invalid"
* )
* @SerializerType("DateTime<'U'>")
* _SerializerAccessor(getter="getUpdated", setter="setUpdatedEmpty")
* @SerializerGroups({
* "core_media_list",
* "core_media_search",
* "core_media_video_list",
* "core_media_video_detail"
* })
* @GedmoTimestampable(on="update")
* @MongoDBDate
*/
protected $updated;
/**
* @var DateTime
*
* @AssertDate(
* message = "core.media.admin:constraints.model.base.date.invalid"
* )
* @SerializerType("DateTime<'U'>")
* @SerializerGroups({
* "core_media_list",
* "core_media_search",
* "core_media_video_list",
* "core_media_video_detail"
* })
* @GedmoTimestampable(on="update", field={"title", "tags", "comment", "dataOrigin", "description", "videoMetaData", "mimeType", "fileName", "file" })
* @MongoDBDate
*/
protected $updatedContent;
}
有趣的是,在反序列化期间没有对Video
对象进行任何更改 - 只有更新查询来设置视频的created
和updated
字段。我还测试了 Timestampable 的字段参数,以便仅在其中一个字段获得更新时才强制更新,但这似乎被完全忽略了。
这里也是反序列化的JSON和相应的MongoDB查询:
{
"id": "547f31e650e56f2c26000063",
"question_id": 12,
"question_text": "Wer einen Gemüsegarten hat, sollte wissen, dass Schnecken…?",
"answer_text": "test",
"answer_video": {
"id": "547f31d850e56f2c26000031"
},
"tags": [
"Schnecken",
"Basilikum",
"Thymian",
"Garten"
]
}
查询:
db.QuizQuestion.find({
"_id": ObjectId("547f31e650e56f2c26000063")
}).limit(1).limit();
db.CoreMediaAdminVideo.update({
"_id": ObjectId("547f31d850e56f2c26000031")
},
{
"$set": {
"created": newISODate("2014-12-03T21:30:02+01:00"),
"updated": newISODate("2014-12-03T21:30:02+01:00"),
"updatedContent": newISODate("2014-12-03T21:30:02+01:00")
}
});
db.ARDBuffetQuizQuestion.update({
"_id": ObjectId("547f31e650e56f2c26000063")
},
{
"$set": {
"created": newISODate("2014-12-03T21:30:02+01:00"),
"updated": newISODate("2014-12-03T21:30:02+01:00"),
"questionText": "Wer einen Gemüsegarten hat, sollte wissen, dass Schnecken…?",
"answerText": "test",
"answerVideo": {
"$ref": "CoreMediaAdminVideo",
"$id": ObjectId("547f31d850e56f2c26000031"),
"$db": "my-database"
}
}
});
db.ARDBuffetQuizQuestion.update({
"_id": ObjectId("547f31e650e56f2c26000063")
},
{
"$set": {
"tags": [
{
"value": "Schnecken",
"normalized": "schnecken"
},
{
"value": "Basilikum",
"normalized": "basilikum"
},
{
"value": "Thymian",
"normalized": "thymian"
},
{
"value": "Garten",
"normalized": "garten"
}
]
}
});
db.ARDBuffetQuizQuestion.find({
"_id": ObjectId("547f31e650e56f2c26000063")
}).limit(1).limit();
db.CoreMediaAdminVideo.find({
"_id": ObjectId("547f31d850e56f2c26000031")
}).limit(1).limit();
Gedmo\Timestampable 将为 $created
和 $updated
设置(新(值,因为在刷新 ObjectManager 时不存在该数据。
尽管类 Video
中的注释定义了序列化此类对象时应包含$created
和$updated
,但显示的 JSON 不包含这些键/属性。
以下是发生的情况:
- JSON 不包含键/属性
created
和updated
。 - 反序列化时,生成的对象将具有
$created
和$updated
的null
值。 - 将对象
merge()
到对象管理器中时,它没有任何反应。对象只是变为"托管",这意味着在刷新期间,ObjectManager 将为其计算更改集并在必要时对其进行更新。 - 刷新 ObjectManager 时,Gedmo\Timestampable 将启动(由于事件侦听器
PreUpdate
(。它将看到$created
和$updated
包含null
值,因此它将分配新值。 - 然后,ObjectManager 将从数据库中检索"当前"数据,因为它需要它来计算更改集。通常,当
find()
对象时,它不会这样做,因为数据已经在该find()
期间检索。 - 由于
$created
和$updated
的值现在与从数据库中检索的值不同,因此它将更新它们。
因此,您将有 2 个选项:
- 首先
find()
对象,然后根据 JSON 进行更改。 - 确保 JSON 包含映射的所有属性(包括
created
和updated
(。
另外,我注意到setter="setCreatedEmpty"
和setter="setUpdatedEmpty"
.我不确定这些方法的作用(因为您没有向我们展示(,但方法名称表示其他简单地分配值的东西。
回答您的意见
当将对象merge()
到对象管理器中时,它被标记为"脏",这将触发更改集的计算。并且由于 DateTime 对象的引用已更改(Doctrine 从数据库获取的实例始终与通过反序列化 JSON 创建的实例不同(,因此对象将被更新。然后 Gedmo\Timestampable 将启动并相应地更改$updated
属性。
如果不希望发生这种情况,则需要find()
当前对象,并且仅在值对象表示的值实际更改时才更改值对象。标量值没有问题:您可以设置相同的值,而 Doctrine 不会将其视为更改。但是对于值对象(如日期时间(对象,当引用发生更改(设置不同的实例时(,Doctrine 将看到更改。