如何在 Doctrine 2 中删除没有 EntityManager#remove(...) 的集合元素?
How to remove a collection element without EntityManager#remove(...) in Doctrine 2?
我有一个像EntityA OneToMany EntityB
这样的经典结构。作为双向关系实现:
EntityB
有一个 属性 $entityA
类型 EntityA
和
EntityA
有一个 属性 $entityBs
,其中包含 ArrayCollection
个 EntityB
个元素。
现在我想删除一些 EntityB
元素。它会像这样工作:
$entityManager->remove($myEntityB);
$entityManager->flush();
但我希望能够 "say" $myEntityA->removeEntityB($entityB)
而不需要关心其他任何事情。一个优点是,我可以实现一个方法 EntityA#replaceEntityBs(ArrayCollection $entityBs)
,它只需删除所有 EntityA#$entityBs
并将它们替换为给定的元素。
是否可能/如何直接从关系的反面移除集合的元素(当然不通过EntityManager
进入实体)?
解决方案是从 EntityB
(第一个)中删除对 EntityA
的引用。在这种情况下,Doctrine 将尝试坚持 EntityB
没有。但是如果我们将它与 orphanRemoval=true
结合起来,我们将得到目标结果:
class EntityA
{
...
/**
* @var ArrayCollection
* @ORM\OneToMany(targetEntity="EntityB", mappedBy="entityA", cascade={"persist"}, orphanRemoval=true)
*/
protected $entityBs;
...
public function removeEntityB(EntityB $entityB)
{
$this->entityBs->removeElement($entityB);
$entityB->setEntityA(null);
return $this;
}
...
}
class EntityB
{
...
/**
* @var EntityA
*
* @ORM\ManyToOne(targetEntity="EntityA", inversedBy="entityBs")
* @ORM\JoinColumns({
* @ORM\JoinColumn(name="entity_a_id", referencedColumnName="id")
* })
*/
protected $entityA;
...
/**
* @param EntityA $entityA
* @return EntityB
*/
public function setEntityA(EntityA $entityA = null)
{
$this->entityA = $entityA;
return $this;
}
...
}
题外话:替换一个集合
由于我在问题中指出,一个优点是可以实现类似 EntityA#replaceEntityBs(ArrayCollection $entityBs)
的方法,我想在这里分享一个可能的实现。
第一个幼稚的尝试只是删除 all EntityBs
然后添加(并保留)新元素。
public function setEntityBs($entityBs)
{
$this->removeEntityBs();
$this->entityBs = new ArrayCollection([]);
/** @var EntityB $entityB */
foreach ($entityBs as $entityB) {
$this->addEntityB($entityB);
}
return $this;
}
public function removeEntityBs()
{
foreach ($this->getEntityBs() as $entityB) {
$this->removeEntityB($entityB);
}
return $this;
}
但是如果 setEntityBs(...)
的输入集合包含现有的 EntityBs
(应该更新),它会导致删除它们并且只有新元素被保留。
这是一个解决方案,可以按预期工作:
public function setEntityBs($entityBs)
{
$this->removeEntityBsNotInList($entityBs);
$this->entityBs = new ArrayCollection([]);
/** @var EntityB $entityB */
foreach ($entityBs as $entityB) {
$this->addEntityB($entityB);
}
return $this;
}
private function removeEntityBsNotInList($entityBs)
{
foreach ($this->getEntityBs() as $entityB) {
if ($entityBs->indexOf($entityB) === false) {
$this->removeEntityB($entityB);
}
}
}
我有一个像EntityA OneToMany EntityB
这样的经典结构。作为双向关系实现:
EntityB
有一个 属性$entityA
类型EntityA
和EntityA
有一个 属性$entityBs
,其中包含ArrayCollection
个EntityB
个元素。
现在我想删除一些 EntityB
元素。它会像这样工作:
$entityManager->remove($myEntityB);
$entityManager->flush();
但我希望能够 "say" $myEntityA->removeEntityB($entityB)
而不需要关心其他任何事情。一个优点是,我可以实现一个方法 EntityA#replaceEntityBs(ArrayCollection $entityBs)
,它只需删除所有 EntityA#$entityBs
并将它们替换为给定的元素。
是否可能/如何直接从关系的反面移除集合的元素(当然不通过EntityManager
进入实体)?
解决方案是从 EntityB
(第一个)中删除对 EntityA
的引用。在这种情况下,Doctrine 将尝试坚持 EntityB
没有。但是如果我们将它与 orphanRemoval=true
结合起来,我们将得到目标结果:
class EntityA
{
...
/**
* @var ArrayCollection
* @ORM\OneToMany(targetEntity="EntityB", mappedBy="entityA", cascade={"persist"}, orphanRemoval=true)
*/
protected $entityBs;
...
public function removeEntityB(EntityB $entityB)
{
$this->entityBs->removeElement($entityB);
$entityB->setEntityA(null);
return $this;
}
...
}
class EntityB
{
...
/**
* @var EntityA
*
* @ORM\ManyToOne(targetEntity="EntityA", inversedBy="entityBs")
* @ORM\JoinColumns({
* @ORM\JoinColumn(name="entity_a_id", referencedColumnName="id")
* })
*/
protected $entityA;
...
/**
* @param EntityA $entityA
* @return EntityB
*/
public function setEntityA(EntityA $entityA = null)
{
$this->entityA = $entityA;
return $this;
}
...
}
题外话:替换一个集合
由于我在问题中指出,一个优点是可以实现类似 EntityA#replaceEntityBs(ArrayCollection $entityBs)
的方法,我想在这里分享一个可能的实现。
第一个幼稚的尝试只是删除 all EntityBs
然后添加(并保留)新元素。
public function setEntityBs($entityBs)
{
$this->removeEntityBs();
$this->entityBs = new ArrayCollection([]);
/** @var EntityB $entityB */
foreach ($entityBs as $entityB) {
$this->addEntityB($entityB);
}
return $this;
}
public function removeEntityBs()
{
foreach ($this->getEntityBs() as $entityB) {
$this->removeEntityB($entityB);
}
return $this;
}
但是如果 setEntityBs(...)
的输入集合包含现有的 EntityBs
(应该更新),它会导致删除它们并且只有新元素被保留。
这是一个解决方案,可以按预期工作:
public function setEntityBs($entityBs)
{
$this->removeEntityBsNotInList($entityBs);
$this->entityBs = new ArrayCollection([]);
/** @var EntityB $entityB */
foreach ($entityBs as $entityB) {
$this->addEntityB($entityB);
}
return $this;
}
private function removeEntityBsNotInList($entityBs)
{
foreach ($this->getEntityBs() as $entityB) {
if ($entityBs->indexOf($entityB) === false) {
$this->removeEntityB($entityB);
}
}
}