Doctrine has introduced an entity repository class, an object finder where you can keep methods to find and fetch entities. To make this process even easier, it provides ready made methods for creating and running DQL queries. As an alternative to writing the DQL you can use a query builder. And that is exactly what we do most of the time.
A lot of methods on our Doctrine2 repos used to perform similar functions: creating a query builder, applying criteria, setting a specific order, perhaps paginating and fetching results as well as counting items, getting statistics, creating custom hydration, etc. Basically, we stored all entity related methods regardless of the repository responsibility. We saw many people taking absolutely the same approach to their application development. Why might it have been wrong?
Problems come up when an application starts growing fast. If continuously adding more and more logic, the amount of methods in repositories will be growing and eventually you’ll get stuck with solid repository classes. They will be full of small code duplications related to different aspects of building queries like filtering posts by category, making sure they are published, or that the most recent posts go first, and others (below I will try to explain the idea with a classic blog application used in almost every respectable tutorial :) ).
Use custom query builders
The first thing we’ve done to improve our code is to start using custom query builders for entities. This simple thing has made our life much easier. Let me show you an example.
Typically, we used to do the following:
With a custom query builder we do it differently:
Just check it out! Now repositories tell us what is being done instead of how queries are being built. Awesome, right?
There are many other great benefits. For instance, if your post publish condition is changed you need to change it in a single place. DRY in action. You’ll also notice how writing unit tests will be bootstrapped. Taking the old approach, we had to duplicate asserts for each statement in repository method to make sure it worked properly. Custom query builders allow us to create one test for one statement and stop messing up tests with repeated asserts.
Enhance your query builders
Our next big step was an attempt to make repositories and query builders more verbal. The default Doctrine QueryBuilder class has quite poor API. Don’t get me wrong, it allows you to build any query you want, but are these statements easy to read?
We’ve come up with an idea while working on one of our applications using Propel ORM. We’ve felt how easily you can describe conditions in Propel and tried to achieve similar behavior with Doctrine QueryBuilder.
Keeping this in mind we’ve ended up with an enhanced query builder class. It has methods for fast filtering by property (the most frequent operation), pagination, fetching entities and more.
An extended query builder looks similar to this:
Honestly, there are not so many additions but we are pretty happy with the results. Anyway, it is a big step towards the ideal approach.
By the way, we’ve recently compiled most of our Doctrine add-ons into a public library available on GitHub. Hope you’ll find it as useful as we do.
Feel free to share you feedback, info about other issues you’ve come across, or send pull requests :)