thingswithpixels

Apr 24, 2010

Getting Started with Doctrine 2, Part 1

Update: Since this post, Doctrine 2 has seen a stable release. I highly recommend you consult the documentation, many things about the API have changed, or new features added.

I’ve been developing with version 2 of the <a href=’http://www.doctrine-project.org/>Doctrine ORM for a few months now, and am really excited about the upcoming beta release. Not only does Doctrine 2 improve upon the first version of the framework, it is gearing up to be the de-facto ORM for modern PHP development.

What follows will be the first part in an ongoing series of articles about getting up and running with Doctrine 2. In this first part, we’ll create mapping files using XML, and use the command line tool to generate our entity classes and database schema.

Learning Doctrine 2

Before we begin, I first want to point you to the official Doctrine 2 Documentation. It’s well written, with lots of great code samples. It’s also been continually updated with each new release, so there’s more information be added all the time.

Seriously, any time you’re stuck working with Doctrine, go back to the manual. There’s a good chance you’ll find the answers you need. If not, getting a response mailing list is usually your second best option.

Now let’s get started.

Starting the project

For the full source code of the sample project as it evolves, please visit the project page on Github.

For now, the project is split up into two folders: lib and tools. lib will hold all our code, plus external libraries. tools will contain the Doctrine command line tool. Create a folder inside lib called Tutorial. This is the namespace we’ll use for our code.

Installing Doctrine

Note: Doctrine is now in it’s second Beta. There are some things in this article which may no longer apply, especially as it concerns the CLI. Please check the Doctrine documentation for up-to-date info. I’ll try to fix this tutorial when I have time.

This series of tutorials is aimed at the first Beta release of Doctrine 2. The Beta hasn’t quite been released yet, so, we’ll be working off the trunk release of the framework for now. Let’s check it out from the repository:

svn co http://svn.doctrine-project.org/trunk DoctrineTrunk

After getting the source, make sure to copy or symlink the Doctrine and Symfony library folders (which can be found in lib and ‘lib/vendor’, respectively) into our own project’s lib folder.

Also, copy the following 3 files from tools/sandbox into the tools folder in your project:

  • cli-config.php
  • doctrine
  • doctrine.php

These are the command line tools we’ll use later to automatically generate our entity classes and database schema.

Starting with XML Mappings

First, I should note that using XML to map your entities is not the default choice Doctrine makes. Most people seem to prefer the Annotation mappings that are in-line with your class property definitions. This mapping style is already well documented, so I thought I would demonstrate an alternate method instead.

If you’re interested in using annotations or YAML instead of XML, I encourage you to keep following along. The basic ideas are the same, only the syntax will differ.

Next create two folders inside our Tutorial folder:

  • Entities
  • Entities\Mappings

These are the folders that will hold our entity classes, and the mappings we’re about to create.

Let’s create our first mapping file. Inside the mappings folder, create an XML file named Tutorial.Entities.Employee.dcm.xml. Accurately naming the file is important, because Doctrine requires you to use the fully-qualified namespace. In this case, the namespace of our entity classes is Tutorial\Entities.

Inside the file, added the following XML code:

<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
                    http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd">

    <entity name="Tutorial\Entities\Employee" table="employees">

     </entity>

</doctrine-mapping>

This is the basic shell of any entity mapping. It consists of a root node called<doctrine-mapping> and an <entity> node. Notice how we’re including the fully-qualified class name as the name of the entity, as well as the name of the database table that the entity will use.

Note: One of the many reasons I prefer XML mappings is because Doctrine provides a schema definition. This enables many IDEs to provide validation and code completion which makes writing the mappings very easy.

Now we need to tell the mapping file how to generate a unique id (or primary key) for our entity:

<entity name="Tutorial\Entities\Employee" table="employees">

<id name="id" type="integer" column="id">
  <generator strategy="AUTO"/>
</id>

</entity>

We use the element to tell Doctrine the name of our identifier, it’s type, and which column in the database to use. We also provide a ‘generator’, which in this case, is telling doctrine to use auto-increment in our primary key field.

Now we can start adding other fields to our mapping:

<entity name="Tutorial\Entities\Employee" table="employees">
  <id name="id" type="integer" column="id">
    <generator strategy="AUTO"/>
  </id>

  <field name="lastName" type="string"/>
  <field name="firstName" type="string"/>
  <field name="jobTitle" type="string"/>
  <field name="hireDate" type="datetime"/>
</entity>

The <field> element is created, along with a name and a type. Tthese types are not database types, but rather a type specific to Doctrine that binds to a specific database type.

Also note that the name attribute references the property that will be on your entity class, not the column name in your database. By default, Doctrine will generate the column name based on your property name. If you wish to customize a column name, use the column attribute:

<field name="lastName" column="last_name" type="string"/>

Create a second mapping

For this tutorial, we’re going to have two entities: Employee and Project. We just finished creating the mapping for Employee, so here’s the complete mapping for Project.

In Tutorial.Entities.Project.dcm.xml:

<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
                    http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd">

  <entity name="Tutorial\Entities\Project" table="projects">

     <id name="id" type="integer" column="id">
      <generator strategy="AUTO"/>
     </id>

     <field name="name" type="string"/>
     <field name="dueDate" type="string"/>

  </entity>
</doctrine-mapping>

Establishing a relationship

Of course, no ORM implementation is complete without a way to establish relationships between objects. These come in the form one-to-one, one-to-many, and many-to-many relationships between your entity classes that need to be represented in your database layer. For our project, we want to establish a bi-directional one-to-many relationship between an Employee and a Project.

In your Employee mapping file, include this element:

<one-to-many target-entity="Project" mapped-by="employee" field="projects"/>

This establishes a one Employee to many Project relationship. We’ve also defined which fields will represent this relationship in our entities. On the Employee side, we will use a property named projects, and the inverse of the relationship will be represented by employee property in the Project entity.

Once again, we’re working at the class level by defining properties that will be mapped to our database. At this level, we have no need to define foreign keys ourselves. When we generate our schema, doctrine will automatically create the foreign key fields.

We also need to define the inverse of the relationship in our Project mapping:

<many-to-one target-entity="Project" inversed-by="projects" field="employee"/>

That’s it for our mappings. Now we’ll set up our command line environment for generating our classes and schema.

Doctrine Command Line Interface

The command line tool has lots of useful feature. For this part, we’re going to focus on two commands. One that will generate entity classes from mappings, and one that will generate database schema.

First, we need to configure the cli-config.php file you copied into the tools folder to load our library namespaces and our XML mappings. Copy and paste this into the file:

&lt;?php 
 require_once __DIR__ . '/../lib/Doctrine/Common/ClassLoader.php';

 $classLoader = new \Doctrine\Common\ClassLoader('Tutorial', __DIR__ . "/../lib");
 $classLoader->register();

 $config = new \Doctrine\ORM\Configuration();
 $config->setMetadataCacheImpl(new \Doctrine\Common\Cache\ArrayCache);

 $driverImpl = new \Doctrine\ORM\Mapping\Driver\XmlDriver(array(__DIR__ .    "/../lib/Tutorial/Entities/Mappings"));
 $config->setMetadataDriverImpl($driverImpl);

 $config->setProxyDir(__DIR__ . '/../Proxies');
 $config->setProxyNamespace('Tutorial\Proxies');

 $connectionOptions = array(
      'driver' => 'pdo_sqlite',
      'path' => __DIR__ . '/../db/database.sqlite'
 );

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

 $helperSet = new \Symfony\Components\Console\Helper\HelperSet(array(
   'db' => new \Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper($em->getConnection()),
   'em' => new \Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper($em)
));

While I won’t go into the details of this here, configuring the Doctrine CLI is already a well-documented process. However, you’ll notice that we’re using sqlite for the purposes of this tutorial. Feel free to substitute your database of choice.

Now we’ll run the command to generate our entity classes. Navigate to your tools folder and run the following:

$ ./doctrine orm:generate-entities ../lib

This should create class files in the project’s Entities folder. You’ll notice it also automatically generates properties as well as getter and setter methods. Very handy.

Once the entities are create, we can create the database schema:

$ ./doctrine orm:schema-tool:create

With luck, everything worked perfectly, and we now have both entity classes and a database schema to start programming with.

In the next part of this tutorial, I’ll talk a bit about the Entity Manager, and how to save and update objects.