Doctrine2中的反向关联分配

时间:2013-07-01 19:28:44

标签: doctrine-orm

Doctrine2是否支持从反面更改关联时更新关联的功能?如果没有开箱即用,是否有一个插件/扩展我可以用来掩盖这种行为?

以下是我的例子:

Organization
/**
 * @Id
 * @Column(type="integer")
 */
    protected $id

/**
 * @ManyToOne(targetEntity="Organization", inversedBy="children", cascade={"persist"})
 * @JoinColumn(name="parent_org_id", referencedColumnName="id")
 */
protected $parent;

/**
* @OneToMany(targetEntity="Organization", mappedBy="parent", cascade={"persist"})
*/
protected $children;    

1 个答案:

答案 0 :(得分:1)

没有。 Doctrine 2通过拥有方来跟踪关联,并且当它试图对你的实体的行为产生最小的影响时,它不想添加这种功能。

跟踪反向变化的标准方法是通过向实体添加逻辑来确保它们保持同步,这些实体在对反向进行更改时更新拥有方。

在您的示例中,您可以使用addChild,removeChild和setParent函数执行以下操作:

public function addChild(Organization $child) 
{
    $this->children[] = $child;
    $child->setParent($this); // < update the owning side
}

public function removeChild(Organization $child) 
{
    $this->children->removeElement($child);
    $child->setParent(null); // < update the owning side
}

public function setParent(Organization $parent = null) 
{
    $this->parent = $parent;
}

你可以看到现在出现了一个新问题,你必须始终使用addChild / removeChild函数(即在反面进行更改),以便拥有方保持同步(或者自己同步,作为调用者)。这导致您必须创建策略,要么呼叫者必须始终在拥有方更新,要么反向更新。

可以使setParent函数也更新反面,但你必须非常小心,因为这很容易导致无限递归:

public function addChild(Organization $child) 
{
    $this->children[] = $child;
    // safely update the owning side
    if ($child->getParent() != $this) {
       $child->setParent($this);
    }
}

public function removeChild(Organization $child) 
{
    $this->children->removeElement($child);
    // safely update the owning side
    if ($child->getParent() == $this) {
        $child->setParent(null); 
    }
}

public function setParent(Organization $parent = null) 
{
    $oldParent = $this->parent;
    $this->parent = $parent;
    // update the inverse side
    if ($oldParent) {
        $oldParent->removeChild($this); // will not call setParent
    }
    if ($this->parent) {
        $this->parent->addChild($this); // will not call setParent
    }
}

除了增加的复杂性之外,这种方案在例如将大量子节点从一个父节点移动到另一个父节点时并不理想,因为removeChild需要线性时间,为移动创建O(n ^ 2)运行时间。

相关问题