使用JMS Serializer进行反序列化时,Gedmo Timestampable始终更新引用

时间:2014-12-03 20:56:30

标签: mongodb symfony doctrine-orm jmsserializerbundle jms-serializer

我的Symfony2项目中有一对一的关系,Question引用了Video - 两者都有已创建已更新 Gedmo \ Timestampable 行为,基本上按预期工作。但有点太多了:

使用附加的Question反序列化Video时(仅作为ID以避免视频元数据中的其他更改)Video文档始终会在created updated字段中获得更新。这似乎不对。我可能理解为什么更新的字段会有一个新的日期 - 即使实际上对象本身没有任何变化,但为什么创建了

这是我的代码(简称):

课堂提问:

<?php

/**
 * Class Question
 *
 * @Serializer\AccessorOrder("alphabetical")
 * @MongoDB\Document(
 *   collection="Quiz",
 *   repositoryClass="MyNamespace\Bundle\QuizBundle\Repository\QuestionRepository",
 * )
 * @package MyNamespace\Bundle\QuizBundle\Document
 */
class Question
{
    /**
     * @var \MongoId
     * @MongoDB\Id(strategy="auto")
     * @Serializer\Type("string")
     * @Serializer\Groups({
     *   "quiz_admin_list",
     *   "quiz_admin_detail"
     * })
     */
    protected $id;

    /**
     * @var \DateTime
     *
     * @Assert\Date(
     *   message = "quiz:constraints.model.question.created.invalid"
     * )
     * @Serializer\Type("DateTime<'U'>")
     * @Serializer\Accessor(getter="getCreated", setter="setCreatedEmpty")
     * @Serializer\Groups({
     *   "quiz_admin_list",
     *   "quiz_admin_detail"
     * })
     * @Gedmo\Timestampable(on="create")
     * @MongoDB\Date
     */
    protected $created;


    /**
     * @var \DateTime
     *
     * @Assert\Date(
     *   message = "quiz:constraints.model.question.updated.invalid"
     * )
     * @Serializer\Type("DateTime<'U'>")
     * @Serializer\Accessor(getter="getUpdated", setter="setUpdatedEmpty")
     * @Serializer\Groups({
     *   "quiz_admin_list",
     *   "quiz_admin_detail"
     * })
     * @Gedmo\Timestampable(on="update")
     * @MongoDB\Date
     */
    protected $updated;

    /**
     * @var Video
     *
     * @Serializer\Type("MyNamespace\Bundle\CoreMediaAdminBundle\Document\Video")
     * @Serializer\Groups({
     *   "quiz_admin_list",
     *   "quiz_admin_detail"
     * })
     * @MongoDB\ReferenceOne(
     *   targetDocument="MyNamespace\Bundle\CoreMediaAdminBundle\Document\Video",
     *   cascade={"all"}
     * )
     */
    protected $answerVideo;

}

课堂视频:

<?php

/**
 * Class Video
 * @Serializer\AccessorOrder("alphabetical")
 * @MongoDB\Document(
 *   collection="CoreMediaAdminVideo",
 *   repositoryClass="MyNamespace\Bundle\CoreMediaAdminBundle\Repository\VideoRepository",
 * )
 * @Vich\Uploadable
 * @package MyNamespace\Bundle\CoreMediaAdminBundle\Document
 */
class Video 
{

    /**
     * @MongoDB\Id(strategy="auto")
     * @Serializer\Type("string")
     * @Serializer\Groups({
     *   "core_media_list",
     *   "core_media_search",
     *   "core_media_video_list",
     *   "core_media_video_detail"
     * })
     */
    protected $id;

    /**
     * @Vich\UploadableField(
     *   mapping = "core_media_admin_video",
     *   fileNameProperty = "fileName"
     * )
     * @Serializer\Exclude
     * @var File $file
     */
    protected $file;

    /**
     * @MongoDB\Field(type="string")
     * @Serializer\Type("string")
     * @Serializer\Groups({
     *   "core_media_list",
     *   "core_media_search",
     *   "core_media_video_list",
     *   "core_media_video_detail"
     * })
     */
    protected $mimeType;

    /**
     * @var String
     *
     * @Assert\NotBlank(
     *   message = "core.media.admin:constraints.model.base.title.not_blank"
     * )
     * @Serializer\Type("string")
     * @Serializer\Groups({
     *   "core_media_list",
     *   "core_media_search",
     *   "core_media_video_list",
     *   "core_media_video_detail"
     * })
     * @MongoDB\Field(type="string")
     */
    protected $title;

    /**
     * @var \DateTime
     *
     * @Assert\Date(
     *   message = "core.media.admin:constraints.model.base.date.invalid"
     * )
     * @Serializer\Type("DateTime<'U'>")
     * @Serializer\Accessor(getter="getCreated", setter="setCreatedEmpty")
     * @Serializer\Groups({
     *   "core_media_list",
     *   "core_media_search",
     *   "core_media_video_list",
     *   "core_media_video_detail"
     * })
     * @Gedmo\Timestampable(on="create")
     * @MongoDB\Date
     */
    protected $created;

    /**
     * @var \DateTime
     *
     * @Assert\Date(
     *   message = "core.media.admin:constraints.model.base.date.invalid"
     * )
     * @Serializer\Type("DateTime<'U'>")
     * _Serializer\Accessor(getter="getUpdated", setter="setUpdatedEmpty")
     * @Serializer\Groups({
     *   "core_media_list",
     *   "core_media_search",
     *   "core_media_video_list",
     *   "core_media_video_detail"
     * })
     * @Gedmo\Timestampable(on="update")
     * @MongoDB\Date
     */
    protected $updated;

    /**
     * @var \DateTime
     *
     * @Assert\Date(
     *   message = "core.media.admin:constraints.model.base.date.invalid"
     * )
     * @Serializer\Type("DateTime<'U'>")
     * @Serializer\Groups({
     *   "core_media_list",
     *   "core_media_search",
     *   "core_media_video_list",
     *   "core_media_video_detail"
     * })
     * @Gedmo\Timestampable(on="update", field={"title", "tags", "comment", "dataOrigin", "description", "videoMetaData", "mimeType", "fileName", "file" })
     * @MongoDB\Date
     */
    protected $updatedContent;

}

有趣的是,在反序列化期间没有对Video个对象进行任何更改 - 只有 更新查询来设置createdupdated字段视频..我还测试了Timestampable的 field 参数,只有当其中一个字段得到更新时强制更新,但这似乎完全被忽略。

这里还有反序列化的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();

1 个答案:

答案 0 :(得分:1)

Gedmo \ Timestampable将为$created$updated设置(新)值,因为在刷新ObjectManager时该数据不存在。

虽然类Video中的注释定义了在序列化此类对象时应包含$created$updated,但您显示的JSON不包含这些键/属性。

以下是发生的事情:

  • JSON不包含键/属性createdupdated
  • 反序列化时,生成的对象的null$created值为$updated
  • merge()将对象放入ObjectManager时,它没有任何反应。该对象简单地变为&#34;托管&#34;,这意味着在刷新期间,ObjectManager将为其计算更改集并在必要时更新它。
  • 当刷新ObjectManager时,Gedmo \ Timestampable将启动(由于PreUpdate事件监听器)。它会看到$created$updated包含null值,因此会分配新值。
  • 然后ObjectManager将检索&#34;当前&#34;来自数据库的数据,因为它需要它来计算变更集。通常,当find()对象时,它不会这样做,因为在find()期间已经检索到了数据。
  • 由于$created$updated的值现在与从数据库中检索到的值不同,因此会更新它们。

所以你有两个选择:

    首先
  1. find()对象,然后根据JSON更改它。
  2. 确保JSON包含已映射的所有属性(包括createdupdated)。
  3. 另外,我注意到setter="setCreatedEmpty"setter="setUpdatedEmpty"。我不确定这些方法是做什么的(因为你没有告诉我们),但方法名称表示其他只是分配值的东西。

    回答您的意见

    merge()将对象放入ObjectManager时,它被标记为&#34;脏&#34;,这将触发变更集的计算。并且因为DateTime对象的引用已更改(Doctrine从db获取的实例始终与通过反序列化JSON创建的实例不同),所以将更新该对象。然后Gedmo \ Timestampable将启动并相应地更改$updated属性。

    如果您不希望这种情况发生,那么您需要find()当前对象,并且更改值对象,当它们代表的值实际更改时。标量值没有问题:您可以设置相同的值,并且Doctrine不会将其视为更改。但是对于值对象(如DateTime)对象,Doctrine会在引用更改时(当设置了不同的实例时)看到更改。