libertyvasion2010
DESCRIPTION
TRANSCRIPT
Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
Doctrine 2.0Enterprise 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
You can contact Jonathan about Doctrine and Open-Source or for training, consulting, application development, or business related
questions at [email protected]
Jonathan H. Wage
• http://www.twitter.com/jwage
• http://www.jwage.com
• http://www.facebook.com/jwage
Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
• 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.
Project History
• Roman S. Borschel
• Guilherme Blanco
• Benjamin Eberlei
• Bulat Shakirzyanov
• Jonathan H. Wage
Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
The Team
Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
Object Relational MapperWhat 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
class User extends Doctrine_Record{ public function setTableDefinition() { $this->hasColumn('username', 'string'); $this->hasColumn('password', 'string'); }}
The Doctrine1 Way
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 WayHow 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 InformationSpecify your mapping information with XML, YAML or
DocBlock Annotations
Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
User: type: entity table: users fields: id: type: integer id: true generator: strategy: AUTO username: type: string(255) password: type: string(255)
YAML
Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
<?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>
XML
Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
use Doctrine\ORM\Mapping\ClassMetadataInfo;
$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);
PHP
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
Doctrine1Doctrine1 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
Doctrine 1.14.3 seconds for 5000 records
Doctrine 2.01.4 seconds for 5000 records
Doctrine 2.03.5 seconds for 10000 records
Doctrine2 Performance
Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
Uses Namespaces
Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
namespace Doctrine\ORM;
use Doctrine\ORM\Query\Expr, Doctrine\Common\DoctrineException;
/** * 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 <[email protected]> * @author Jonathan Wage <[email protected]> * @author Roman Borschel <[email protected]> */class QueryBuilder{ // ...
Namespaces
Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
use Doctrine\ORM\QueryBuilder;
$qb = new QueryBuilder($em);$qb->select('u') ->from('Models\User', 'u') ->orderBy('u.username', 'ASC');
$q = $qb->getQuery();$users = $q->execute();
Using Namespaces
Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
http://groups.google.com/group/php-standards
Follow PHP 5.3 Interoperability
Standards
Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
• 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
Benefits
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
Doctrine\Common• 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
Doctrine\DBAL
• Relational database abstraction layer
• MySQL• Sqlite• PostgreSQL• Oracle• MsSQL
Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
DBAL is StandaloneUse the DBAL without the ORM
Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
• The Doctrine DBAL is a very powerful project in itself
• Based off of code forked from other projects
• PEAR MDB2
• Zend_Db
DBAL History
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 = \Doctrine\DBAL\DriverManager::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
$sm = $conn->getSchemaManager();
Schema Manager
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
$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);
Drop and Create Table
Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
$definition = array( 'name' => 'user_id_fk', 'local' => 'user_id', 'foreign' => 'id', 'foreignTable' => 'user');$sm->createForeignKey('profile', $definition);
Create Foreign Key
Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
Doctrine\ORM
• 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
Doctrine\ODM\MongoDBDoctrine Object Document Mapper (ODM) for
MongoDB
Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
Doctrine\ODM\MongoDB
• 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
ArchitectureThe architecture of the Doctrine object persistence
layers
Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
The ObjectManagerEntityManager and DocumentManager
Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
APISimple API for managing the persistent state of objects
• 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
Object States
Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
$config = new \Doctrine\ORM\Configuration();$config->setMetadataCacheImpl(new \Doctrine\Common\Cache\ArrayCache);$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 = \Doctrine\ORM\EntityManager::create($connectionOptions, $config);
Creating EntityManager
Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
$config = new \Doctrine\ODM\MongoDB\Configuration();$config->setMetadataCacheImpl(new \Doctrine\Common\Cache\ArrayCache);$driverImpl = $config->newDefaultAnnotationDriver(array(__DIR__."/Documents"));$config->setMetadataDriverImpl($driverImpl);
$config->setProxyDir(__DIR__ . '/Proxies');$config->setProxyNamespace('Proxies');
$mongo = new \Doctrine\ODM\MongoDB\Mongo();
$dm = \Doctrine\ORM\DocumentManager::create($mongo, $config);
Creating DocumentManager
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 LanguageThe 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 CachedIn 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
$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();
Sample DQL Query
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 Models\User u');$query->useResultCache(true, 3600, 'user_query');$users = $query->execute();
$users = $query->execute();
Execute query again and the results are retrieved from the cache
Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
Symfony2 IntegrationThe Doctrine features are tightly integrated with
Symfony2
Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
Enabling the DBAL
# config/config.yml
doctrine.dbal: driver: PDOMySql dbname: Symfony2 user: root password: null
Enable the DBAL in your application configuration
Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
Using the DBAL
class UserController extends Controller{ public function indexAction() { $conn = $this->container->getService('database_connection');
$users = $conn->fetchAll('SELECT * FROM users'); }}
Use the DBAL connection in your controllers
Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
Enabling the ORM
# config/config.yml
doctrine.orm: ~
Enable the ORM in your application configuration
Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
namespace Application\HelloBundle\Entity;
/** * @Entity */class User{ /** @Id */ public $id;
/** @Column(type="string", length="255") */ public $username;
/** @Column(type="string", length="255") */ public $password;}
Application/HelloBundle/Entity/User.php
Creating an Entity
Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
$ php hello/console doctrine:schema:create
Create database schema
Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
Using the ORM
use Application\HelloBundle\Entities\User;
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();
// ... }}
Use the ORM EntityManager in your controllers
Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
ConclusionWhy use an ORM?
Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
EncapsulationEncapsulate all your domain logic in one place, in an
object oriented way
Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
MaintainabilityThe organization of the domain logic in an OO way
improves maintainability
Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
TestabilityKeeping a clean OO domain model makes your
business logic easily testable for improved stability
Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
PortabilityWrite portable and thin application controller code and
fat models.
Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org
You can contact Jonathan about Doctrine and Open-Source or for training, consulting, application development, or business related
questions at [email protected]
Jonathan H. [email protected]://www.twitter.com/jwage+1 615 513 9185
sensiolabs.com | doctrine-project.org | jwage.com
Questions?