Protect your ArrayCollections

Here's a little Doctrine ORM tip to safeguard your business logic when dealing with collections.

Do you have some business logic tied up in your adder methods? Something like this maybe.

class Cart
{
    private $items;

    public function __construct()
    {
        $this->items = new ArrayCollection();
    }

    public function addItem(Item $item)
    {
        if ($item->isExpired()) {
            throw new ItemExpiredException(sprintf("The item is expired: %s.", $item->getName()));
        }

        $this->items->add($item);
    }

    public function getItems()
    {
        return $this->items;
    }
}

Look how easy it is for another developer to accidentally skirt your nice logic.

$cart = new Cart();
$cart->getItems()->add($expiredItem);

It's easy to prevent though!

class Cart
{
    // ...

    public function getItems()
    {
        return new ArrayCollection($this->items->toArray());
    }
}

If you're concerned with them still calling $cart->getItems()->add($expiredItem) and silently not doing what they think it should be doing, simply return $this->items->toArray() from the getItems() method.

If you want to return an ArrayCollection and also take advantage of Doctrine's extra lazy functionality one solution would be to subclass ArrayCollection and proxy read-only calls to the lazy collection.

class Cart
{
    // ...

    public function getItems()
    {
        return new ReadOnlyCollection($this->items);
    }
}

Tags: PHP, DoctrineORM