Monday, February 15, 2010

Repositories in Doctrine

Jebb wrote to me asking information about the Repository pattern:
I read your blog at http://giorgiosironi.blogspot.com/and I found it very helpful. I develop applications using DDD and Zend Framework however for persistence ignorance, I use some basic Repository as interface for DAOs. May I ask you how to implement Doctrine ORM in the Repository?
As I explain in depth in my previous post about this pattern, a Repository is an illusion of an in-memory collection of all persistent ignorant entities of the same class. A Repository may be abstracted beyond an interface defining only the mandatory methods that make sense in the domain model.
How an Orm fits in this discussion? Very few repositories accomplish their tasks alone: most of them compose an underlying generic Data Mapper layer which is usually an Object-Relational Mapper which translates classes and objects in tables and rows. A generic Data Mapper such as Doctrine 2 provides all the possibly imaginable operations on the collection of entities, and the Repository abstraction decouples the other parts of the application from knowing anything about an Orm. This is the primary advantage of a Repository.

As you may have thought at the persistence ignorance reference, with Doctrine 1 there are no chances to implement a real Repository. Doctrine 1 does not provide persistence ignorance because it requires entity classes to extend a base abstract Active Record.
In Doctrine 2, repositories can be implemented as Plain Old Php Objects: simply injecting the EntityManager and other collaborators they may need in the constructor will suffice. A Factory can then encapsulate the new operators.
This is the standard Repository pattern: a Plain Old Php Object which aggregates whatever is needed to hide the persistence mechanism of objects.

Doctrine 2 also provides a facility to quickly implement Repositories, but the freedom of implementation of this approach is limited. The process is described in the Doctrine manual and it consists in:
  • extending an abstract class, which has a protected member $this->_em you can execute queries with;
  • annotating the entity class with the class name of the concrete repository class;
  • obtaining an instance with $em->getRepository('EntityClassName'): the EntityManager will create the object and automatically inject itself in the Repository.
Some notes on this architecture:
  • there is no entity-specific interface;
  • you extend an abstract class, which may be a cling because you want only some find*() methods to be available. However testing won't be affected since a concrete Repository will always involve a persistence mechanism.
  • it would be useful for having different repositories referencing each other via the EntityManager; but you may manually inject them instead, maintaining the original solution.
  • Doctrine 2 default Repository class is instantiated instead if you do not specify a subclass; if you use a lot of repositories and inject some of them in other service classes, the default implementation will be very handy. This would be a pro for taking advantage of the Orm support instead of using POPOs.

11 comments:

Jeboy said...

Thanks giorgio, that helps

Juan said...

Hi, I'm new to Doctrine 2 but I also find it useful on implementing with Repository pattern. However I'm lil' bit confused what should be the entity that will going to pass to $em->persist() to make the entity persist, is it the Entity Domain Object from the domain layer of DDD? If that is, then how could it be map to a Database table? Thanks

- juan.delacruzweb@yahoo.com

Giorgio said...

Usually your repository has $this->_em as a field reference, passing it the entity to persist when an add() method is called. The Api of the repository is similar to a collection one, but its concrete implementation puts to and gets from the EntityMnager instead of to and from an array.

Unknown said...

How would I unit test my custom implementation in a repository, should I mock the entity manager?

Giorgio said...

No, in general you should mock only type you create. You can *integration* test your repository by passing it an EntityManager which works with sqlite (lightweight) instead of MySQL or your production database of choice.

Anonymous said...

Hi,
I have a question. I am looking at CmsArticle entity here
https://github.com/doctrine/doctrine2/blob/master/tests/Doctrine/Tests/Models/CMS/CmsArticle.php

So CmsArticle can have many CmsComments. Let's say a article has 1 million comments, when we do $aArticle->getComments() (which is not implemented on the github) we will load all 1 million comments.

is there a way to use some kind of limit/offset or pagination within doctrine entities ?

Thanks
Bill

Giorgio said...

As far as I know, the collection is lazy loaded (but in a single shot), so you'll get the 1 million comments only if you call a method on the Collection object. We usually do pagination via queries - it's very simple to write a query in DQL to retrieve the content of a collection, especially if you already have the Entity.

Anonymous said...

Thanks for the reply.
I thought collection would be lazy loaded via entities proxy object. But how would you implement the query here? $article->comment will retreive all comments, right? So do we implement something like
$article->getComments($from, $count)
and then write the dql in article objects getComments method?

Giorgio said...

You'll need the EntityManager to execute a query, so it's best seen as a method on an external Repository or DAO more than on the Article or on another Entity.

Anonymous said...

Hi again,
I am quite a noob :) Could you give me a short example please?

Thanks
Bill

Giorgio said...

Here's how to use DQL for pagination:
http://www.doctrine-project.org/docs/orm/2.0/en/reference/dql-doctrine-query-language.html#first-and-max-result-items-dql-query-only

ShareThis