Doctrine 2.0
           Enterprise Object Relational Mapper for PHP 5.3




Doctrine 2.0         www.doctrine-project.org   www.sensiolabs.org
Jonathan H. Wage
•   Web developer for over a decade
•   Tech junky for even longer
•   Open Source Evangelist
•   Published Author
•   Contributes to...
•   ...Doctrine
•   ...Symfony
•   ...and more
•   Works full-time for Sensio Labs



     Doctrine 2.0     www.doctrine-project.org   www.sensiolabs.org
Jonathan H. Wage
                  • http://www.twitter.com/jwage
                  • http://www.jwage.com
                  • http://www.facebook.com/jwage

               You can contact Jonathan about Doctrine and Open-Source or for
               training, consulting, application development, or business related
                            questions at jonathan.wage@sensio.com




Doctrine 2.0                 www.doctrine-project.org   www.sensiolabs.org
Project History
• First commit April 13th 2006

• First stable version finished and Released September
  1st 2008

• One of the first ORM implementations for PHP

• 1.0 is First LTS(long term support) release.
  Maintained until March 1st 2010

• Integrated with many popular frameworks: Symfony,
  Zend Framework, Code Igniter, etc.
Doctrine 2.0     www.doctrine-project.org   www.sensiolabs.org
The Team
                         • Roman S. Borschel

                         • Guilherme Blanco

                         • Benjamin Eberlei

                         • Bulat Shakirzyanov

                         • Jonathan H. Wage




Doctrine 2.0   www.doctrine-project.org   www.sensiolabs.org
Object Relational Mapper
                                 What is it?




Doctrine 2.0   www.doctrine-project.org        www.sensiolabs.org
Object-relational mapping (ORM, O/RM, and O/R
mapping) in computer software is a programming
technique for converting data between incompatible type
systems in object-oriented programming languages. This
creates, in effect, a "virtual object database" that can be
used from within the programming language. There are
both free and commercial packages available that
perform object-relational mapping, although some
programmers opt to create their own ORM tools.




Doctrine 2.0    www.doctrine-project.org   www.sensiolabs.org
Doctrine1 vs Doctrine2



Doctrine 2.0   www.doctrine-project.org   www.sensiolabs.org
The Doctrine1 Way

               class User extends Doctrine_Record
               {
                   public function setTableDefinition()
                   {
                       $this->hasColumn('username', 'string');
                       $this->hasColumn('password', 'string');
                   }
               }




Doctrine 2.0            www.doctrine-project.org   www.sensiolabs.org
The Doctrine1 Way

     $user = new User();
     $user->setUsername('jwage');
     $user->setPassword('password');
     $user->save();




Doctrine 2.0      www.doctrine-project.org   www.sensiolabs.org
Problems
• Domain is bound to persistence layer
• Complexity of domain entities limited by
    persistence layer

• $model->save() is a bottle neck for persisting
    large numbers of objects

• Testing your domain model requires mocking
    the persistence layer

Doctrine 2.0    www.doctrine-project.org   www.sensiolabs.org
The Doctrine2 Way
                  How does Doctrine2 do it?




Doctrine 2.0      www.doctrine-project.org   www.sensiolabs.org
No base class to extend



Doctrine 2.0   www.doctrine-project.org   www.sensiolabs.org
/**
                 * @Entity
                 * @Table(name="users")
                 */
               class User
               {
                    /**
                     * @Id
                     */
                    private $id;

                   /**
                    * @Column(type="string", length=255)
                    */
                   private $username;

                   public function setUsername($username)
                   {
                       $this->username = $username;
                   }

                   // ...
               }




Doctrine 2.0       www.doctrine-project.org   www.sensiolabs.org
Mapping Information
      Specify your mapping information with XML, YAML or
                     DocBlock Annotations




Doctrine 2.0      www.doctrine-project.org   www.sensiolabs.org
YAML
               User:
                 type: entity
                 table: users
                 fields:
                   id:
                     type: integer
                     id: true
                     generator:
                       strategy: AUTO
                   username:
                     type: string(255)
                   password:
                     type: string(255)



Doctrine 2.0     www.doctrine-project.org   www.sensiolabs.org
XML

         <?xml version="1.0" encoding="utf-8"?>
         <doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-
         mapping" xsi="http://www.w3.org/2001/XMLSchema-instance" schemaLocation="http://
         doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
             <entity name="User" table="users">
                 <id name="id" type="integer" column="id">
                     <generator strategy="AUTO"/>
                 </id>

                 <field name="username" type="string" column="username" length="255" />
                 <field name="password" type="string" column="password" length="255">
             </entity>
         </doctrine-mapping>




Doctrine 2.0                www.doctrine-project.org      www.sensiolabs.org
PHP
         use DoctrineORMMappingClassMetadataInfo;

         $metadata->setInheritanceType(ClassMetadataInfo::INHERITANCE_TYPE_NONE);
         $metadata->setPrimaryTable(array(
             'name' => 'users',
         ));
         $metadata->mapField(array(
             'fieldName' => 'id',
             'type' => 'integer',
             'id' => true,
             'columnName' => 'id',
         ));
         $metadata->mapField(array(
             'fieldName' => 'username',
             'type' => 'string',
             'length' => '255',
             'columnName' => 'username',
         ));
         $metadata->mapField(array(
             'fieldName' => 'password',
             'type' => 'string',
             'length' => '255',
             'columnName' => 'password',
         ));
         $metadata->setIdGeneratorType(ClassMetadataInfo::GENERATOR_TYPE_AUTO);




Doctrine 2.0                  www.doctrine-project.org        www.sensiolabs.org
Transparent Persistence



Doctrine 2.0   www.doctrine-project.org   www.sensiolabs.org
$user = new User();
     $user->setUsername('jwage');
     $user->setPassword('password');

     $em->persist($user);
     $em->flush();



Doctrine 2.0   www.doctrine-project.org   www.sensiolabs.org
Single Flush Operation
• Batch inserts
• Best to build up all object changes in your
    application and flush no more than 1 or 2 times

• All managed objects are tracked and changesets
    generated on flush and used to persist to
    RDBMS, MongoDB, etc.


Doctrine 2.0   www.doctrine-project.org   www.sensiolabs.org
$user = $em->getRepository('User')
         ->findBy(array('username' => 'jwage'));

     $user->setPassword('newpassword');

     // Issues SQL update setting the new password
     $em->flush();




Doctrine 2.0     www.doctrine-project.org   www.sensiolabs.org
Why Transparency?
• Better performance
• Easier to cache regular PHP objects that are
    not bound to the persistence layer

• Easier to test your domain, no mocking
    required

• Ultimate flexibility over the complexity of your
    domain entities without being imposed on by
    your persistence layer
Doctrine 2.0      www.doctrine-project.org   www.sensiolabs.org
Performance
• Less abstraction on domain entities improves
    performance when working with them.

• Physical accessors and getters are faster.
• Lighter and no required dependencies.



Doctrine 2.0   www.doctrine-project.org   www.sensiolabs.org
PHP 5.3



Doctrine 2.0   www.doctrine-project.org   www.sensiolabs.org
PHP 5.3 is Fast



Doctrine 2.0    www.doctrine-project.org   www.sensiolabs.org
Doctrine1
      Doctrine1 benefits from using PHP 5.3. Test suite uses
             30% less memory and runs 20% faster




Doctrine 2.0       www.doctrine-project.org   www.sensiolabs.org
Doctrine2 Performance

                  Doctrine 1.1
                  4.3 seconds for 5000 records

                  Doctrine 2.0
                  1.4 seconds for 5000 records

                  Doctrine 2.0
                  3.5 seconds for 10000 records




Doctrine 2.0   www.doctrine-project.org   www.sensiolabs.org
Uses Namespaces



Doctrine 2.0     www.doctrine-project.org   www.sensiolabs.org
Namespaces
     namespace DoctrineORM;

     use DoctrineORMQueryExpr,
         DoctrineCommonDoctrineException;

     /**
       * This class is responsible for building DQL query strings via an object oriented
       * PHP interface.
       *
       * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
       * @link    www.doctrine-project.org
       * @since   2.0
       * @version $Revision$
       * @author Guilherme Blanco <guilhermeblanco@hotmail.com>
       * @author Jonathan Wage <jonwage@gmail.com>
       * @author Roman Borschel <roman@code-factory.org>
       */
     class QueryBuilder
     {
          // ...




Doctrine 2.0                   www.doctrine-project.org        www.sensiolabs.org
Using Namespaces

                use DoctrineORMQueryBuilder;

                $qb = new QueryBuilder($em);
                $qb->select('u')
                   ->from('ModelsUser', 'u')
                   ->orderBy('u.username', 'ASC');

                $q = $qb->getQuery();
                $users = $q->execute();




Doctrine 2.0       www.doctrine-project.org   www.sensiolabs.org
Follow PHP 5.3
               Interoperability
                  Standards
http://groups.google.com/group/php-standards




Doctrine 2.0     www.doctrine-project.org   www.sensiolabs.org
Benefits
• PHP libraries will be able to be mixed without
    autoloader issues and conflict.

• Symfony2 + Doctrine2 + ZendFramework2
• Load classes from 3 different libraries with one
    autoloader and one include path



Doctrine 2.0    www.doctrine-project.org   www.sensiolabs.org
Doctrine code is
               divided into a few
               different packages

Doctrine 2.0      www.doctrine-project.org   www.sensiolabs.org
DoctrineCommon
• Common functionality shared across packages
• Events
• Annotations Library
• Lexer Parser that is used for parsing the
     Doctrine Query Language (DQL)

• Other various convenience tools
Doctrine 2.0      www.doctrine-project.org   www.sensiolabs.org
DoctrineDBAL

• Relational database abstraction layer
•    MySQL
•    Sqlite
•    PostgreSQL
•    Oracle
•    MsSQL



Doctrine 2.0    www.doctrine-project.org   www.sensiolabs.org
DBAL is Standalone
                 Use the DBAL without the ORM




Doctrine 2.0       www.doctrine-project.org   www.sensiolabs.org
DBAL History
 • The Doctrine DBAL is a very powerful
      project in itself

 • Based off of code forked from other
      projects

 • PEAR MDB2
 • Zend_Db

Doctrine 2.0     www.doctrine-project.org   www.sensiolabs.org
DBAL
• Using the Doctrine DBAL standalone is a good
    option for your PHP projects if a fully featured
    ORM is not needed

• API for performing DDL statements, executing
    queries, etc.




Doctrine 2.0        www.doctrine-project.org   www.sensiolabs.org
DBAL Example

               $params = array(
                   'driver' => 'pdo_mysql',
                   'host' => 'localhost',
                   'user' => 'root',
                   'password' => '',
                   'dbname' => 'doctrine2dbal'
               );
               $conn = DoctrineDBALDriverManager::getConnection($params);




    Execute queries against your database using $conn. The
    API is very similar to that of PDO and adds some extra
                          conveniences.

Doctrine 2.0                www.doctrine-project.org   www.sensiolabs.org
Schema Manager
                $sm = $conn->getSchemaManager();




Doctrine 2.0      www.doctrine-project.org   www.sensiolabs.org
Schema Manager
• Issue DDL statements through intuitive
    API: createTable(), dropTable(),
    createForeignKey(), etc.

• Introspect your database schema as well:
    listTables(), listTableColumns(), listUsers(),
    etc.

Doctrine 2.0     www.doctrine-project.org   www.sensiolabs.org
Drop and Create Table
               $columns = array(
                   'id' => array(
                       'type' => Type::getType('integer'),
                       'autoincrement' => true,
                       'primary' => true,
                       'notnull' => true
                   ),
                   'name' => array(
                       'type' => Type::getType('string'),
                       'length' => 255
                   ),
               );

               $sm->dropAndCreateTable('user', $columns);




Doctrine 2.0            www.doctrine-project.org   www.sensiolabs.org
Create Foreign Key
                 $definition = array(
                     'name' => 'user_id_fk',
                     'local' => 'user_id',
                     'foreign' => 'id',
                     'foreignTable' => 'user'
                 );
                 $sm->createForeignKey('profile', $definition);




Doctrine 2.0          www.doctrine-project.org   www.sensiolabs.org
DoctrineORM

• Object Relational Mapper
• Built on top of the DBAL
• Provides transparent domain object persistence
     to relational databases




Doctrine 2.0    www.doctrine-project.org   www.sensiolabs.org
Persisting to Other
                   Databases


Doctrine 2.0      www.doctrine-project.org   www.sensiolabs.org
DoctrineODMMongoDB
               Doctrine Object Document Mapper (ODM) for
                                MongoDB




Doctrine 2.0            www.doctrine-project.org   www.sensiolabs.org
DoctrineODMMongoDB

• Provides transparent domain object persistence
     to MongoDB
•    Same architecture and design as the ORM

• New addition to the Doctrine project
• Umbrella of project widened to offer Doctrine
     style persistence layers for other popular open
     source databases.


Doctrine 2.0    www.doctrine-project.org   www.sensiolabs.org
Architecture
        The architecture of the Doctrine object persistence
                               layers




Doctrine 2.0        www.doctrine-project.org   www.sensiolabs.org
The ObjectManager
                EntityManager and DocumentManager




Doctrine 2.0         www.doctrine-project.org   www.sensiolabs.org
API
     Simple API for managing the persistent state of objects




Doctrine 2.0       www.doctrine-project.org   www.sensiolabs.org
Object States
• A NEW object instance has no persistent identity, and is not yet
    associated with a ObjectManager (i.e. those just created with the "new"
    operator).

• A MANAGED object instance is an instance with a persistent identity
    that is associated with an ObjectManager and whose persistence is thus
    managed.

• A DETACHED object instance is an instance with a persistent
    identity that is not (or no longer) associated with an ObjectManager.

• A REMOVED object instance is an instance with a persistent identity,
    associated with an ObjectManager, that will be removed from the
    database upon transaction commit.



Doctrine 2.0         www.doctrine-project.org   www.sensiolabs.org
Creating EntityManager
     $config = new DoctrineORMConfiguration();
     $config->setMetadataCacheImpl(new DoctrineCommonCacheArrayCache);
     $driverImpl = $config->newDefaultAnnotationDriver(array(__DIR__."/Entities"));
     $config->setMetadataDriverImpl($driverImpl);

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

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

     $em = DoctrineORMEntityManager::create($connectionOptions, $config);




Doctrine 2.0             www.doctrine-project.org   www.sensiolabs.org
Creating DocumentManager

     $config = new DoctrineODMMongoDBConfiguration();
     $config->setMetadataCacheImpl(new DoctrineCommonCacheArrayCache);
     $driverImpl = $config->newDefaultAnnotationDriver(array(__DIR__."/Documents"));
     $config->setMetadataDriverImpl($driverImpl);

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

     $mongo = new DoctrineODMMongoDBMongo();

     $dm = DoctrineORMDocumentManager::create($mongo, $config);




 Doctrine 2.0            www.doctrine-project.org   www.sensiolabs.org
Managing the State



Doctrine 2.0      www.doctrine-project.org   www.sensiolabs.org
Persisting
        $user = new User();
        $user->setUsername('jwage');
        $user->setPassword('password');

        // Persist the new object so it is
        // saved to the database
        $em->persist($user);
        $em->flush();




Doctrine 2.0     www.doctrine-project.org   www.sensiolabs.org
Updating

               $user = $em->getRepository('User')
                   ->find(array('username' => 'jwage'));

               // modify the already managed object
               $user->setPassword('changed');
               $em->flush(); // issues update




Doctrine 2.0           www.doctrine-project.org   www.sensiolabs.org
Removing

               $user = $em->getRepository('User')
                   ->find(array('username' => 'jwage'));

               // schedule for deletion
               $em->remove($user);
               $em->flush(); // issues delete




Doctrine 2.0           www.doctrine-project.org   www.sensiolabs.org
Detaching
           $user = $em->getRepository('User')
               ->find(array('username' => 'jwage'));

           $user->setUsername('changed');

           // detach the object
           $em->detach($user);

           $em->flush(); // does nothing




Doctrine 2.0        www.doctrine-project.org   www.sensiolabs.org
Doctrine Query Language
                  The killer ORM feature




Doctrine 2.0   www.doctrine-project.org   www.sensiolabs.org
Propietary Object
                Query Language


Doctrine 2.0      www.doctrine-project.org   www.sensiolabs.org
Parsed by Recursive
                 Descent Parser


Doctrine 2.0       www.doctrine-project.org   www.sensiolabs.org
Constructs AST



Doctrine 2.0     www.doctrine-project.org   www.sensiolabs.org
AST = Abstract Syntax Tree



Doctrine 2.0   www.doctrine-project.org   www.sensiolabs.org
Tree representation of the
       syntactical structure of a
           source language


Doctrine 2.0   www.doctrine-project.org   www.sensiolabs.org
PHP class names of DQL
         parser directly represent
            the language itself
               • OrderByClause.php
               • SelectClause.php
               • SelectExpression.php
               • Subselect.php
               • DeleteClause.php
Doctrine 2.0     www.doctrine-project.org   www.sensiolabs.org
• Easy to use
  • Easy to expand and add new features
  • Easy to use and understand the
       parsing of a DQL string

  • Expand the DQL parser with your
       own functionality and add to the
       DQL language
Doctrine 2.0   www.doctrine-project.org   www.sensiolabs.org
Parsing of DQL is Cached
     In a production environment a DQL query is only ever
      parsed and transformed to SQL once so performance
                         is not an issue.




Doctrine 2.0      www.doctrine-project.org   www.sensiolabs.org
Sample DQL Query

                $query = $em->createQuery(
                    'SELECT u, g, FROM User u ' .
                    'LEFT JOIN u.Groups g ' .
                    'ORDER BY u.name ASC, g.name ASC'
                );
                $users = $query->execute();




Doctrine 2.0         www.doctrine-project.org   www.sensiolabs.org
QueryBuilder API

               $qb = $em->createQueryBuilder()
                   ->select('u, g')
                   ->from('User', 'u')
                   ->leftJoin('u.Groups', 'g')
                   ->orderBy('u.name', 'ASC')
                   ->addOrderBy('g.name', 'ASC');

               $query = $qb->getQuery();




Doctrine 2.0        www.doctrine-project.org   www.sensiolabs.org
Executing Queries

               $users = $query->execute();

               foreach ($users as $user) {
                   // ...
                   foreach ($user->getGroups() as $group) {
                       // ...
                   }
               }




Doctrine 2.0           www.doctrine-project.org   www.sensiolabs.org
Result Cache
                $query = $em->createQuery('SELECT u FROM ModelsUser u');
                $query->useResultCache(true, 3600, 'user_query');
                $users = $query->execute();




Execute query again and the results are retrieved from the cache


                                  $users = $query->execute();




 Doctrine 2.0              www.doctrine-project.org    www.sensiolabs.org
Symfony2 Integration
           The Doctrine features are tightly integrated with
                             Symfony2




Doctrine 2.0          www.doctrine-project.org   www.sensiolabs.org
Enabling the DBAL
           Enable the DBAL in your application configuration


                       # config/config.yml

                       doctrine.dbal:
                           driver:   PDOMySql
                           dbname:   Symfony2
                           user:     root
                           password: null




Doctrine 2.0          www.doctrine-project.org   www.sensiolabs.org
Using the DBAL
               Use the DBAL connection in your controllers


    class UserController extends Controller
    {
        public function indexAction()
        {
            $conn = $this->container->getService('database_connection');

               $users = $conn->fetchAll('SELECT * FROM users');
          }
    }




Doctrine 2.0             www.doctrine-project.org   www.sensiolabs.org
Enabling the ORM
           Enable the ORM in your application configuration




                      # config/config.yml

                      doctrine.orm: ~




Doctrine 2.0         www.doctrine-project.org   www.sensiolabs.org
Creating an Entity
               Application/HelloBundle/Entity/User.php


               namespace ApplicationHelloBundleEntity;

               /**
                 * @Entity
                 */
               class User
               {
                    /** @Id */
                    public $id;

                   /** @Column(type="string", length="255") */
                   public $username;

                   /** @Column(type="string", length="255") */
                   public $password;
               }




Doctrine 2.0           www.doctrine-project.org   www.sensiolabs.org
Create database schema


               $ php hello/console doctrine:schema:create




Doctrine 2.0          www.doctrine-project.org   www.sensiolabs.org
Using the ORM
               Use the ORM EntityManager in your controllers

   use ApplicationHelloBundleEntitiesUser;

   class UserController extends Controller
   {
       public function createAction()
       {
           $user = new User();
           $user->setName('Jonathan H. Wage');

                $em = $this->container->getService('doctrine.orm.entity_manager');
                $em->persist($user);
                $em->flush();

                // ...
         }
   }




Doctrine 2.0               www.doctrine-project.org   www.sensiolabs.org
Conclusion
                       Why use an ORM?




Doctrine 2.0   www.doctrine-project.org   www.sensiolabs.org
Encapsulation
        Encapsulate all your domain logic in one place, in an
                         object oriented way




Doctrine 2.0        www.doctrine-project.org   www.sensiolabs.org
Maintainability
        The organization of the domain logic in an OO way
                     improves maintainability




Doctrine 2.0       www.doctrine-project.org   www.sensiolabs.org
Testability
           Keeping a clean OO domain model makes your
          business logic easily testable for improved stability




Doctrine 2.0          www.doctrine-project.org   www.sensiolabs.org
Portability
    Write portable and thin application controller code and
                         fat models.




Doctrine 2.0      www.doctrine-project.org   www.sensiolabs.org
Questions?
                 Jonathan H. Wage
                 jonwage@gmail.com
                 http://www.twitter.com/jwage
                 +1 615 513 9185

                 sensiolabs.com | doctrine-project.org | jwage.com



     You can contact Jonathan about Doctrine and Open-Source or for
      training, consulting, application development, or business related
                   questions at jonathan.wage@sensio.com




Doctrine 2.0          www.doctrine-project.org   www.sensiolabs.org

Libertyvasion2010

  • 1.
    Doctrine 2.0 Enterprise Object Relational Mapper for PHP 5.3 Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 2.
    Jonathan H. Wage • Web developer for over a decade • Tech junky for even longer • Open Source Evangelist • Published Author • Contributes to... • ...Doctrine • ...Symfony • ...and more • Works full-time for Sensio Labs Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 3.
    Jonathan H. Wage • http://www.twitter.com/jwage • http://www.jwage.com • http://www.facebook.com/jwage You can contact Jonathan about Doctrine and Open-Source or for training, consulting, application development, or business related questions at jonathan.wage@sensio.com Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 4.
    Project History • Firstcommit April 13th 2006 • First stable version finished and Released September 1st 2008 • One of the first ORM implementations for PHP • 1.0 is First LTS(long term support) release. Maintained until March 1st 2010 • Integrated with many popular frameworks: Symfony, Zend Framework, Code Igniter, etc. Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 5.
    The Team • Roman S. Borschel • Guilherme Blanco • Benjamin Eberlei • Bulat Shakirzyanov • Jonathan H. Wage Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 6.
    Object Relational Mapper What is it? Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 7.
    Object-relational mapping (ORM,O/RM, and O/R mapping) in computer software is a programming technique for converting data between incompatible type systems in object-oriented programming languages. This creates, in effect, a "virtual object database" that can be used from within the programming language. There are both free and commercial packages available that perform object-relational mapping, although some programmers opt to create their own ORM tools. Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 8.
    Doctrine1 vs Doctrine2 Doctrine2.0 www.doctrine-project.org www.sensiolabs.org
  • 9.
    The Doctrine1 Way class User extends Doctrine_Record { public function setTableDefinition() { $this->hasColumn('username', 'string'); $this->hasColumn('password', 'string'); } } Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 10.
    The Doctrine1 Way $user = new User(); $user->setUsername('jwage'); $user->setPassword('password'); $user->save(); Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 11.
    Problems • Domain isbound to persistence layer • Complexity of domain entities limited by persistence layer • $model->save() is a bottle neck for persisting large numbers of objects • Testing your domain model requires mocking the persistence layer Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 12.
    The Doctrine2 Way How does Doctrine2 do it? Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 13.
    No base classto extend Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 14.
    /** * @Entity * @Table(name="users") */ class User { /** * @Id */ private $id; /** * @Column(type="string", length=255) */ private $username; public function setUsername($username) { $this->username = $username; } // ... } Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 15.
    Mapping Information Specify your mapping information with XML, YAML or DocBlock Annotations Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 16.
    YAML User: type: entity table: users fields: id: type: integer id: true generator: strategy: AUTO username: type: string(255) password: type: string(255) Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 17.
    XML <?xml version="1.0" encoding="utf-8"?> <doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine- mapping" xsi="http://www.w3.org/2001/XMLSchema-instance" schemaLocation="http:// doctrine-project.org/schemas/orm/doctrine-mapping.xsd"> <entity name="User" table="users"> <id name="id" type="integer" column="id"> <generator strategy="AUTO"/> </id> <field name="username" type="string" column="username" length="255" /> <field name="password" type="string" column="password" length="255"> </entity> </doctrine-mapping> Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 18.
    PHP use DoctrineORMMappingClassMetadataInfo; $metadata->setInheritanceType(ClassMetadataInfo::INHERITANCE_TYPE_NONE); $metadata->setPrimaryTable(array( 'name' => 'users', )); $metadata->mapField(array( 'fieldName' => 'id', 'type' => 'integer', 'id' => true, 'columnName' => 'id', )); $metadata->mapField(array( 'fieldName' => 'username', 'type' => 'string', 'length' => '255', 'columnName' => 'username', )); $metadata->mapField(array( 'fieldName' => 'password', 'type' => 'string', 'length' => '255', 'columnName' => 'password', )); $metadata->setIdGeneratorType(ClassMetadataInfo::GENERATOR_TYPE_AUTO); Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 19.
    Transparent Persistence Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 20.
    $user = newUser(); $user->setUsername('jwage'); $user->setPassword('password'); $em->persist($user); $em->flush(); Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 21.
    Single Flush Operation •Batch inserts • Best to build up all object changes in your application and flush no more than 1 or 2 times • All managed objects are tracked and changesets generated on flush and used to persist to RDBMS, MongoDB, etc. Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 22.
    $user = $em->getRepository('User') ->findBy(array('username' => 'jwage')); $user->setPassword('newpassword'); // Issues SQL update setting the new password $em->flush(); Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 23.
    Why Transparency? • Betterperformance • Easier to cache regular PHP objects that are not bound to the persistence layer • Easier to test your domain, no mocking required • Ultimate flexibility over the complexity of your domain entities without being imposed on by your persistence layer Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 24.
    Performance • Less abstractionon domain entities improves performance when working with them. • Physical accessors and getters are faster. • Lighter and no required dependencies. Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 25.
    PHP 5.3 Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 26.
    PHP 5.3 isFast Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 27.
    Doctrine1 Doctrine1 benefits from using PHP 5.3. Test suite uses 30% less memory and runs 20% faster Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 28.
    Doctrine2 Performance Doctrine 1.1 4.3 seconds for 5000 records Doctrine 2.0 1.4 seconds for 5000 records Doctrine 2.0 3.5 seconds for 10000 records Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 29.
    Uses Namespaces Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 30.
    Namespaces namespace DoctrineORM; use DoctrineORMQueryExpr, DoctrineCommonDoctrineException; /** * This class is responsible for building DQL query strings via an object oriented * PHP interface. * * @license http://www.opensource.org/licenses/lgpl-license.php LGPL * @link www.doctrine-project.org * @since 2.0 * @version $Revision$ * @author Guilherme Blanco <guilhermeblanco@hotmail.com> * @author Jonathan Wage <jonwage@gmail.com> * @author Roman Borschel <roman@code-factory.org> */ class QueryBuilder { // ... Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 31.
    Using Namespaces use DoctrineORMQueryBuilder; $qb = new QueryBuilder($em); $qb->select('u') ->from('ModelsUser', 'u') ->orderBy('u.username', 'ASC'); $q = $qb->getQuery(); $users = $q->execute(); Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 32.
    Follow PHP 5.3 Interoperability Standards http://groups.google.com/group/php-standards Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 33.
    Benefits • PHP librarieswill be able to be mixed without autoloader issues and conflict. • Symfony2 + Doctrine2 + ZendFramework2 • Load classes from 3 different libraries with one autoloader and one include path Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 34.
    Doctrine code is divided into a few different packages Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 35.
    DoctrineCommon • Common functionalityshared across packages • Events • Annotations Library • Lexer Parser that is used for parsing the Doctrine Query Language (DQL) • Other various convenience tools Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 36.
    DoctrineDBAL • Relational databaseabstraction layer • MySQL • Sqlite • PostgreSQL • Oracle • MsSQL Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 37.
    DBAL is Standalone Use the DBAL without the ORM Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 38.
    DBAL History •The Doctrine DBAL is a very powerful project in itself • Based off of code forked from other projects • PEAR MDB2 • Zend_Db Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 39.
    DBAL • Using theDoctrine DBAL standalone is a good option for your PHP projects if a fully featured ORM is not needed • API for performing DDL statements, executing queries, etc. Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 40.
    DBAL Example $params = array( 'driver' => 'pdo_mysql', 'host' => 'localhost', 'user' => 'root', 'password' => '', 'dbname' => 'doctrine2dbal' ); $conn = DoctrineDBALDriverManager::getConnection($params); Execute queries against your database using $conn. The API is very similar to that of PDO and adds some extra conveniences. Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 41.
    Schema Manager $sm = $conn->getSchemaManager(); Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 42.
    Schema Manager • IssueDDL statements through intuitive API: createTable(), dropTable(), createForeignKey(), etc. • Introspect your database schema as well: listTables(), listTableColumns(), listUsers(), etc. Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 43.
    Drop and CreateTable $columns = array( 'id' => array( 'type' => Type::getType('integer'), 'autoincrement' => true, 'primary' => true, 'notnull' => true ), 'name' => array( 'type' => Type::getType('string'), 'length' => 255 ), ); $sm->dropAndCreateTable('user', $columns); Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 44.
    Create Foreign Key $definition = array( 'name' => 'user_id_fk', 'local' => 'user_id', 'foreign' => 'id', 'foreignTable' => 'user' ); $sm->createForeignKey('profile', $definition); Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 45.
    DoctrineORM • Object RelationalMapper • Built on top of the DBAL • Provides transparent domain object persistence to relational databases Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 46.
    Persisting to Other Databases Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 47.
    DoctrineODMMongoDB Doctrine Object Document Mapper (ODM) for MongoDB Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 48.
    DoctrineODMMongoDB • Provides transparentdomain object persistence to MongoDB • Same architecture and design as the ORM • New addition to the Doctrine project • Umbrella of project widened to offer Doctrine style persistence layers for other popular open source databases. Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 49.
    Architecture The architecture of the Doctrine object persistence layers Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 50.
    The ObjectManager EntityManager and DocumentManager Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 51.
    API Simple API for managing the persistent state of objects Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 52.
    Object States • ANEW object instance has no persistent identity, and is not yet associated with a ObjectManager (i.e. those just created with the "new" operator). • A MANAGED object instance is an instance with a persistent identity that is associated with an ObjectManager and whose persistence is thus managed. • A DETACHED object instance is an instance with a persistent identity that is not (or no longer) associated with an ObjectManager. • A REMOVED object instance is an instance with a persistent identity, associated with an ObjectManager, that will be removed from the database upon transaction commit. Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 53.
    Creating EntityManager $config = new DoctrineORMConfiguration(); $config->setMetadataCacheImpl(new DoctrineCommonCacheArrayCache); $driverImpl = $config->newDefaultAnnotationDriver(array(__DIR__."/Entities")); $config->setMetadataDriverImpl($driverImpl); $config->setProxyDir(__DIR__ . '/Proxies'); $config->setProxyNamespace('Proxies'); $connectionOptions = array( 'driver' => 'pdo_sqlite', 'path' => 'database.sqlite' ); $em = DoctrineORMEntityManager::create($connectionOptions, $config); Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 54.
    Creating DocumentManager $config = new DoctrineODMMongoDBConfiguration(); $config->setMetadataCacheImpl(new DoctrineCommonCacheArrayCache); $driverImpl = $config->newDefaultAnnotationDriver(array(__DIR__."/Documents")); $config->setMetadataDriverImpl($driverImpl); $config->setProxyDir(__DIR__ . '/Proxies'); $config->setProxyNamespace('Proxies'); $mongo = new DoctrineODMMongoDBMongo(); $dm = DoctrineORMDocumentManager::create($mongo, $config); Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 55.
    Managing the State Doctrine2.0 www.doctrine-project.org www.sensiolabs.org
  • 56.
    Persisting $user = new User(); $user->setUsername('jwage'); $user->setPassword('password'); // Persist the new object so it is // saved to the database $em->persist($user); $em->flush(); Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 57.
    Updating $user = $em->getRepository('User') ->find(array('username' => 'jwage')); // modify the already managed object $user->setPassword('changed'); $em->flush(); // issues update Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 58.
    Removing $user = $em->getRepository('User') ->find(array('username' => 'jwage')); // schedule for deletion $em->remove($user); $em->flush(); // issues delete Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 59.
    Detaching $user = $em->getRepository('User') ->find(array('username' => 'jwage')); $user->setUsername('changed'); // detach the object $em->detach($user); $em->flush(); // does nothing Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 60.
    Doctrine Query Language The killer ORM feature Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 61.
    Propietary Object Query Language Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 62.
    Parsed by Recursive Descent Parser Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 63.
    Constructs AST Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 64.
    AST = AbstractSyntax Tree Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 65.
    Tree representation ofthe syntactical structure of a source language Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 66.
    PHP class namesof DQL parser directly represent the language itself • OrderByClause.php • SelectClause.php • SelectExpression.php • Subselect.php • DeleteClause.php Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 67.
    • Easy touse • Easy to expand and add new features • Easy to use and understand the parsing of a DQL string • Expand the DQL parser with your own functionality and add to the DQL language Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 68.
    Parsing of DQLis Cached In a production environment a DQL query is only ever parsed and transformed to SQL once so performance is not an issue. Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 69.
    Sample DQL Query $query = $em->createQuery( 'SELECT u, g, FROM User u ' . 'LEFT JOIN u.Groups g ' . 'ORDER BY u.name ASC, g.name ASC' ); $users = $query->execute(); Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 70.
    QueryBuilder API $qb = $em->createQueryBuilder() ->select('u, g') ->from('User', 'u') ->leftJoin('u.Groups', 'g') ->orderBy('u.name', 'ASC') ->addOrderBy('g.name', 'ASC'); $query = $qb->getQuery(); Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 71.
    Executing Queries $users = $query->execute(); foreach ($users as $user) { // ... foreach ($user->getGroups() as $group) { // ... } } Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 72.
    Result Cache $query = $em->createQuery('SELECT u FROM ModelsUser u'); $query->useResultCache(true, 3600, 'user_query'); $users = $query->execute(); Execute query again and the results are retrieved from the cache $users = $query->execute(); Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 73.
    Symfony2 Integration The Doctrine features are tightly integrated with Symfony2 Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 74.
    Enabling the DBAL Enable the DBAL in your application configuration # config/config.yml doctrine.dbal: driver: PDOMySql dbname: Symfony2 user: root password: null Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 75.
    Using the DBAL Use the DBAL connection in your controllers class UserController extends Controller { public function indexAction() { $conn = $this->container->getService('database_connection'); $users = $conn->fetchAll('SELECT * FROM users'); } } Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 76.
    Enabling the ORM Enable the ORM in your application configuration # config/config.yml doctrine.orm: ~ Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 77.
    Creating an Entity Application/HelloBundle/Entity/User.php namespace ApplicationHelloBundleEntity; /** * @Entity */ class User { /** @Id */ public $id; /** @Column(type="string", length="255") */ public $username; /** @Column(type="string", length="255") */ public $password; } Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 78.
    Create database schema $ php hello/console doctrine:schema:create Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 79.
    Using the ORM Use the ORM EntityManager in your controllers use ApplicationHelloBundleEntitiesUser; class UserController extends Controller { public function createAction() { $user = new User(); $user->setName('Jonathan H. Wage'); $em = $this->container->getService('doctrine.orm.entity_manager'); $em->persist($user); $em->flush(); // ... } } Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 80.
    Conclusion Why use an ORM? Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 81.
    Encapsulation Encapsulate all your domain logic in one place, in an object oriented way Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 82.
    Maintainability The organization of the domain logic in an OO way improves maintainability Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 83.
    Testability Keeping a clean OO domain model makes your business logic easily testable for improved stability Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 84.
    Portability Write portable and thin application controller code and fat models. Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
  • 85.
    Questions? Jonathan H. Wage jonwage@gmail.com http://www.twitter.com/jwage +1 615 513 9185 sensiolabs.com | doctrine-project.org | jwage.com You can contact Jonathan about Doctrine and Open-Source or for training, consulting, application development, or business related questions at jonathan.wage@sensio.com Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org