Models made easy with the Doctrine ORM

📁 symfony

Most web applications like to persist things, and as a web developer, you’re going to be spending a good portion of your development time pulling things in and out of databases.

If you’ve worked on more than a couple of projects, you know how quickly interfacing with your database becomes a bore. Especially if you’re dealing directly with SQL, the code that persists or retrieves your objects is usually verbose and repetitive. And if you think about it you’re really doing the same thing many times over: creating, reading, updating and deleting data with some sanitisation and error checking thrown in around the edges.

Luckily, Symfony comes bundled with an Object-Relational Mapper (ORM). An ORM provides a set of classes that map the generic datatypes from a programming language into their relational database counterparts. Using an ORM, we can automatically generate the code that we need to persist and retrieve PHP objects to a relational database.

Note: this article assumes that you’ve already configured the database for your Symfony installation.

Enter Doctrine

The ORM included in Symfony is called Doctrine. Using Doctrine we can either define our model and then generate the classes and database schema to implement it, or we can go the other way and introspect an existing database, generating the PHP classes required to access it. Boom.

Doctrine also provides loads of advanced features for things like complicated foreign key relationships, special field-types (timestampables, sluggables, etc), fixtures, migrations and much more.

Creating Doctrine Entities from the Command Line

Generating our models with the Symfony-Doctrine tag team is incredibly easy. From the command line (assuming you’re in the base folder of your Symfony project) we can run the following command:

$ php app/console doctrine:generate:entity

We’ll then be placed in an interactive mode with the Symfony command line interface (CLI) which will ask us for the information it requires to generate the model class. These questions include:

  • The “entity shortcut name.” This will be in the format [BUNDLE]:[ENTITY NAME], ie: MyCompanyBlogBundle:Article
  • The configuration format. This sets the file format that will be used to keep track of the entity’s definition. PHP annotation is the default (and I tend to stick with this).
  • The fields. Now the CLI will ask us to enter the names and datatypes of all the columns for our entity. This will continue until you leave the field blank and hit enter.
  • It will also ask us if we want to create an “empty repository class.” The default is no, but I recommend selecting yes. We’ll see the DoctrineRepository object later, but creating an empty instance of this will allow us to define our own methods to search the database.

This will then create the class that represents the entity. Note that we can also speed up the interactive CLI process by providing some of the values as command arguments. For example:

$ php app/console doctrine:generate:entity \ --entity="MyCompanyBlogBundle:Article" \ --fields="title:string(255) description:text"

And here’s the class that will be generated:

Simple, right? So simple that you would think you could write this class without using the command line tool – and you’d be right. But before we get too far ahead of ourselves, let’s look at this class in a little more depth.

The most important thing to notice is that this class looks really simple. A little bit too simple. And that’s because a lot of the magic is not actually occurring in the code, but in PHP annotations in the comments. For example, notice this section at the top of the file:

/** * Acme\BlogBundle\Entity\Article * * @ORM\Table() * @ORM\Entity */

This is letting Doctrine know that we’re defining a new table, and that the table is going to represent an entity. There are also a number of sections where we define the fields in the database which, funnily enough, match up with the information we provided in the configuration process. These look similar to the following:

/** * @var string $title * * @ORM\Column(name="title", type="string", length=255) */ private $title;

As you can see all the metadata about our columns is represented in the annotations, including the name of the field in the database, its type and any other relevant information. One thing you may have noticed is that Doctrine added something for us:

/** * @var integer $id * * @ORM\Column(name="id", type="integer") * @ORM\Id * @ORM\GeneratedValue(strategy="AUTO") */ private $id;

If you don’t explicitly define a primary key for your entity, Doctrine is nice enough to create an autoincrementing integer field for you. Great! Except for one minor thing… If you actually try and use this class anywhere in your code, you’ll notice it doesn’t actually work. Damn.

Updating the Database

So far we’ve only built one side of the bridge: the PHP classes. The next step is to build the other side of the bridge across the gaping chasm of modellessness so they meet in the middle. That construction process is surprisingly simple, thanks again to our good friend Doctrine. From the command line, we can type the following to force Doctrine to go and work its magic on the database:

php app/console doctrine:schema:update --force

Note

9 times out of 10 you’re going to get a big scary error message at this point about DateTime::__construct(). This is basically just complaining that you haven’t adjusted your timezone correctly and should do so in your “php.ini” file. To fix this, open the local “php.ini” file and add the declaration:

date.timezone = "[YOUR TIMEZONE]"

Where [YOUR TIMEZONE] is a valid value from here. Can’t find your “php.ini” file? Create a blank PHP file with a call to the phpinfo(); function in it. Navigate to it and you’ll see the .ini path under “Loaded Configuration File”.

Putting it all together

Now we have our PHP classes and we’ve generated our database schema, it’s time to get them talking. Let’s create two basic actions within a controller called “MyCompanyBlogBundle:Post.” One will display a basic form, the second will create an entry in the database based on the form values. We’ll also define a index page that will display a given article based on its ID.

Here we go! Be sure to read the comments for an explanation of the code – this is our controller:…

… and our two view files

Conclusion

That is a lot of information to digest for one lesson!

You should now be able to build and use basic entities within your Symfony applications. This is incredibly powerful, especially when coupled with Symfony’s powerful Form and Validator frameworks, which will be explored in later lessons.

For now, check out the Symfony and Doctrine documentation sites for more information.