Keywords

These keywords were added by machine and not by the authors. This process is experimental and the keywords may be updated as the learning algorithm improves.

In the previous chapter we learned about the complexity of an ORM the hard way by implementing our own persistence code. Doctrine 2 instead provides full transparent persistence for PHP objects by implementing the so called “Data Mapper Pattern”. Essentially, Doctrine allows the programmer to focus on the object-oriented business logic and takes the pain out of PHP object persistence.

FormalPara Don’t worry!

We run through Doctrine 2 quickly in this chapter, but we will look into each individual aspect of it in depth later in the book.

Installation

The easiest way to install Doctrine 2 is by using “Composer,” which can be downloaded from its website.Footnote 1 Store the PHP archive (phar) file in the root directory of the application. Then, create a file called composer.json with the following contents (you may need to adjust the version number given):

1   { 2        "require": { 3                "doctrine/orm": "2.3.1" 4         } 5   }

In the root directory of the application, execute the command

1   $ php composer.phar install

to download Doctrine 2 to the vendor subfolder . Composer makes it available to the application by configuring autoloading for its classes.

If you receive a composer error or warning, you may want to update composer itself to its latest version first by running

1   $ php composer.phar self-update

You then only need to add

1   <?php 2   // [..] 3   if (file_exists('vendor/autoload.php')) { 4       $loader = include 'vendor/autoload.php'; 5   }

to the index.php file. Once downloaded, all Doctrine 2 classes are loaded automatically on demand. Nice!

A First Entity

Based on the first section of this book , in which we developed our own little ORM system, we now can radically simplify the code needed by adding Doctrine 2 annotations to the User entity:

  1   <?php   2   namespace Entity;   3      4   /**   5    * @Entity   6    * @Table(name="users")   7    */   8   class User   9   {  10           /**  11           * @Id @Column(type="integer")  12           * @GeneratedValue  13           */  14           private $id;  15     16           /** @Column(type="string", name="first_name", nullable=true) */  17           private $firstName;  18     19           /** @Column(type="string", name="last_name", nullable=true) */  20           private $lastName;  21     22           /** @Column(type="string", nullable=true) */  23           private $gender;  24     25           /** @Column(type="string", name="name_prefix", nullable=true)  */  26           private $namePrefix;  27     28           const GENDER_MALE = 0;  29           const GENDER_FEMALE = 1;  30     31           const GENDER_MALE_DISPLAY_VALUE = "Mr.";  32           const GENDER_FEMALE_DISPLAY_VALUE = "Ms.";  33  34           public function assembleDisplayName()  35           {  36                   $displayName = '';  37     38                   if ($this->gender == self::GENDER_MALE) {  39                           $displayName .= self::GENDER_MALE_DISPLAY_VALUE;  40                   } elseif ($this->gender == self::GENDER_FEMALE) {  41                           $displayName .= self::GENDER_FEMALE_DISPLAY_VALUE;  42                   }  43     44                   if ($this->namePrefix) {  45                           $displayName .= ' ' . $this->namePrefix;  46                   }  47     48                   $displayName .= ' ' . $this->firstName . ' ' . $this->lastName;  49     50                   return $displayName;  51           }  52     53           public function setFirstName($firstName)  54           {  55                   $this->firstName = $firstName;  56           }  57     58           public function getFirstName()  59           {  60                   return $this->firstName;  61           }  62     63           public function setGender($gender)  64           {  65                   $this->gender = $gender;  66           }  67     68           public function getGender()  69           {  70                   return $this->gender;  71           }  72     73           public function setId($id)  74           {  75                   $this->id = $id;  76           }  77     78           public function getId()  79           {  80                   return $this->id;  81           }  82     83           public function setLastName($lastName)  84           {  85                   $this->lastName = $lastName;  86           }  87     88           public function getLastName()  89           {  90                   return $this->lastName;  91           }  92     93           public function setNamePrefix($namePrefix)  94           {  95                   $this->namePrefix = $namePrefix;  96           }  97     98           public function getNamePrefix()  99           { 100                   return $this->namePrefix; 101           } 102   }

The code in index.php now can be changed to the following lines of code to read and modify entities using Doctrine 2:

 1   <?php  2   include '../entity/User.php';  3   include '../vendor/autoload.php';  4     5   use Doctrine\ORM\Tools\Setup;  6   use Doctrine\ORM\EntityManager;  7     8   $paths = array(__DIR__ . "/../entity/");  9   $isDevMode = true; 10    11   $dbParams = array( 12           'driver' => 'pdo_mysql', 13           'user' => 'root', 14           'password' => '', 15           'dbname' => 'app', 16   ); 17    18   $config = Setup::createAnnotationMetadataConfiguration($paths, $isDevMode); 19   $em = EntityManager::create($dbParams, $config); 20   $user = $em->getRepository('Entity\User')->findOneById(1); 21   echo $user->assembleDisplayName() . '<br />'; 22   $user->setFirstname('Moritz'); 23   $em->persist($user); 24   $em->flush();

Please note that we replaced our custom entity manager with the one provided by Doctrine as well as the user repository with it findOneById method. All this code is available out-of-the-box. The only custom code still needed is the user entity definition.

That’s it! Easy!

A First Association

Dealing with the association is easy as well. The User entity has to be changed to:

 1   <?php  2   namespace Entity;  3     4   /**  5    * @Entity  6    * @Table(name="users")  7    */  8   class User  9   { 10           // [..] 11    12          /** 13          * @OneToMany(targetEntity="Entity\Post", mappedBy="user") 14          */ 15          private $posts; 16    17          // [..] 18    19          public function __construct() 20          { 21                  $this->posts = new \Doctrine\Common\Collections\ArrayCollection(); 22          } 23    24          // [..] 25   }

Now add some more annotations to the Post entity:

 1   <?php  2   namespace Entity;  3  4   /**  5   * @Entity  6   * @Table(name="posts")  7   */  8   class Post  9   { 10           /** 11           * @Id @Column(type="integer") 12           * @GeneratedValue 13           */ 14           private $id; 15    16           /** 17           * @ManyToOne(targetEntity="Entity\User", inversedBy="posts") 18           * @JoinColumn(name="user_id", referencedColumnName="id") 19           */ 20           private $user; 21    22           /** @Column(type="string") */ 23           private $title; 24    25           /** @Column(type="string") */ 26           private $content; 27    28           public function setUserId($user_id) 29           { 30                   $this->user_id = $user_id; 31           } 32    33           public function getUserId() 34           { 35                   return $this->user_id; 36           } 37    38           public function setContent($content) 39           { 40                   $this->content = $content; 41           } 42    43           public function getContent() 44           { 45                   return $this->content; 46           } 47    48           public function setId($id) 49           { 50                   $this->id = $id; 51           } 52    53           public function getId() 54           { 55                   return $this->id; 56           } 57    58           public function setTitle($title) 59           { 60                   $this->title = $title; 61           } 62    63           public function getTitle() 64           { 65                   return $this->title; 66           } 67   }

Once the User is loaded, you can iterate over the User’s posts as before:

 1   <?php  2   // [..]  3   $user = $em->getRepository('Entity\User')->findOneById(1);  4   ?>  5   <h1> <?echo $user->assembleDisplayName(); ?> </h1>  6   <ul>  7           <?php foreach($user->getPosts() as $post) {?>  8                   <li> <?php echo $post->getTitle(); ?> </li>  9           <?php } ?> 10   </ul>

In fact, with Doctrine 2 included, we don’t need most of the code we wrote to implement the exact same functionality by hand:

1   entity/ 2           Post.php 3           User.php 4   public/ 5           index.php

It couldn’t be much easier—thank you, Doctrine 2! Before we add more features to our demo app, we will now look at some basic concepts of Doctrine 2.

Core Concepts at a Glance

Before we take a deep dive into the details of Doctrine 2, let’s step back and look at its core concepts.

We already defined an entity as an object with identity that is managed by Doctrine 2 and persisted in a database. Entities are the domain objects of an application. Saving entities and retrieving them from the database is essential to an application. The term “entity” is used with two different meanings throughout this book: on one hand, it is used for single, persistent object; on the other hand, it is used for PHP classes that act as templates for persistent objects. With Doctrine 2, a persistent object, an entity, is always in a specific state. This state can be “NEW,” “MANAGED,” “DETACHED,” or “REMOVED.” We will learn about this later in the book.

The entity manager is Doctrine’s core object. As an application developer, one does interact a lot with the entity manager. The entity manager is responsible for persisting new entities, and also takes care of updating and deleting them. Repositories are retrieved via the entity manager.

Simply speaking, a repository is a container for all entities of a specific type. It allows you to look-up entities via so-called finder methods or finders. Some finders are available by default, others may be added manually if needed.

Doctrine’s database access layer (DBAL) is the foundation for the Doctrine 2 ORM library. It’s a stand-alone component that can be used even without using Doctrine’s ORM capabilities. Technically, DBAL is a wrapper for PHP’s PHP data objects (PDO) extension that makes dealing with databases even easier compared to PHP alone. And again, Doctrine 2 ORM makes dealing with databases even easier than with DBAL alone.

Summary

What a ride! By simply pulling in Doctrine as a persistence library into our application, we could already reduce the complexity of our application code dramatically. It’s now time to explorer all the powerful features of Doctrine in more detail!