thingswithpixels

May 06, 2010

Getting Started with Doctrine 2, Part 2

In Part Two of my Doctrine 2 tutorial, I’m going to spend some time talking about the general ideas behind Doctrine 2, and how it lets you manage database persistence using Plain Old PHP Objects (POPO).

What Doctrine is Not

But first, a few words about why Doctrine might not be like any other ORM you’ve used. Doctrine is not ActiveRecord. If you’ve been working with Rails, Django, Propel or even Doctrine 1, you’re probably very used to the ActiveRecord pattern.

ActiveRecord ORMs are typically characterized by providing a model base class for your business objects to inherit from:

class User extends Doctrine_Record // Doctrine 1
class User < ActiveRecord::Base // Rails
class User(models.Model) // Django

In Doctrine 2, there is no need to inherit from a base class. You can define your business objects like so:

class User

That’s it. Real simple.

Furthermore, ActiveRecord usually provides a save() method on the model object that handles insertion and updates to the database. With most implementations, this means that a single instance of your model object corresponds to a single row in your database. And the model object is responsible for its own data persistence.

Instead, Doctrine 2 relies on a separate persistence layer to handle database updates, via an Entity Manager object. This can have several benefits, including making it easier to test a domain layer.

What Doctrine Is

If we’re going to contrast Doctrine 2 to ActiveRecord, it can be said that it is an implementation of the Data Mapper pattern. It fully separates the in-memory representation of an object from it’s persistence details. It accomplishes this using a combination of provided mapping information along with class reflection.

While not a direct port, Doctrine 2 is very similar to Hibernate framework found in Java (or NHibernate in C#). It will likely feel very familiar to anyone who has used either of those libraries.

The Entity Manager

If you followed part one of this tutorial, you learned how to create entity mappings and classes. You should treat these entity classes just like any other object in PHP. When you’re ready to write back to the database, you will need an instance of EntityManager.

$em = \Doctrine\ORM\EntityManager::create($connectionOptions, $config);

For details on configuring and instantiating the Entity Manager, see the documentation.

Once you have an instance of EntityManager, you can send entity objects to it to either be added, updated or removed in the database.

$em->persist($employee); // will create or update rows
$em->remove($employee); // will remove rows

By using these methods, you are instructing the Entity Manager to modify the Unit of Work. When it’s time to actually write back to the database, call flush()

$em->flush();

All pending database operations will be wrapped in a single transaction and executed.

So for a complete example, let’s use our entities to create a new Employee, and assign it a new project.

$employee = new Employee();
$employee->setLastName('Roberts')
         ->setFirstName('Roger')
         ->setJobTitle('Manager');

$project = new Project();
$project->setName('TPS Initiative');

$employee->getProjects()->add($project);
$project->setEmployee($employee);

$em->persist($employee);
$em->persist($project);
$em->flush();

Note that for new objects, id’s will not generated until flush() is called.

In the third part, I’ll discuss how to handle relationships between objects, and concept of the ‘owning side’ in Doctrine 2.