开发者

Ensuring object relationship consistency (doctrine 2)

I am having a problem ensuring that relations between my objects remain consistent. I am using Doctrine 2, but this probably applies to other languages and ORM mappers as well.

Doctrine says that relationships have an owning side and an inverse side. If you use bidirectional relatio开发者_运维技巧nships, then you need to ensure consistency yourself. Basically than means, when one side of the relationship changes, you manually need to update the other side.

Here my example where Foo hasMany Bar:

class Foo
{
    /** @OneToMany(targetEntity="Bar",mappedBy="foo") */
    private $bars;

    public function addBar($bar)
    {
        $this->bars[] = $bar;
        // Ensure consistency
        $bar->setFoo($this);
    }
}

class Bar
{
    /** @OneToMany(targetEntity="Foo",inversedBy="bars") */
    private $foo;

    public function setFoo($foo)
    {
        $this->foo = $foo;
        // Ensure consistency
        $foo->addBar($this);
    }
}

Now obviously, the code above will not work as intended. When Foo::addBar or Bar::setFoo is called it will descend into an infinite loop. So, how should Foo and Bar be implemented so this works as designed?

The User/Bug example in the Doctrine 2 "Getting started" guide simply skirts around the issue by only ensuring consistency in addBar and not in setFoo.

The wall that I am running into is that is that both addBar and setFoo must be public functions or I cannot call them from the related model. But that means that developers using these objects could use either function to create new relationships.

How do I solve this? How do other languages and mappers solve this?


I don't know how other mappers or such solve this, but basically in Doctrine 2...

When they talk about the "owning side" in the manual, they refer to the entity which your code should always use to associate the objects with.

This is mostly a semantic decision: For example, if you have cars and tires, it makes sense that a tire is added to a car $car->addTire($tire); and the car is not added to the tire $tire->setCar($car);

So what the owning entity should do is ensure the consistency of the relation. The "non-owning" side should just set the association property.

So yeah, if your code incorrectly uses the non-owning side, the relation may end up inconsistent. However, this is only a problem during the execution of the script. Even if you don't set up the relation in both ends, it will get stored correctly in the DB, so the next time the data is fetched, it will get set up between the entities in a consistent manner.


This is my approach so far. Just added some checks.

class Foo
{
    /** @OneToMany(targetEntity="Bar",mappedBy="foo") */
    private $bars;

    public function addBar($bar)
    {
        if (!$this->bars->contain($bar)) {
            $this->bars[] = $bar;
            $bar->setFoo($this);
        }
    }
}

class Bar
{
    /** @OneToMany(targetEntity="Foo",inversedBy="bars") */
    private $foo;

    public function setFoo($foo)
    {
        if ($this->foo !== $foo) {
            $this->foo = $foo;
            $foo->addBar($this);
        }
    }
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜