【问题标题】:Incorrect Doctrine2 hydration when received data is correct接收到的数据正确时不正确的 Doctrine2 水合
【发布时间】:2016-08-17 09:40:50
【问题描述】:

编辑 2018-05-22: 无法回答完全修复的问题 - 由于不再具有访问权限,因此无法再复制问题。不删除基于this meta discussion

请不要花费时间/精力来创建答案

@Wilt 的回答中的讨论让我了解了我现在对使用鉴别器的了解,它可能对未来的提问者有所帮助。就我而言,它有所帮助,但没有提供答案。


我遇到了一个复杂的问题,即从客户端接收到的数据的水合不正确。我一直在尝试解决这个问题近一个星期,所以我想问问你们。

我们有这个应用程序允许为学生创建作业。作业可能包含问题、文本项目、媒体项目等。问题在于问题和相关的答案。

场景

作业

  • 问题表 1 (L1)
    • 问题 1(L1 - V1)
      • 答案 A(L1 - V1 - A1)
      • 答案 B(L1 - V1 - A2)
    • 问题 2(L1 - V2)
      • 答案 A (L1 - V2 - A1)(只有 1 个答案)
  • 问题表 2 (L2)
    • 问题 1(……等等)
      • 回答A
      • 答案 B
    • 问题 2
      • 回答A
      • 答案 B
    • 问题 3
      • 回答A
      • 答案 B

以上内容从客户端正确发送。该数据的截图:

需要注意的是,正如您在上面看到的,Question 实际上是GridElements 实体。它也可能是TextImage,这是基于type = question 属性,即Discriminator

水合数据后,我们得到以下实体结构:

如您所见,水合后数据不再正确。这是在$form->isValid() 期间完成的。 QuestionSheet 1 包含QuestionSheet 2 的第一个Question,而Question 包含第二个Question 的第三个Answer 和第二个QuestionSheet 的第一个Answer

在阅读完整的水合数据集时,我看到为第一个 QuestionSheet 创建的 Answers 已被删除。第二个QuestionSheet 中的Answers 已被复制并覆盖了第一个问题表中的答案。本质上,就是您在上图中看到的内容。

更糟

以下是上述之后保存到数据库中的所有数据,上面提到的场景是2个列表,5个问题,9个答案。

所以对于第一个问题,没有剩下的问答环节。第二个 QuestionSheet 的问题已用于覆盖它们。此外,只有最后 2 个答案在那里,填补了应该是 9 的空间!。

顺便说一句,返回 this 的查询完全是 LEFT JOIN 以便同时显示所有空数据,仅此而已。

它似乎抓住了最后一组子实体来填充以前的实体,或者其他东西。我迷路了。

这怎么可能?

正如我所提到的,我已经做了很长时间,但找不到解决方案。希望大家能帮忙。

如果您需要有关代码的任何信息,我会尽力在此处显示或在某些专有代码的情况下尽可能解释。

更新 - 实体

分配实体

/**
 * @ORM\Entity(repositoryClass="Wms\Admin\Assignment\Repository\AssignmentRepository" )
 * @ORM\HasLifecycleCallbacks
 * @ORM\Table(name="ass_assignment")
 * @Gedmo\SoftDeleteable(fieldName="deletedAt", timeAware=false)
 */
class Assignment extends SeoUrl
{
    //Traits and properties

    /**
     * @ORM\OneToMany(targetEntity="Wms\Admin\Assignment\Entity\QuestionSheet", mappedBy="assignment", cascade={"persist", "remove"}, orphanRemoval=true)
     **/
    protected $questionSheets;

    public function __construct()
    {
        $this->abstractEntity_entityCategories = new ArrayCollection();
        $this->questionSheets = new ArrayCollection();
        $this->documents = new ArrayCollection();
    }

    public function __toString()
    {
        return (string)$this->id;
    }

    //More getters/setters
}

问题表实体

/**
 * @ORM\Entity(repositoryClass="Wms\Admin\Assignment\Repository\QuestionSheetRepository")
 * @ORM\Table(name="ass_questionsheet")
 **/
class QuestionSheet extends AbstractEntity
{    
    /**
     * @ORM\ManyToOne(targetEntity="Wms\Admin\Assignment\Entity\Assignment", inversedBy="questionSheets")
     * @ORM\JoinColumn(name="assignment_id", referencedColumnName="id", onDelete="CASCADE")
     **/
    protected $assignment;

    /**
     * @ORM\OneToOne(targetEntity="Wms\Admin\LayoutGrid\Entity\Grid", cascade={"persist", "remove"})
     * @ORM\JoinColumn(name="grid_id", referencedColumnName="id")
     **/
    protected $grid;

    public function __construct()
    {
        $this->gridElements = new ArrayCollection();
    }

    //More getters/setters
}

网格实体

/**
 * @ORM\Entity
 * @ORM\Table(name="lg_grid")
 **/
class Grid extends AbstractEntity
{
    /**
     * @ORM\OneToMany(targetEntity="Wms\Admin\LayoutGrid\Entity\Element\AbstractElement", mappedBy="grid", cascade={"persist", "remove"}, orphanRemoval=true)
     * @ORM\OrderBy({"y" = "ASC", "x" = "ASC"})
     */
    protected $gridElements;

    public function __construct()
    {
        $this->gridElements = new ArrayCollection();
    }
}

网格元素实体

/**
 * @ORM\Table(name="lg_grid_element")
 * @ORM\Entity
 * @ORM\InheritanceType("JOINED")
 * @ORM\HasLifecycleCallbacks
 **/
class AbstractElement extends AbstractEntity implements GridElementInterface
{
    /**
     * @ORM\ManyToOne(targetEntity="Wms\Admin\LayoutGrid\Entity\Grid", inversedBy="gridElements")
     * @ORM\JoinColumn(name="grid_id", referencedColumnName="id", onDelete="CASCADE")
     **/
    protected $grid;

    public $type = ''; //This is a discriminator
}

问题实体

/**
 * @ORM\Entity
 * @ORM\Table(name="lg_grid_question")
 **/
class Question extends AbstractElement
{
    /**
     * @ORM\OneToMany(targetEntity="Wms\Admin\LayoutGrid\Entity\Element\Answer", mappedBy="question", cascade={"persist"})
     */
    protected $answers;

    public $type = 'question'; //Inherited property, now filled in with discriminator value

    public function __construct()
    {
        $this->answers = new ArrayCollection();
    }
}

回答实体

/**
 * @ORM\Entity
 * @ORM\Table(name="lg_grid_answer")
 **/
class Answer extends AbstractEntity
{    
    /**
     * @ORM\ManyToOne(targetEntity="Wms\Admin\LayoutGrid\Entity\Element\Question", inversedBy="answers", cascade={"persist"})
     * @ORM\JoinColumn(name="question_id", referencedColumnName="id", onDelete="CASCADE")
     **/
    protected $question;

    public function __toSting() {
        return (string) $this->getId();
    }
}

更新 2 根据@Wilt 的回答更新了AbstractElement 实体。

/**
 * @ORM\Table(name="lg_grid_element")
 * @ORM\Entity
 * @ORM\InheritanceType("JOINED")
 * @ORM\HasLifecycleCallbacks

 * @ORM\DiscriminatorColumn(name="type", type="string")
 * @ORM\DiscriminatorMap({
 *     "abstractElement"="AbstractElement",
 *     "question"="Question",
 *     //Others
 * })
 **/
class AbstractElement extends AbstractEntity implements GridElementInterface
{
    //Same as above
}

此更新导致处理获取正确实体的NonUniformCollection 出现一些问题。这曾经基于$type 属性。

但是,不允许在以 * @ORM\DiscriminatorColumn(name="type", type="string") 作为符号的实体中拥有 $type 属性。因此,所有使用鉴别器的类也都更新了以下内容。

const ELEMENT_TYPE = 'question'; //Overwritten from AbstractElement

/**
 * @return string
 */
public function getType() //Overwritten from AbstractElement
{
    return self::ELEMENT_TYPE;
}

唉,原来的问题依然存在。

【问题讨论】:

  • validate your schema 有教义吗?也许您应该分享您的实体定义?
  • 是的,模式被验证为正常,数据库模式也很好。我会稍微更新一下这个问题,根据需要添加尽可能多的关系。
  • 对不起,一只虫子在别处长出了丑陋的脑袋,我的一天结束了。明天早上 (UTC+1) 将在这里更新问题。我将添加上述逻辑所需的实体,以及可能需要从表单/字段集中添加的实体。振作起来,可能很多。 ;)
  • 我已更新问题以包含与屏幕截图/问题相关的实体。希望你能帮忙:)
  • 快速更新。这个问题仍然存在,但由于需要在每次修改 Question(包括其 Answer)后执行保存操作的快速修复,它的重要性已经下降。同时,我愿意接受建议。 NonUniformCollection 仍然在列表中,有时间可以删除。

标签: php doctrine-orm zend-framework2 hydration


【解决方案1】:

我不确定这是否会导致您的问题,但在我看来您的继承映射设置不正确:

您需要在您的实体定义as written in the docs 中声明您的鉴别器列,它们不应设置为属性,学说会在您的数据库中设置它们:

/**
 * @InheritanceType("JOINED")
 * @DiscriminatorColumn(name="type", type="string")
 * @DiscriminatorMap({"element"="AbstractElement", "question"="Question", "text"="TextItem", "media"="MediaItem"})
 */
class AbstractElement extends AbstractEntity implements GridElementInterface
{
    //...
}

您的抽象实体是否正确映射as a @MappedSuperClass

/**
 * @MappedSuperclass
 */
class AbstractEntity
{
    //...
}

这可能是您解决方案的一部分,请在相应更新后返回反馈...

【讨论】:

  • 您好,威尔特,我已根据您的回答在问题中添加了更多信息(请参阅更新 2)。但是,问题仍然存在。有机会再看看吗?
  • @Nukeface 您是否也将您的AbstactEntity 映射为@MappedSuperclass
  • 对不起,我忘了回答这个问题,是的,它是一个带有 MappedSuperClass 映射的抽象类:` /* * @ORM\MappedSuperclass * @ORM\HasLifecycleCallbacks */ abstract class AbstractEntity implements AbstractEntityInterface {//stuff}`
  • @Nukeface 很难说问题出在哪里。你用的是什么保湿剂?也许你应该使用 xdebug 来确定出错的地方。
  • 使用 Doctrine Hydrator、Doctrine Entity 和 Doctrine Collections。但是,为了正确链接鉴别器类型实体,使用NonUniformCollection(有问题的链接)。关于调试,一直试图找出它决定最后一个问题/答案对第一组有效的精确位置。注意到它永远不会填充超过原始计数。假设 L1-Q1 有 2A,它将使用最后一个可用问题的前 2 个答案(例如 L5-Q10)。我觉得这很奇怪,但会继续调试。感谢您的努力,解决后会更新。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-07-20
  • 1970-01-01
  • 2011-09-09
  • 2021-12-27
  • 1970-01-01
相关资源
最近更新 更多