【问题标题】:Symfony Form ManyToOne OneToManySymfony 形式 ManyToOne OneToMany
【发布时间】:2016-02-28 07:09:32
【问题描述】:

我有三个实体,Block、BlockPlacement、BlockPosition:

class BlockEntity
{
    private $bid;
    /**
     * @ORM\OneToMany(
     *     targetEntity="BlockPlacementEntity",
     *     mappedBy="block",
     *     cascade={"remove"})
     */
    private $placements;
}

class BlockPlacementEntity
{
    /**
     * The id of the block postion
     *
     * @ORM\Id
     * @ORM\ManyToOne(targetEntity="BlockPositionEntity", inversedBy="placements")
     * @ORM\JoinColumn(name="pid", referencedColumnName="pid", nullable=false)
     */
    private $position;

    /**
     * The id of the block
     *
     * @var BlockEntity
     * @ORM\Id
     * @ORM\ManyToOne(targetEntity="BlockEntity", inversedBy="placements")
     * @ORM\JoinColumn(name="bid", referencedColumnName="bid", nullable=false)
     */
    private $block;

    private $sortorder;
}

class BlockPositionEntity
{
    private $pid;
    /**
     * @ORM\OneToMany(
     *     targetEntity="BlockPlacementEntity",
     *     mappedBy="position",
     *     cascade={"remove"})
     * @ORM\OrderBy({"sortorder" = "ASC"})
     */
    private $placements;
}

所以,你可以看到关系:Block Placement Position。

现在我正在尝试构建一个表单来创建/编辑一个块:

    $builder
        ->add($builder->create('placements', 'entity', [
            'class' => 'Zikula\BlocksModule\Entity\BlockPositionEntity',
            'choice_label' => 'name',
            'multiple' => true,
            'required' => false
        ]))
    ;

这为我提供了一个很好的选择框,可以进行多项选择,并有适当的位置列表可供选择。但它没有显示以前的放置选择(我正在使用现有数据),例如将位置标记为“已选择”。我还没有尝试创建新块,只编辑现有数据。

我怀疑我需要使用addModelTransformer()addViewTransformer(),但我尝试了其中一些,但无法使其正常工作。

我查看了collection 表单类型,但我不喜欢该解决方案,因为它不是多选框。它需要 JS 并且不像简单的选择元素那样直观。

这对人们来说似乎是一个普遍的问题。我搜索并发现没有共同的答案,也没有任何帮助。

【问题讨论】:

  • 你为什么会想到 DataTransformers 来解决这个问题?当您需要转换模型数据时使用它们。是的,在这种情况下,收集字段要复杂得多。我将在下面添加指向我的答案的链接,以向您展示我的项目中同一案例的工作代码

标签: symfony doctrine-orm entity-relationship symfony-forms zikula


【解决方案1】:

更新:请看这个example repo

更新 2:我已经更新了 repo。

我使用表单事件侦听器和未映射的选择字段来做到这一点。 仔细看看BlockType form type 如有任何问题,请随时提出。

【讨论】:

  • 是的,我可以发布它以便我仔细看看吗?
  • @craigh,当然,请参阅此答案中的更新块以获取 github 链接
  • 谢谢。在接下来的几天里,我会仔细研究一下,然后再回来。
  • 好的 - 我想再次感谢您抽出宝贵的时间制作演示包 - 您真的很努力,我很感激。
  • 不幸的是,这也不是我需要的——你有一个使用选择元素的块,它显示了单独创建的 BlockPosition,但我需要的是一个仅显示位置的选定元素。并在创建块时,显示可用职位列表。我在本地有一些现在可以工作的东西。当我确认它按预期工作时,我会在此处发布。再次感谢您!
【解决方案2】:

好的 - 所以最后,我找到了不同的方法。 @Stepan Yudin 的回答有效,但很复杂(听众等),并不像我希望的那样。

所以,我有相同的三个实体。 BlockPlacement 和 BlockPosition 保持不变(因此不会重新发布,见上文),但我对 BlockEntity 进行了一些更改:

class BlockEntity
{
    private $bid;
    /**
     * @ORM\OneToMany(
     *     targetEntity="BlockPlacementEntity",
     *     mappedBy="block",
     *     cascade={"remove", "persist"},
     *     orphanRemoval=true)
     */
    private $placements;

    /**
     * Get an ArrayCollection of BlockPositionEntity that are assigned to this Block
     * @return ArrayCollection
     */
    public function getPositions()
    {
        $positions = new ArrayCollection();
        foreach($this->getPlacements() as $placement) {
            $positions->add($placement->getPosition());
        }

        return $positions;
    }

    /**
     * Set BlockPlacementsEntity from provided ArrayCollection of positionEntity
     * requires
     *   cascade={"remove, "persist"}
     *   orphanRemoval=true
     *   on the association of $this->placements
     * @param ArrayCollection $positions
     */
    public function setPositions(ArrayCollection $positions)
    {
        // remove placements and skip existing placements.
        foreach ($this->placements as $placement) {
            if (!$positions->contains($placement->getPosition())) {
                $this->placements->removeElement($placement);
            } else {
                $positions->removeElement($placement->getPosition()); // remove from positions to add.
            }
        }

        // add new placements
        foreach ($positions as $position) {
            $placement = new BlockPlacementEntity();
            $placement->setPosition($position);
            // sortorder is irrelevant at this stage.
            $placement->setBlock($this); // auto-adds placement
        }
    }
}

因此您可以看到 BlockEntity 现在正在处理一个在实体中根本不存在的 positions 参数。这是相关的表单组件:

$builder
    ->add('positions', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', [
        'class' => 'Zikula\BlocksModule\Entity\BlockPositionEntity',
        'choice_label' => 'name',
        'multiple' => true,
    ])

请注意,自从我第一次发帖后,我已更改为 Symfony 2.8 表单样式

这会在页面上呈现一个多选元素,该元素接受任意数量的位置,并在提交时将它们转换为 ArrayCollection。然后由表单的获取/设置位置方法直接处理,这些方法与放置属性相互转换。 cascadeorphanRemoval 很重要,因为它们会“清理”剩余的实体。

因为这里引用的是 BlockPlacement setBlock($block) 方法:

public function setBlock(BlockEntity $block = null)
{
    if ($this->block !== null) {
        $this->block->removePlacement($this);
    }

    if ($block !== null) {
        $block->addPlacement($this);
    }

    $this->block = $block;

    return $this;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-11-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多