Doctrine2一对多关系覆盖整个集合

时间:2015-10-15 19:42:28

标签: php symfony orm doctrine-orm

假设我有两个实体Page和Block。它的双向映射。每个页面可以有多个块。每个块都可以属于单页。

/**
 * @ORM\Entity
 * @ORM\Table(name="page")
 */
class Page
{
    ...
    /**
     * @ORM\OneToMany(targetEntity="Block", mappedBy="page", cascade={"all"}, orphanRemoval=true)
    **/
    private $blocks;
    ...
}

/**
 * @ORM\Entity
 * @ORM\Table(name="block")
 */
class Block
{
    ...
    /**
     * @ORM\ManyToOne(targetEntity="Page", inversedBy="blocks")
     * @ORM\JoinColumn(name="page_id", referencedColumnName="id")
     *
     **/
    private $page;
    ...
}

有时候,属于页面的块会发生变化。将添加一些块,其中一些将被删除。下一个案例对我不起作用:

$page->setBlocks([1, 2, 3]);
$em->merge($page)
$em->flush() //Page will have blocks 1, 2, 3

$page->setBlocks([1, 4])
$em->merge($page)
$em->flush() //Page will have blocks 1, 2, 3, 4

第二次flush()调用后的预期结果是:// Page将有1,4

所以我需要用merge方法覆盖完整的块集合。

约束:

  • 我无法在Page class
  • 中实现deleteBlock
  • 我只能在$ em
  • 上调用merge()和flush()

是否可以通过注释或其他技巧实现所需的结果?

1 个答案:

答案 0 :(得分:0)

解决约束

您的约束不能完成。

建议的解决方案

我在BlockRepository类中创建一个自定义函数,删除给定Page的所有块,然后在调用setBlock函数之前调用该函数。

将此添加到您的Block Repository类:

public function deleteBlocksByPageId($page_id)
{
  $qb = $this->createQueryBuilder()
    ->delete('Block', 'b');
    ->where('b.page', ':page'));
    ->setParameter(':page', $page_id);

  $numDeleted = $qb->execute();

  return $numDeleted;
}

在您的控制器中:

//Delete all the existing blocks before adding them back in.
$repo = $this->getDoctrine()->getRepository('BundleName:BlockRepository');
$repo->deleteBlocksByPageId($page->getId());

$page->setBlocks([1, 4])
$em->merge($page)
$em->flush()

结论

这是管理经常更改的项目的常见模式。它不需要循环。

如果约束是自我施加的,那么你需要放松它们并找到另一种解决方案,例如我上面提供的解决方案。如果他们受到某些高层人员的限制,那么是时候和他/她谈谈在这种情况下它会产生反作用。