libertyvasion2010

85
Doctrine 2.0 www.doctrine-project.org www.sensiolabs.org Doctrine 2.0 Enterprise Object Relational Mapper for PHP 5.3

Upload: jonathan-wage

Post on 15-Jan-2015

973 views

Category:

Documents


1 download

DESCRIPTION

 

TRANSCRIPT

Page 1: Libertyvasion2010

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

Doctrine 2.0Enterprise Object Relational Mapper for PHP 5.3

Page 2: Libertyvasion2010

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

Page 3: Libertyvasion2010

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

Page 4: Libertyvasion2010

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

Page 5: Libertyvasion2010

• Roman S. Borschel

• Guilherme Blanco

• Benjamin Eberlei

• Bulat Shakirzyanov

• Jonathan H. Wage

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

The Team

Page 6: Libertyvasion2010

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

Object Relational MapperWhat is it?

Page 7: Libertyvasion2010

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.

Page 8: Libertyvasion2010

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

Doctrine1 vs Doctrine2

Page 9: Libertyvasion2010

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

Page 10: Libertyvasion2010

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

The Doctrine1 Way

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

Page 11: Libertyvasion2010

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

Page 12: Libertyvasion2010

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

The Doctrine2 WayHow does Doctrine2 do it?

Page 13: Libertyvasion2010

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

No base class to extend

Page 14: Libertyvasion2010

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; }

// ...}

Page 15: Libertyvasion2010

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

Mapping InformationSpecify your mapping information with XML, YAML or

DocBlock Annotations

Page 16: Libertyvasion2010

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

Page 17: Libertyvasion2010

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

Page 18: Libertyvasion2010

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

Page 19: Libertyvasion2010

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

Transparent Persistence

Page 20: Libertyvasion2010

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

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

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

Page 21: Libertyvasion2010

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.

Page 22: Libertyvasion2010

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();

Page 23: Libertyvasion2010

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

Page 24: Libertyvasion2010

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.

Page 26: Libertyvasion2010

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

PHP 5.3 is Fast

Page 27: Libertyvasion2010

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

Page 28: Libertyvasion2010

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

Page 29: Libertyvasion2010

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

Uses Namespaces

Page 30: Libertyvasion2010

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

Page 31: Libertyvasion2010

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

Page 32: Libertyvasion2010

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

http://groups.google.com/group/php-standards

Follow PHP 5.3 Interoperability

Standards

Page 33: Libertyvasion2010

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

Page 34: Libertyvasion2010

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

Doctrine code is divided into a few different packages

Page 35: Libertyvasion2010

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

Page 36: Libertyvasion2010

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

Doctrine\DBAL

• Relational database abstraction layer

• MySQL• Sqlite• PostgreSQL• Oracle• MsSQL

Page 37: Libertyvasion2010

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

DBAL is StandaloneUse the DBAL without the ORM

Page 38: Libertyvasion2010

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

Page 39: Libertyvasion2010

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.

Page 40: Libertyvasion2010

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.

Page 41: Libertyvasion2010

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

$sm = $conn->getSchemaManager();

Schema Manager

Page 42: Libertyvasion2010

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.

Page 43: Libertyvasion2010

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

Page 44: Libertyvasion2010

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

Page 45: Libertyvasion2010

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

Page 46: Libertyvasion2010

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

Persisting to Other Databases

Page 47: Libertyvasion2010

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

Doctrine\ODM\MongoDBDoctrine Object Document Mapper (ODM) for

MongoDB

Page 48: Libertyvasion2010

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.

Page 49: Libertyvasion2010

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

ArchitectureThe architecture of the Doctrine object persistence

layers

Page 50: Libertyvasion2010

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

The ObjectManagerEntityManager and DocumentManager

Page 51: Libertyvasion2010

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

APISimple API for managing the persistent state of objects

Page 52: Libertyvasion2010

• 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

Page 53: Libertyvasion2010

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

Page 54: Libertyvasion2010

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

Page 55: Libertyvasion2010

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

Managing the State

Page 56: Libertyvasion2010

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();

Page 57: Libertyvasion2010

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

Page 58: Libertyvasion2010

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

Page 59: Libertyvasion2010

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

Page 60: Libertyvasion2010

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

Doctrine Query LanguageThe killer ORM feature

Page 61: Libertyvasion2010

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

Propietary Object Query Language

Page 62: Libertyvasion2010

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

Parsed by Recursive Descent Parser

Page 63: Libertyvasion2010

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

Constructs AST

Page 64: Libertyvasion2010

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

AST = Abstract Syntax Tree

Page 65: Libertyvasion2010

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

Tree representation of the syntactical structure of a

source language

Page 66: Libertyvasion2010

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

Page 67: Libertyvasion2010

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

Page 68: Libertyvasion2010

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.

Page 69: Libertyvasion2010

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

Page 70: Libertyvasion2010

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();

Page 71: Libertyvasion2010

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

Executing Queries

$users = $query->execute();

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

Page 72: Libertyvasion2010

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

Page 73: Libertyvasion2010

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

Symfony2 IntegrationThe Doctrine features are tightly integrated with

Symfony2

Page 74: Libertyvasion2010

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

Page 75: Libertyvasion2010

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

Page 76: Libertyvasion2010

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

Page 77: Libertyvasion2010

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

Page 78: Libertyvasion2010

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

$ php hello/console doctrine:schema:create

Create database schema

Page 79: Libertyvasion2010

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

Page 80: Libertyvasion2010

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

ConclusionWhy use an ORM?

Page 81: Libertyvasion2010

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

EncapsulationEncapsulate all your domain logic in one place, in an

object oriented way

Page 82: Libertyvasion2010

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

MaintainabilityThe organization of the domain logic in an OO way

improves maintainability

Page 83: Libertyvasion2010

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

Page 84: Libertyvasion2010

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

PortabilityWrite portable and thin application controller code and

fat models.

Page 85: Libertyvasion2010

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?