cult3

Getting started with Doctrine 2 and Laravel

Jun 30, 2014

Table of contents:

  1. What is Doctrine2 and how is it different to Eloquent?
  2. Why would you want to use Doctrine2 instead of Eloquent?
  3. An example of the benefits of Doctrine
  4. Using Doctrine to enforce business rules
  5. Setting up Doctrine2 with Laravel
  6. Conclusion

So far in this Building Cribbb series of tutorials I’ve used Laravel’s Eloquent ORM to handle dealing with the database. Eloquent makes querying, inserting, updating and deleting data from a database really easy, and it comes bundled with Laravel meaning everything is set up right out of the box for you.

However recently I’ve been thinking that it might be advantageous to use a different ORM other than Eloquent. Eloquent is a fine ORM and I’ve used it extensively in other projects, but I think it would be really interesting to use a different ORM in this project.

Instead of Eloquent, I’ve decided to use Doctrine 2. Doctrine 2 is an ORM, but it follows a different methodology to Eloquent that offers a number of benefits.

In this tutorial I’m going to talk about the benefits of using Doctrine 2 over Eloquent as well as how to set it up within your Laravel project.

What is Doctrine2 and how is it different to Eloquent?

Doctrine2 is an ORM is much the same way as Eloquent, but there are some very important differences.

An ORM (Object-relational mapping) is a way of moving data between a relational database (like MySQL) into PHP objects that are useful to work with in our applications. Because the database will store the data as relations, we need a way of converting back and forth whenever we save or retrieve data.

Both Doctrine 2 and Eloquent are ORMs that allow us to easily query and persist data with a database without ever having to worry about converting objects to relations or vice versa.

However, Doctrine2 and Eloquent differ in the way that they work.

Eloquent uses the Active Record pattern. Generally speaking, this is where each row in the database is tied to an object. When you retrieve a row from the database you can update, delete or save using the object itself.

For example using Eloquent you would do something like this:

$user = new User();
$user->name = "Philip Brown";
$user->save();

Doctrine 2 uses the Data mapper pattern. This is where data is mapped to the database, but the ability to persist data is kept separate from the actually object itself. To persist the data we use something called an entity manager.

Using Doctrine 2 looks like this:

$user = new User("Philip Brown");
EntityManager::persist($user);
EntityManager::flush();

Why would you want to use Doctrine2 instead of Eloquent?

The benefit of using Doctrine2 over Eloquent is that the domain logic of the object is kept completely separate from the logic of persisting data to the database. This means that the things that make your application unique, don’t need to be concerned with how the data is actually persisted to storage.

When you use an Active Record implementation like Eloquent, each object has the ability to change the database. This makes for code that is leaky and can cause issues if the rules of dealing with those objects aren’t strictly followed.

Using a Data Mapper implementation like Doctrine 2 ensures that the “business logic” of the objects are encapsulated in plain PHP objects, and all logic about how those objects are stored is handled by a completely separate service.

For more on this topic, take a look at What’s the difference between Active Record and Data Mapper?.

An example of the benefits of Doctrine

So you are probably thinking, “the difference between using Eloquent or using Doctrine seems a bit hand wavy”. I would have to agree that without actual real world examples, the difference between using Active Record verses using The Data Mapper pattern does seem like a bit of a non-issue.

However with that being said, here is a real life example of why I’m choosing to use Doctrine over Eloquent for this project.

The big benefit of using the Data Mapper pattern over the Active Record pattern is the encapsulation of business rules. Business rules are simply the rules around how your application works.

Business rules

Each application will have rules around how things should work. Typically if you are building an application within an existing business, the business will already have rules and processes around how things work. But even for new applications or new businesses, the application will typically be required to work in a certain way.

For example in Cribbb, a User might only be able to write a new Post if she is a member of the Cribbb.

This is a business rule of our application. We need to be able to enforce this rule so users can’t make posts into cribbbs that they are not a member of.

But how and where do we enforce this rule in our application?

Where to enforce business rules

The business rules of our applications are extremely important as they enforce how the application should work and how users should be able to interact with the system.

Knowing when and where to place your business rules, however, can be a little bit tricky.

Typically you might see an application dealing with this kind of logic in the controller:

public function store()
{
    $user = User::find(Input::get('user_id');
    $cribbb = Cribbb::find(Input::get('cribbb_id');

    if ($cribbb->users->contains($user) {
        $cribbb->posts()->save(Post::create(Input::all()));
    }

    return $cribbb;
}

This would work, but strictly speaking, this kind of logic should not be in the Controller. If you place this kind of logic in the Controller you will probably find that you end up repeating yourself in multiple places around your application. Your Controllers are only for web requests, and so if you need to provide an API or a command line interface to your application you will have to duplicate this code.

Alternatively you could place this logic in the Model itself. This means whenever a user tries to post to a Cribbb our business rule will be in place:

class Cribbb extends Eloquent {

    public function addPost(User $user, array $post)
    {
        if ($this->users->contains($user) {
            $this->posts()->save($post);
        }
    }
}

This means whenever we use an instance of Cribbb in our controllers, API or command line the business rule will be enforced!

However…

The Cribbb model is still an instance of Eloquent and so we could quite easily bypass the business rule by simple doing:

$cribbb->posts()->save(new Post(Input::all()));

This means our all important business rules are easy to break if someone who isn’t aware of the business rules is working in the codebase.

Finally, we could place the business rules logic in a Service class or Repository. This means that the Service class or Repository would provide an interface that enforces the business rules. This means we couldn’t add posts to a cribbb we weren’t a member of.

However, once again because we are working with Active Record models, there would be nothing to stop us from bypassing the Service or Repository altogether and just doing what we want with the model object.

So hopefully you can see, having direct access to the persistence layer of the database means we can’t enforce the business rules of our application. We can define an API for our models, or put a layer of indirection in-between the model and the developer, but there is no way of totally enforcing the rules. Active Record will allow us direct access to the database to make whatever changes we want, no matter what business rules we have in place.

Using Doctrine to enforce business rules

This is not an issue with Doctrine because Doctrine entities are plain PHP objects that do not inherit any public methods and they have no knowledge of how the entities are persisted to the database.

When using Doctrine, we could define a method on our Cribbb entity like this:

public function addPost(Post $post)
{
    if ($this->isMemberOf($post->user)) {
        $this->posts[] = $post;
    }
}

This would enforce the rule that a post can only be added to cribbb by members of that cribbb.

So as you can see, the big benefit of using the Data Mapper pattern with Doctrine is the ability to define and encapsulate our business rules within our entities. This prevents leaky code, and allows us to enforce these rules at the heart of application’s domain.

For more on this problem, have a read of How We Code: ORMs and Anemic Domain Models.

Setting up Doctrine2 with Laravel

Doctrine2 is a stand alone package that can be basically dropped into any PHP project as the ORM layer.

However Doctrine 2 isn’t exactly a trivial package to set up as there is a lot going on under the hood.

What’s more, ideally we want Doctrine 2 to seamlessly fit with the structure of Laravel 4. It would be a bit awkward if the configuration of Doctrine looked totally out of place with how everything else is set up in our Laravel 4 project.

Fortunately we work within the beautiful world of Open Source software and so we don’t have to reinvent the wheel! Mitchell van Wijngaarden has already created a bridge to allow Doctrine 2 to melt seamlessly with Laravel 4’s existing configuration.

This will make our lives working with Doctrine 2 and Laravel 4 a whole lot easier!

To install Doctrine 2 within our Laravel 4 project we can simply add the following to the composer.json file:

"require": {
"mitchellvanw/laravel-doctrine": "0.*"
}

And then update composer:

composer update

Next we need to add the Service Provider and Facade alias to the app.php configuration file under app/config.

Add the following to your providers array:

"Mitch\LaravelDoctrine\LaravelDoctrineServiceProvider";

And the following to your alias array:

'EntityManager' => 'Mitch\LaravelDoctrine\EntityManagerFacade'

Finally this package has a custom configuration file. To publish the configuration file, run the following command:

php artisan config:publish mitch/laravel-doctrine -path=vendor/mitch/laravel-doctrine/config

Conclusion

Using Doctrine2 is quite a bit of a shift from using Eloquent and so I’ll save getting into the code for next week. Whilst there are many of the same similarities, I think if you are approaching Doctrine 2 for the first time, some of the concepts do need a bit of explaining.

It’s important to say, there is absolutely nothing wrong with Eloquent or the Active Record pattern. I’ve personally used Eloquent in a number of projects. Eloquent (and the Active Record pattern) make working with data and persistence in your application a total breeze.

However I do like the Data Mapper pattern and what it can offer you as a developer. I think using Doctrine 2 in a Laravel application is a good opportunity to write some tutorials on using the ORM and making good use of the different methodology. I’ve been looking at using it for a while, but Mitchell’s bridge package sealed the deal for me.

If you are happy using Eloquent you shouldn’t switch just for the sake of it. Continue using Eloquent and use the next couple of tutorials as something to think about for one of your future projects.

This is a series of posts on building an entire Open Source application called Cribbb. All of the tutorials will be free to web, and all of the code is available on GitHub.

Philip Brown

@philipbrown

© Yellow Flag Ltd 2024.