2

我在我的 Symfony2 项目中有一对一的关系,其中Questiona 引用了 a Video- 两者都有一个创建更新的 Gedmo\Timestampable行为,它基本上按预期工作。但是有点过分了:

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

这是我的代码(简化):

课堂问题:

<?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 的字段参数,以仅在其中一个字段获得时强制更新更新,但这似乎被完全忽略了。

这也是反序列化的 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();
4

1 回答 1

1

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

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

这是发生的事情:

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

因此,您将有 2 个选择:

  1. find()首先是对象,然后根据 JSON 进行更改。
  2. 确保 JSON 包含所有映射的属性(包括createdupdated)。

另外,我注意到setter="setCreatedEmpty"setter="setUpdatedEmpty"。我不确定这些方法的作用(因为您没有向我们展示),但方法名称表示其他只是分配值的东西。

回复您的评论

merge()将一个对象 -ing 到 ObjectManager 中时,它被标记为“脏”,这将触发更改集的计算。并且由于 DateTime 对象的引用发生了变化(Doctrine 从数据库中获取的实例总是与反序列化 JSON 所创建的实例不同),因此对象将被更新。然后 Gedmo\Timestampable 将启动并$updated相应地更改属性。

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

于 2014-12-10T13:39:22.327 回答