Libertyvasion2010

Post on 15-Jan-2015

978 views 1 download

Tags:

description

 

Transcript of Libertyvasion2010

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 jonathan.wage@sensio.com

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 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 <guilhermeblanco@hotmail.com> * @author Jonathan Wage <jonwage@gmail.com> * @author Roman Borschel <roman@code-factory.org> */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 jonathan.wage@sensio.com

Jonathan H. Wagejonwage@gmail.comhttp://www.twitter.com/jwage+1 615 513 9185

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

Questions?