Symfony and Doctrine
               What’s new in the Symfony and Doctrine Integration




Doctrine 2   www.doctrine-project.org    www.sensiolabs.com
Updated DoctrineBundle
• Doctrine2 features fully integrated
    – Database Abstraction Layer
    – Object Relational Mapper




Doctrine 2    www.doctrine-project.org   www.sensiolabs.com
DoctrineMongoDBBundle
• MongoDB Object Document Mapper
    – Transparent persistence to MongoDB
    – Same architecture as ORM
    – Map a class as an entity and document




Doctrine 2    www.doctrine-project.org   www.sensiolabs.com
DoctrineMigrationsBundle
• Integration with the database migrations
  project.

• Easily manage and deploy different versions
  of your database.

• Generate migrations when you change your
  schema mapping information



Doctrine 2    www.doctrine-project.org   www.sensiolabs.com
DBAL
• To use just the DBAL you must configure it:



                              doctrine.dbal:
                                dbname:   Symfony
                                user:     root
                                password: ~




Doctrine 2   www.doctrine-project.org     www.sensiolabs.com
DBAL
• If you need to specify multiple connections
  you can use the following syntax:
             doctrine.dbal:
               default_connection:             default
               connections:
                 default:
                   driver:                     PDOSqlite
                   dbname:                     Symfony
                   user:                       root
                   password:                   null
                   host:                       localhost
                   port:                       ~
                   path:                       %kernel.data_dir%/symfony.sqlite
                   event_manager_class:        DoctrineCommonEventManager
                   configuration_class:        DoctrineDBALConfiguration
                   wrapper_class:              ~
                   options:                    []
Doctrine 2         www.doctrine-project.org       www.sensiolabs.com
DBAL Console Commands
• Create all configured databases
       $ php console doctrine:database:create

• Create a specific database
       $ php console doctrine:database:create --connection=default



• Drop all configured databases
       $ php console doctrine:database:drop

• Drop a specific database
       $ php console doctrine:database:drop --connection=default


Doctrine 2       www.doctrine-project.org     www.sensiolabs.com
DBAL Console Commands
• Execute SQL queries
       $ php console doctrine:query:sql “SELECT * FROM user”



• Specify connection
       $ php console doctrine:query:sql “...” --connection=default




Doctrine 2       www.doctrine-project.org     www.sensiolabs.com
DBAL
• Get the default configured database
  connection:
             class MyController extends DoctrineController
             {
               public function indexAction()
               {
                 $conn = $this->getDatabaseConnection();

                       // ...
                 }
             }




Doctrine 2           www.doctrine-project.org     www.sensiolabs.com
DBAL
• Get a configured database connection service
  by its name:
             class MyController extends DoctrineController
             {
               public function indexAction()
               {
                 $conn = $this->getDatabaseConnection('default');

                     // ...
                 }
             }




Doctrine 2            www.doctrine-project.org     www.sensiolabs.com
ORM
• The EntityManager
         • Central place for persisting and retrieving entities
         • Multiple instances allowed
         • One EntityManager per database connection
               $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);



         • Dependency Injection handles the creation and
           management of entity manager services
Doctrine 2      www.doctrine-project.org                www.sensiolabs.com
ORM
• What is an Entity? It is a regular PHP object
  that has been mapped to the Doctrine2 ORM:
                                  /** @Entity */
                                  class User
                                  {
                                      /**
                                       * @Id @Column(type="integer")
                                       * @GeneratedValue
                                       */
                                      private $id;

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

                                        public function getId()
                                        {
                                            return $this->id;
                                        }

                                        public function getName()
                                        {
                                            return $this->name;
                                        }

                                        public function setName($name)
                                        {
                                            $this->name = $name;
                                        }
                                  }


Doctrine 2   www.doctrine-project.org                        www.sensiolabs.com
ORM
• No more magic in your domain
• Clean and testable
• Fast!
• Only limited by what you can do with PHP OO
  to design your domain
• Inheritance
• Use __construct() without any problems
• Entities are persisted transparently by the
  EntityManager

Doctrine 2   www.doctrine-project.org    www.sensiolabs.com
ORM
• Configure an entity manager to start using
  the ORM:

    doctrine.orm:
      default_entity_manager:             default
      cache_driver:                       apc              # array, apc, memcache, xcache
      entity_managers:
        default:
          connection:                     default




Doctrine 2     www.doctrine-project.org             www.sensiolabs.com
ORM
• Console commands implemented for
  improved developer workflow:




Doctrine 2   www.doctrine-project.org    www.sensiolabs.com
ORM
• Console commands implemented for
  improved developer workflow:
         •   Ensure production settings
         •   Clear metadata, query and result cache
         •   Load data fixtures
         •   Create and drop configured databases
         •   Generate entities from mapping information
         •   Generate new skeleton entities
         •   Generate skeleton entity repository classes
         •   Convert mapping information between formats
         •   Convert a Doctrine1 schema
         •   Import mapping information from an existing database
         •   Execute DQL and SQL queries
         •   Create, drop and update database schema from mapping information


Doctrine 2          www.doctrine-project.org    www.sensiolabs.com
ORM
• Get the default configured entity manager
  service:

             class MyController extends DoctrineController
             {
               public function indexAction()
               {
                 $em = $this->getEntityManager();

                     // ...
                 }
             }




Doctrine 2           www.doctrine-project.org    www.sensiolabs.com
ORM
• Get a configured entity manager service by its
  name:

             class MyController extends DoctrineController
             {
               public function indexAction()
               {
                 $em = $this->getEntityManager('default');

                     // ...
                 }
             }




Doctrine 2           www.doctrine-project.org    www.sensiolabs.com
ORM
• Persisting entities is as simple as creating the
  object and telling Doctrine to persist it:
             class MyController extends DoctrineController
             {
               public function createAction()
               {
                 $em = $this->getEntityManager();

                     $user = new User();
                     $user->setName('Jonathan H. Wage');
                     $em->persist($user);
                     $em->flush();

                     // ...
                 }
             }



Doctrine 2       www.doctrine-project.org    www.sensiolabs.com
ORM
• Creating Query instances and issue DQL
  queries to retrieve objects:
             class MyController extends DoctrineController
             {
               public function indexAction()
               {
                 $em = $this->getEntityManager();
                 $query = $em->createQuery('select u from MyBundle:User u');

                     $users = $query->execute();

                     // ...
                 }
             }




Doctrine 2            www.doctrine-project.org     www.sensiolabs.com
ORM
• Creating QueryBuilder instances to
  programatically build DQL queries through a
  fluent interface:
                      class MyController extends DoctrineController
                      {
                        public function indexAction()
                        {
                          $em = $this->getEntityManager();
                          $qb = $em->createQueryBuilder()
                            ->select('u')
                            ->from('MyBundle:User', 'u');

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

                              // ...
                          }
                      }

Doctrine 2   www.doctrine-project.org              www.sensiolabs.com
ORM
• Update your database schema during
  development as your domain model evolves

• Add a new column to our User entity
             /** @Entity */
             class User
             {
                 // ...

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



Doctrine 2   www.doctrine-project.org    www.sensiolabs.com
ORM
• Run update command to update your
  database schema from mapping information

       $ php console doctrine:schema:update



• The above compares your current database
  schema to your new mapping information and
  executes the necessary queries to bring your
  database up-to-date.


Doctrine 2      www.doctrine-project.org    www.sensiolabs.com
Object Document Mapper
• New Doctrine Project for persisting objects to
  MongoDB

• Same architecture as ORM

• Transparently persist PHP5 objects to
  MongoDB




Doctrine 2    www.doctrine-project.org   www.sensiolabs.com
MongoDB ODM
• The DocumentManager
         • Central place for persisting and retrieving documents
         • Multiple instances allowed
             $config = new Configuration();

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

             $reader = new AnnotationReader();
             $reader->setDefaultAnnotationNamespace('DoctrineODMMongoDBMapping');
             $config->setMetadataDriverImpl(new AnnotationDriver($reader, __DIR__ . '/Documents'));

             $dm = DocumentManager::create(new Mongo(), $config);



         • Dependency Injection handles the creation and
           management of document manager services


Doctrine 2        www.doctrine-project.org                www.sensiolabs.com
MongoDB ODM
• To use the MongoDB ODM you must configure
  it:

             doctrine_odm.mongodb:
               default_document_manager:       default
               cache_driver:                   array
               document_managers:
                 default:
                   connection:                 mongodb
               connections:
                 mongodb:
                   server:                     localhost/somedatabase




Doctrine 2       www.doctrine-project.org   www.sensiolabs.com
MongoDB ODM
• If the defaults are good enough for you then
  you can omit all the previous options:



             doctrine_odm.mongodb: ~




Doctrine 2     www.doctrine-project.org   www.sensiolabs.com
MongoDB ODM
• What is a Document? It is a regular PHP object
  that has been mapped to the MongoDB ODM:
                                        /** @Document */
                                        class User
                                        {
                                            /**
                                             * @Id
                                             */
                                            private $id;

                                            /** @String */
                                            private $name;

                                            public function getId()
                                            {
                                                return $this->id;
                                            }

                                            public function getName()
                                            {
                                                return $this->name;
                                            }

                                            public function setName($name)
                                            {
                                                $this->name = $name;
                                            }
                                        }


Doctrine 2   www.doctrine-project.org                        www.sensiolabs.com
MongoDB ODM
• Get the default configured document
  manager:

             class MyController extends DoctrineController
             {
               public function indexAction()
               {
                 $dm = $this->getDocumentManager();

                     // ...
                 }
             }




Doctrine 2           www.doctrine-project.org   www.sensiolabs.com
MongoDB ODM
• Get a configured document manager by its
  name:

             class MyController extends DoctrineController
             {
               public function indexAction()
               {
                 $dm = $this->getDocumentManager('default');

                     // ...
                 }
             }




Doctrine 2           www.doctrine-project.org   www.sensiolabs.com
MongoDB ODM
• Just like the ORM persisting documents is
  easy:
             class MyController extends DoctrineController
             {
               public function createAction()
               {
                 $dm = $this->getDocumentManager();

                     $user = new User();
                     $user->setName('Jonathan H. Wage');
                     $dm->persist($user);
                     $dm->flush();

                     // ...
                 }
             }



Doctrine 2       www.doctrine-project.org    www.sensiolabs.com
MongoDB ODM
• Change tracking
    – All objects are tracked in an identity map
    – Changesets are calculated on flush
    – Changesets are used to perform updates using
     the atomic operators
         • The following code results in an efficient mongo update
           with only the properties that need updated:

                                                               Array
                                                               (
 $user->setName('new name');
                                              ->
                                                                 [$set] => Array
                                                                 (
 $dm->flush();                                                      [name] => new name
                                                                 )
                                                               )


Doctrine 2     www.doctrine-project.org   www.sensiolabs.com
MongoDB ODM
• Traditional MongoDB find() and findOne()



             $users = $dm->find('User', $criteria);

             $query = $dm->findOne('User',
                 array('username' => 'jwage')
             );
             $user = $query->getSingleResult();




Doctrine 2    www.doctrine-project.org   www.sensiolabs.com
MongoDB ODM
• Query API for building MongoDB queries
  through a fluent OO interface:

             class MyController extends DoctrineController
             {
               public function indexAction()
               {
                 $dm = $this->getDocumentManager();
                 $query = $dm->createQuery('MyBundle:User');

                       $users = $query->execute();

                       // ...
                  }
             }


Doctrine 2       www.doctrine-project.org   www.sensiolabs.com
MongoDB ODM
• Using Query builder API
                       class MyController extends DoctrineController
                       {
                         public function indexAction()
                         {
                           $dm = $this->getDocumentManager();
                           $query = $dm->createQuery('User')
                               ->where('username', 'jwage');

                               $user = $query->getSingleResult();

                               // ...
                           }
                       }


• where(), whereIn(), whereMod(), whereNot(),
  etc.

Doctrine 2   www.doctrine-project.org              www.sensiolabs.com
MongoDB ODM
• Fluent Query interface generates and
  executes find() and findOne() methods
  internally

• Query information is collected via fluent oo
  interface and executed later




Doctrine 2   www.doctrine-project.org   www.sensiolabs.com
MongoDB ODM
• Document Query Language (DQL)
    – SQL like grammar for querying MongoDB

• Query types supported
    –    Find
    –    Insert
    –    Update
    –    Remove




Doctrine 2   www.doctrine-project.org   www.sensiolabs.com
MongoDB ODM
• Find query



             $query = $dm->query('find all User');
             $users = $query->execute();




Doctrine 2   www.doctrine-project.org   www.sensiolabs.com
MongoDB ODM
• Selecting fields




             $query = $dm->query('find username, password User');




Doctrine 2      www.doctrine-project.org   www.sensiolabs.com
MongoDB ODM
• $slice operator for paging embedded
  collections
         $query = $dm->query('find comments skip 20 limit 10 Post');

                                   Array
                                   (
                                       [comments] => Array
                                           (
                                               [$slice] => Array
                                                   (
                                                       [0] => 20
                                                       [1] => 10
                                                   )

                                           )

                                   )


Doctrine 2      www.doctrine-project.org           www.sensiolabs.com
MongoDB ODM
• Use atomic operators
             $query = $dm->query("update User set password =
             'changeme' where username = 'jwage'");



                             Array
                             (
                               [$set] => Array
                                  (
                                    [password] => changeme
                                  )

                             )

Doctrine 2         www.doctrine-project.org   www.sensiolabs.com
MongoDB ODM
• Complex update
             $query = $dm->query("update User inc count = 1, inc
             views = 2, set username = 'jwage'");

                                       Array
                                       (
                                           [$inc] => Array
                                               (
                                                   [count] => 1
                                                   [views] => 2
                                               )

                                              [$set] => Array
                                                  (
                                                      [username] => jwage
                                                  )

                                       )


Doctrine 2         www.doctrine-project.org                www.sensiolabs.com
MongoDB ODM
• Document Query Language (DQL)
    – atomic operators
    – skip and limit main results
    – skip and limit embedded documents
    – use dot notation for querying embedded
     documents
    – embed JSON values in your DQL syntax

       $query = $dm->query("update User pushAll groups = '[1, 2, 3]'");




Doctrine 2      www.doctrine-project.org   www.sensiolabs.com
Database Migrations
• New DoctrineMigrationsBundle contains
  integration with the Doctrine Database
  Migrations project

• Migrations have been completely re-written
  from Doctrine1 and are an extension of the
  database abstraction layer

             http://www.doctrine-project.org/projects/migrations



Doctrine 2   www.doctrine-project.org   www.sensiolabs.com
Database Migrations
• Migration classes:


             class Version20100416130401 extends AbstractMigration
             {
                 public function up(Schema $schema)
                 {

                     }

                     public function down(Schema $schema)
                     {

                     }
             }




Doctrine 2       www.doctrine-project.org    www.sensiolabs.com
Database Migrations
• Manually execute SQL for migrations:

   class Version20100416130422 extends AbstractMigration
   {
       public function up(Schema $schema)
       {
           $this->_addSql('CREATE TABLE addresses (id INT NOT NULL, street
   VARCHAR(255) NOT NULL, PRIMARY KEY(id)) ENGINE = InnoDB');
       }

             public function down(Schema $schema)
             {
                 $this->_addSql('DROP TABLE addresses');
             }
   }




Doctrine 2           www.doctrine-project.org   www.sensiolabs.com
Database Migrations
• Use API of Schema objects to perform
  migration:

             class Version20100416130401 extends AbstractMigration
             {
                 public function up(Schema $schema)
                 {
                     $table = $schema->createTable('users');
                     $table->addColumn('username', 'string');
                     $table->addColumn('password', 'string');
                 }

                    public function down(Schema $schema)
                    {
                        $schema->dropTable('users');
                    }
             }

Doctrine 2       www.doctrine-project.org   www.sensiolabs.com
Database Migrations
• Check migrations status:

 $ ./doctrine migrations:status

  == Configuration

      >>   Name:                                   Doctrine Sandbox Migrations
      >>   Configuration Source:                   /Users/jwage/Sites/doctrine2git/tools/sandbox/migrations.xml
      >>   Version Table Name:                     doctrine_migration_versions
      >>   Migrations Namespace:                   DoctrineMigrations
      >>   Migrations Directory:                   /Users/jwage/Sites/doctrine2git/tools/sandbox/DoctrineMigrations
      >>   Current Version:                        2010-04-16 13:04:22 (20100416130422)
      >>   Latest Version:                         2010-04-16 13:04:22 (20100416130422)
      >>   Executed Migrations:                    0
      >>   Available Migrations:                   1
      >>   New Migrations:                         1

  == Migration Versions

      >> 2010-04-16 13:04:01 (20100416130401)      not migrated




Doctrine 2              www.doctrine-project.org        www.sensiolabs.com
Database Migrations
• Execute migration dry runs:
             $ ./doctrine migrations:migrate --dry-run
             Are you sure you wish to continue?
             y
             Executing dry run of migration up to 20100416130452 from 0

              >> migrating 20100416130452

                 -> CREATE TABLE users (username VARCHAR(255) NOT NULL, password VARCHAR(255) NOT NULL) ENGINE = InnoDB




• Omit --dry-run to execute migration.




Doctrine 2                www.doctrine-project.org                   www.sensiolabs.com
Database Migrations
• Specify a version number to revert to or 0 to
  revert all migrations:
                                        $ ./doctrine migrations:migrate 0
                                        Are you sure you wish to continue?
                                        y
                                        Migrating down to 0 from 20100416130401

                                          -- reverting 20100416130401

                                            -> DROP TABLE users

                                          -- reverted




Doctrine 2   www.doctrine-project.org                        www.sensiolabs.com
Database Migrations
• Write migration SQL file instead of executing:
        $ ./doctrine migrations:migrate --write-sql
        Executing dry run of migration up to 20100416130401 from 0

             >> migrating 20100416130401

               -> CREATE TABLE users (username VARCHAR(255) NOT NULL, password VARCHAR(255) NOT NULL) ENGINE = InnoDB

        Writing migration file to "/path/to/sandbox/doctrine_migration_20100416130405.sql"




• It would produce a file like:
                # Doctrine Migration File Generated on 2010-04-16 13:04:05
                # Migrating from 0 to 20100416130422

                # Version 20100416130401
                CREATE TABLE users (username VARCHAR(255) NOT NULL, password VARCHAR(255) NOT NULL) ENGINE = InnoDB;




Doctrine 2                www.doctrine-project.org                   www.sensiolabs.com
Database Migrations
• Integration with ORM for generating
  migrations when you change your mapping
  information. Add a new property to your
  Entity:          /** @Entity @Table(name="users") */
                                                 class User
                                                 {
                                                     /**
                                                      * @var string $test
                                                      */
                                                     private $test;

                                                     // ...
                                                 }



• Run the migrations diff command:
$ ./doctrine migrations:diff
Generated new migration class to "/path/to/migrations/DoctrineMigrations/Version20100416130459.php" from schema differences.



Doctrine 2            www.doctrine-project.org                   www.sensiolabs.com
Database Migrations
• The generated migration class looks like:
             class Version20100416130459 extends AbstractMigration
             {
                 public function up(Schema $schema)
                 {
                     $this->_addSql('ALTER TABLE users ADD test VARCHAR(255) NOT NULL');
                 }

                 public function down(Schema $schema)
                 {
                     $this->_addSql('ALTER TABLE users DROP test');
                 }
             }



• It contains the SQL statements required to
  update your database with the schema
  changes.

Doctrine 2         www.doctrine-project.org           www.sensiolabs.com
Database Migrations
• Run migrate command to execute the
  generated migration:
             $ ./doctrine migrations:migrate




• Now your database is up to date and contains
  the new column named test.




Doctrine 2    www.doctrine-project.org         www.sensiolabs.com
Questions?
     Jonathan H. Wage
     jonathan.wage@sensio.com

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

       You should follow me on http://www.twitter.com/jwage for updates about Symfony,
       Doctrine and related developments.


        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        www.doctrine-project.org         www.sensiolabs.com

Symfony2 and Doctrine2 Integration

  • 1.
    Symfony and Doctrine What’s new in the Symfony and Doctrine Integration Doctrine 2 www.doctrine-project.org www.sensiolabs.com
  • 2.
    Updated DoctrineBundle • Doctrine2features fully integrated – Database Abstraction Layer – Object Relational Mapper Doctrine 2 www.doctrine-project.org www.sensiolabs.com
  • 3.
    DoctrineMongoDBBundle • MongoDB ObjectDocument Mapper – Transparent persistence to MongoDB – Same architecture as ORM – Map a class as an entity and document Doctrine 2 www.doctrine-project.org www.sensiolabs.com
  • 4.
    DoctrineMigrationsBundle • Integration withthe database migrations project. • Easily manage and deploy different versions of your database. • Generate migrations when you change your schema mapping information Doctrine 2 www.doctrine-project.org www.sensiolabs.com
  • 5.
    DBAL • To usejust the DBAL you must configure it: doctrine.dbal: dbname: Symfony user: root password: ~ Doctrine 2 www.doctrine-project.org www.sensiolabs.com
  • 6.
    DBAL • If youneed to specify multiple connections you can use the following syntax: doctrine.dbal: default_connection: default connections: default: driver: PDOSqlite dbname: Symfony user: root password: null host: localhost port: ~ path: %kernel.data_dir%/symfony.sqlite event_manager_class: DoctrineCommonEventManager configuration_class: DoctrineDBALConfiguration wrapper_class: ~ options: [] Doctrine 2 www.doctrine-project.org www.sensiolabs.com
  • 7.
    DBAL Console Commands •Create all configured databases $ php console doctrine:database:create • Create a specific database $ php console doctrine:database:create --connection=default • Drop all configured databases $ php console doctrine:database:drop • Drop a specific database $ php console doctrine:database:drop --connection=default Doctrine 2 www.doctrine-project.org www.sensiolabs.com
  • 8.
    DBAL Console Commands •Execute SQL queries $ php console doctrine:query:sql “SELECT * FROM user” • Specify connection $ php console doctrine:query:sql “...” --connection=default Doctrine 2 www.doctrine-project.org www.sensiolabs.com
  • 9.
    DBAL • Get thedefault configured database connection: class MyController extends DoctrineController { public function indexAction() { $conn = $this->getDatabaseConnection(); // ... } } Doctrine 2 www.doctrine-project.org www.sensiolabs.com
  • 10.
    DBAL • Get aconfigured database connection service by its name: class MyController extends DoctrineController { public function indexAction() { $conn = $this->getDatabaseConnection('default'); // ... } } Doctrine 2 www.doctrine-project.org www.sensiolabs.com
  • 11.
    ORM • The EntityManager • Central place for persisting and retrieving entities • Multiple instances allowed • One EntityManager per database connection $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); • Dependency Injection handles the creation and management of entity manager services Doctrine 2 www.doctrine-project.org www.sensiolabs.com
  • 12.
    ORM • What isan Entity? It is a regular PHP object that has been mapped to the Doctrine2 ORM: /** @Entity */ class User { /** * @Id @Column(type="integer") * @GeneratedValue */ private $id; /** @Column(type="string", length=255) */ private $name; public function getId() { return $this->id; } public function getName() { return $this->name; } public function setName($name) { $this->name = $name; } } Doctrine 2 www.doctrine-project.org www.sensiolabs.com
  • 13.
    ORM • No moremagic in your domain • Clean and testable • Fast! • Only limited by what you can do with PHP OO to design your domain • Inheritance • Use __construct() without any problems • Entities are persisted transparently by the EntityManager Doctrine 2 www.doctrine-project.org www.sensiolabs.com
  • 14.
    ORM • Configure anentity manager to start using the ORM: doctrine.orm: default_entity_manager: default cache_driver: apc # array, apc, memcache, xcache entity_managers: default: connection: default Doctrine 2 www.doctrine-project.org www.sensiolabs.com
  • 15.
    ORM • Console commandsimplemented for improved developer workflow: Doctrine 2 www.doctrine-project.org www.sensiolabs.com
  • 16.
    ORM • Console commandsimplemented for improved developer workflow: • Ensure production settings • Clear metadata, query and result cache • Load data fixtures • Create and drop configured databases • Generate entities from mapping information • Generate new skeleton entities • Generate skeleton entity repository classes • Convert mapping information between formats • Convert a Doctrine1 schema • Import mapping information from an existing database • Execute DQL and SQL queries • Create, drop and update database schema from mapping information Doctrine 2 www.doctrine-project.org www.sensiolabs.com
  • 17.
    ORM • Get thedefault configured entity manager service: class MyController extends DoctrineController { public function indexAction() { $em = $this->getEntityManager(); // ... } } Doctrine 2 www.doctrine-project.org www.sensiolabs.com
  • 18.
    ORM • Get aconfigured entity manager service by its name: class MyController extends DoctrineController { public function indexAction() { $em = $this->getEntityManager('default'); // ... } } Doctrine 2 www.doctrine-project.org www.sensiolabs.com
  • 19.
    ORM • Persisting entitiesis as simple as creating the object and telling Doctrine to persist it: class MyController extends DoctrineController { public function createAction() { $em = $this->getEntityManager(); $user = new User(); $user->setName('Jonathan H. Wage'); $em->persist($user); $em->flush(); // ... } } Doctrine 2 www.doctrine-project.org www.sensiolabs.com
  • 20.
    ORM • Creating Queryinstances and issue DQL queries to retrieve objects: class MyController extends DoctrineController { public function indexAction() { $em = $this->getEntityManager(); $query = $em->createQuery('select u from MyBundle:User u'); $users = $query->execute(); // ... } } Doctrine 2 www.doctrine-project.org www.sensiolabs.com
  • 21.
    ORM • Creating QueryBuilderinstances to programatically build DQL queries through a fluent interface: class MyController extends DoctrineController { public function indexAction() { $em = $this->getEntityManager(); $qb = $em->createQueryBuilder() ->select('u') ->from('MyBundle:User', 'u'); $query = $qb->getQuery(); $users = $query->execute(); // ... } } Doctrine 2 www.doctrine-project.org www.sensiolabs.com
  • 22.
    ORM • Update yourdatabase schema during development as your domain model evolves • Add a new column to our User entity /** @Entity */ class User { // ... /** @Column(type="string", length=255) */ private $email; } Doctrine 2 www.doctrine-project.org www.sensiolabs.com
  • 23.
    ORM • Run updatecommand to update your database schema from mapping information $ php console doctrine:schema:update • The above compares your current database schema to your new mapping information and executes the necessary queries to bring your database up-to-date. Doctrine 2 www.doctrine-project.org www.sensiolabs.com
  • 24.
    Object Document Mapper •New Doctrine Project for persisting objects to MongoDB • Same architecture as ORM • Transparently persist PHP5 objects to MongoDB Doctrine 2 www.doctrine-project.org www.sensiolabs.com
  • 25.
    MongoDB ODM • TheDocumentManager • Central place for persisting and retrieving documents • Multiple instances allowed $config = new Configuration(); $config->setProxyDir(__DIR__ . '/Proxies'); $config->setProxyNamespace('Proxies'); $config->setDefaultDB('doctrine_odm_sandbox'); $reader = new AnnotationReader(); $reader->setDefaultAnnotationNamespace('DoctrineODMMongoDBMapping'); $config->setMetadataDriverImpl(new AnnotationDriver($reader, __DIR__ . '/Documents')); $dm = DocumentManager::create(new Mongo(), $config); • Dependency Injection handles the creation and management of document manager services Doctrine 2 www.doctrine-project.org www.sensiolabs.com
  • 26.
    MongoDB ODM • Touse the MongoDB ODM you must configure it: doctrine_odm.mongodb: default_document_manager: default cache_driver: array document_managers: default: connection: mongodb connections: mongodb: server: localhost/somedatabase Doctrine 2 www.doctrine-project.org www.sensiolabs.com
  • 27.
    MongoDB ODM • Ifthe defaults are good enough for you then you can omit all the previous options: doctrine_odm.mongodb: ~ Doctrine 2 www.doctrine-project.org www.sensiolabs.com
  • 28.
    MongoDB ODM • Whatis a Document? It is a regular PHP object that has been mapped to the MongoDB ODM: /** @Document */ class User { /** * @Id */ private $id; /** @String */ private $name; public function getId() { return $this->id; } public function getName() { return $this->name; } public function setName($name) { $this->name = $name; } } Doctrine 2 www.doctrine-project.org www.sensiolabs.com
  • 29.
    MongoDB ODM • Getthe default configured document manager: class MyController extends DoctrineController { public function indexAction() { $dm = $this->getDocumentManager(); // ... } } Doctrine 2 www.doctrine-project.org www.sensiolabs.com
  • 30.
    MongoDB ODM • Geta configured document manager by its name: class MyController extends DoctrineController { public function indexAction() { $dm = $this->getDocumentManager('default'); // ... } } Doctrine 2 www.doctrine-project.org www.sensiolabs.com
  • 31.
    MongoDB ODM • Justlike the ORM persisting documents is easy: class MyController extends DoctrineController { public function createAction() { $dm = $this->getDocumentManager(); $user = new User(); $user->setName('Jonathan H. Wage'); $dm->persist($user); $dm->flush(); // ... } } Doctrine 2 www.doctrine-project.org www.sensiolabs.com
  • 32.
    MongoDB ODM • Changetracking – All objects are tracked in an identity map – Changesets are calculated on flush – Changesets are used to perform updates using the atomic operators • The following code results in an efficient mongo update with only the properties that need updated: Array ( $user->setName('new name'); -> [$set] => Array ( $dm->flush(); [name] => new name ) ) Doctrine 2 www.doctrine-project.org www.sensiolabs.com
  • 33.
    MongoDB ODM • TraditionalMongoDB find() and findOne() $users = $dm->find('User', $criteria); $query = $dm->findOne('User', array('username' => 'jwage') ); $user = $query->getSingleResult(); Doctrine 2 www.doctrine-project.org www.sensiolabs.com
  • 34.
    MongoDB ODM • QueryAPI for building MongoDB queries through a fluent OO interface: class MyController extends DoctrineController { public function indexAction() { $dm = $this->getDocumentManager(); $query = $dm->createQuery('MyBundle:User'); $users = $query->execute(); // ... } } Doctrine 2 www.doctrine-project.org www.sensiolabs.com
  • 35.
    MongoDB ODM • UsingQuery builder API class MyController extends DoctrineController { public function indexAction() { $dm = $this->getDocumentManager(); $query = $dm->createQuery('User') ->where('username', 'jwage'); $user = $query->getSingleResult(); // ... } } • where(), whereIn(), whereMod(), whereNot(), etc. Doctrine 2 www.doctrine-project.org www.sensiolabs.com
  • 36.
    MongoDB ODM • FluentQuery interface generates and executes find() and findOne() methods internally • Query information is collected via fluent oo interface and executed later Doctrine 2 www.doctrine-project.org www.sensiolabs.com
  • 37.
    MongoDB ODM • DocumentQuery Language (DQL) – SQL like grammar for querying MongoDB • Query types supported – Find – Insert – Update – Remove Doctrine 2 www.doctrine-project.org www.sensiolabs.com
  • 38.
    MongoDB ODM • Findquery $query = $dm->query('find all User'); $users = $query->execute(); Doctrine 2 www.doctrine-project.org www.sensiolabs.com
  • 39.
    MongoDB ODM • Selectingfields $query = $dm->query('find username, password User'); Doctrine 2 www.doctrine-project.org www.sensiolabs.com
  • 40.
    MongoDB ODM • $sliceoperator for paging embedded collections $query = $dm->query('find comments skip 20 limit 10 Post'); Array ( [comments] => Array ( [$slice] => Array ( [0] => 20 [1] => 10 ) ) ) Doctrine 2 www.doctrine-project.org www.sensiolabs.com
  • 41.
    MongoDB ODM • Useatomic operators $query = $dm->query("update User set password = 'changeme' where username = 'jwage'"); Array ( [$set] => Array ( [password] => changeme ) ) Doctrine 2 www.doctrine-project.org www.sensiolabs.com
  • 42.
    MongoDB ODM • Complexupdate $query = $dm->query("update User inc count = 1, inc views = 2, set username = 'jwage'"); Array ( [$inc] => Array ( [count] => 1 [views] => 2 ) [$set] => Array ( [username] => jwage ) ) Doctrine 2 www.doctrine-project.org www.sensiolabs.com
  • 43.
    MongoDB ODM • DocumentQuery Language (DQL) – atomic operators – skip and limit main results – skip and limit embedded documents – use dot notation for querying embedded documents – embed JSON values in your DQL syntax $query = $dm->query("update User pushAll groups = '[1, 2, 3]'"); Doctrine 2 www.doctrine-project.org www.sensiolabs.com
  • 44.
    Database Migrations • NewDoctrineMigrationsBundle contains integration with the Doctrine Database Migrations project • Migrations have been completely re-written from Doctrine1 and are an extension of the database abstraction layer http://www.doctrine-project.org/projects/migrations Doctrine 2 www.doctrine-project.org www.sensiolabs.com
  • 45.
    Database Migrations • Migrationclasses: class Version20100416130401 extends AbstractMigration { public function up(Schema $schema) { } public function down(Schema $schema) { } } Doctrine 2 www.doctrine-project.org www.sensiolabs.com
  • 46.
    Database Migrations • Manuallyexecute SQL for migrations: class Version20100416130422 extends AbstractMigration { public function up(Schema $schema) { $this->_addSql('CREATE TABLE addresses (id INT NOT NULL, street VARCHAR(255) NOT NULL, PRIMARY KEY(id)) ENGINE = InnoDB'); } public function down(Schema $schema) { $this->_addSql('DROP TABLE addresses'); } } Doctrine 2 www.doctrine-project.org www.sensiolabs.com
  • 47.
    Database Migrations • UseAPI of Schema objects to perform migration: class Version20100416130401 extends AbstractMigration { public function up(Schema $schema) { $table = $schema->createTable('users'); $table->addColumn('username', 'string'); $table->addColumn('password', 'string'); } public function down(Schema $schema) { $schema->dropTable('users'); } } Doctrine 2 www.doctrine-project.org www.sensiolabs.com
  • 48.
    Database Migrations • Checkmigrations status: $ ./doctrine migrations:status == Configuration >> Name: Doctrine Sandbox Migrations >> Configuration Source: /Users/jwage/Sites/doctrine2git/tools/sandbox/migrations.xml >> Version Table Name: doctrine_migration_versions >> Migrations Namespace: DoctrineMigrations >> Migrations Directory: /Users/jwage/Sites/doctrine2git/tools/sandbox/DoctrineMigrations >> Current Version: 2010-04-16 13:04:22 (20100416130422) >> Latest Version: 2010-04-16 13:04:22 (20100416130422) >> Executed Migrations: 0 >> Available Migrations: 1 >> New Migrations: 1 == Migration Versions >> 2010-04-16 13:04:01 (20100416130401) not migrated Doctrine 2 www.doctrine-project.org www.sensiolabs.com
  • 49.
    Database Migrations • Executemigration dry runs: $ ./doctrine migrations:migrate --dry-run Are you sure you wish to continue? y Executing dry run of migration up to 20100416130452 from 0 >> migrating 20100416130452 -> CREATE TABLE users (username VARCHAR(255) NOT NULL, password VARCHAR(255) NOT NULL) ENGINE = InnoDB • Omit --dry-run to execute migration. Doctrine 2 www.doctrine-project.org www.sensiolabs.com
  • 50.
    Database Migrations • Specifya version number to revert to or 0 to revert all migrations: $ ./doctrine migrations:migrate 0 Are you sure you wish to continue? y Migrating down to 0 from 20100416130401 -- reverting 20100416130401 -> DROP TABLE users -- reverted Doctrine 2 www.doctrine-project.org www.sensiolabs.com
  • 51.
    Database Migrations • Writemigration SQL file instead of executing: $ ./doctrine migrations:migrate --write-sql Executing dry run of migration up to 20100416130401 from 0 >> migrating 20100416130401 -> CREATE TABLE users (username VARCHAR(255) NOT NULL, password VARCHAR(255) NOT NULL) ENGINE = InnoDB Writing migration file to "/path/to/sandbox/doctrine_migration_20100416130405.sql" • It would produce a file like: # Doctrine Migration File Generated on 2010-04-16 13:04:05 # Migrating from 0 to 20100416130422 # Version 20100416130401 CREATE TABLE users (username VARCHAR(255) NOT NULL, password VARCHAR(255) NOT NULL) ENGINE = InnoDB; Doctrine 2 www.doctrine-project.org www.sensiolabs.com
  • 52.
    Database Migrations • Integrationwith ORM for generating migrations when you change your mapping information. Add a new property to your Entity: /** @Entity @Table(name="users") */ class User { /** * @var string $test */ private $test; // ... } • Run the migrations diff command: $ ./doctrine migrations:diff Generated new migration class to "/path/to/migrations/DoctrineMigrations/Version20100416130459.php" from schema differences. Doctrine 2 www.doctrine-project.org www.sensiolabs.com
  • 53.
    Database Migrations • Thegenerated migration class looks like: class Version20100416130459 extends AbstractMigration { public function up(Schema $schema) { $this->_addSql('ALTER TABLE users ADD test VARCHAR(255) NOT NULL'); } public function down(Schema $schema) { $this->_addSql('ALTER TABLE users DROP test'); } } • It contains the SQL statements required to update your database with the schema changes. Doctrine 2 www.doctrine-project.org www.sensiolabs.com
  • 54.
    Database Migrations • Runmigrate command to execute the generated migration: $ ./doctrine migrations:migrate • Now your database is up to date and contains the new column named test. Doctrine 2 www.doctrine-project.org www.sensiolabs.com
  • 55.
    Questions? Jonathan H. Wage jonathan.wage@sensio.com sensiolabs.com | doctrine-project.org | sympalphp.org | jwage.com You should follow me on http://www.twitter.com/jwage for updates about Symfony, Doctrine and related developments. 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 www.doctrine-project.org www.sensiolabs.com