97
Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

Embed Size (px)

DESCRIPTION

Ralph Schindler (of Zend Framework) and Jon Wage (of Doctrine) presented these slides for a webinar hosted by zend.com (webinar available online).Links are contained within the slides to the demo application that was also used during the webinar.

Citation preview

Page 1: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

Page 2: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

Jonathan H. Wage•PHP Developer for over 10 years•Symfony Contributor•Doctrine Contributor•Published Author•Business Owner•Nashville, TN Resident

•http://www.twitter.com/jwage

•http://www.facebook.com/jwage

2

Page 3: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

I work at OpenSky

•What is OpenSky?“a social commerce platform”

•Based in New York and is a major open•source software advocate

•http://www.shopopensky.com

3

Page 4: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

OpenSky Technologies•PHP 5.3.2•Apache2•Symfony2•Doctrine2•jQuery•mule, stomp, hornetq•MongoDB•nginx•varnish

4

Page 5: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

Ralph Schindler•Software Engineer on the Zend Framework team

At Zend for almost 3 yearsBefore that TippingPoint/3Com

•Programming PHP for 12+ years•Live in New Orleans, LA.

Lived in Austin, Tx for 5 years

•Where To Find Me:http://ralphschindler.comhttp://twitter.com/ralphschindlerhttp://github.com/ralphschindlerralphschindler on freenode

5

Page 6: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

Guilherme Blanco•Programming Experience

12+ years web development experience 9 years with PHP

•Software Engineer at Yahoo!•Open Source Evangelist

Contributes regularly to the Doctrine Project, Symfony, and Zend Framework

•Where to find me:http://twitter.com/guilhermeblancohttp://github.com/guilhermeblanco

6

Page 7: Zend Framework 1 + Doctrine 2

The Doctrine Project

Doctrine 2

7

Page 8: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

What is Doctrine?• Open Source PHP Project started in 2006

• Specializes in database functionality

Database Abstraction Layer (DBAL) Database Migrations Object Relational Mapper (DBAL) MongoDB Object Document Manager (ODM) CouchDB Object Document Manager (ODM)

8

Page 9: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

Who is on the team?

• Roman S. Borschel

• Guilherme Blanco

• Benjamin Eberlei

• Bulat Shakirzyanov

• Jonathan H. Wage

9

Page 10: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

Project History• First commit April 13th 2006• First stable version finished and Released September 1st 2008

• One of the first ORM implementations for PHP• 1.0 is First LTS(long term support) release. Maintained until March 1st 2010

• Integrated with many popular frameworks: Symfony, Zend Framework, Code Igniter

10

Page 11: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

Doctrine Libraries• Database Abstraction Layer

• Database Migrations• Object Relational Mapper• MongoDB Object Document Manager• CouchDB Object Document Manager

11

Page 12: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

DBALDatabase Abstraction Layer

12

Page 13: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

Database Abstraction Layer• The Doctrine Database Abstraction

• Layer (DBAL) is a thin layer on top of• PDO, it offers:

select, update, delete, transactions database schema introspection schema management

13

Page 14: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

Can be used standalone

14

Page 15: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

Evolved fork of PEAR MDB, MDB2, Zend_Db, etc.

15

Page 16: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

Download•You can download a standalone

•package to get started using the DBAL:

http://www.doctrine-project.org/projects/dbal/download

16

Page 17: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

Autoloader•To use any Doctrine library you must

•register an autoloader:

use Doctrine\Common\ClassLoader;

require '/path/to/doctrine-common/lib/Doctrine/Common/ClassLoader.php';

$classLoader = new ClassLoader('Doctrine\DBAL', '/path/to/doctrine-dbal/lib');$classLoader->register();

17

Page 18: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

Create a Connection

$config = new \Doctrine\DBAL\Configuration();//..$connectionParams = array( 'dbname' => 'mydb', 'user' => 'user', 'password' => 'secret', 'host' => 'localhost', 'driver' => 'pdo_mysql',);$conn = DriverManager::getConnection($connectionParams);

18

Page 19: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

Data API• prepare($sql) - Prepare a given sql statement and return the \Doctrine\DBAL\Driver\Statement

instance.

• executeUpdate($sql, array $params) - Executes a prepared statement with the given sql and parameters and returns the affected rows count.

• execute($sql, array $params) - Creates a prepared statement for the given sql and passes the parameters to the execute method, then returning the statement.

• fetchAll($sql, array $params) - Execute the query and fetch all results into an array.

• fetchArray($sql, array $params) - Numeric index retrieval of first result row of the given query.

• fetchBoth($sql, array $params) - Both numeric and assoc column name retrieval of the first result row.

• fetchColumn($sql, array $params, $colnum) - Retrieve only the given column of the first result row.

• fetchRow($sql, array $params) - Retrieve assoc row of the first result row.

• select($sql, $limit, $offset) - Modify the given query with a limit clause.

• delete($tableName, array $identifier) - Delete all rows of a table matching the given identifier, where keys are column names.

• insert($tableName, array $data) - Insert a row into the given table name using the key value pairs of data.

19

Page 20: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

Very Similar to PDO

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

20

Page 21: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

Schema Manager•Learn about and modify your database

•through the SchemaManager:

$sm = $conn->getSchemaManager();

21

Page 22: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

Introspection API•listDatabases()•listFunctions()•listSequences()•listTableColumns($tableName)•listTableConstraints($tableName)•listTableDetails($tableName)•listTableForeignKeys($tableName)•listTableIndexes($tableName)•listTables()

22

Page 23: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

Introspection API

$tables = $sm->listTables();foreach ($tables as $table) { $columns = $sm->listTableColumns($table); // ...}

23

Page 24: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

DDL Statements•Progromatically issue DDL statements:

$columns = array( 'id' => array( 'type' => \Doctrine\DBAL\Type::getType('integer'), 'autoincrement' => true, 'primary' => true, 'notnull' => true ), 'test' => array( 'type' => \Doctrine\DBAL\Type::getType('string'), 'length' => 255 ));

$options = array();

$sm->createTable('new_table', $columns, $options);

24

Page 25: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

DDL Statements

•Progromatically issue DDL statements:

$definition = array( 'name' => 'user_id_fk', 'local' => 'user_id', 'foreign' => 'id', 'foreignTable' => 'user');$sm->createForeignKey('profile', $definition);

25

Page 26: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

Try a Method•You can try a method and return true if

•the operation was successful:

if ($sm->tryMethod('createTable', 'new_table', $columns, $options)) { // do something}

26

Page 27: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

Drop and Create Database

try { $sm->dropDatabase('test_db');} catch (Exception $e) {}

$sm->createDatabase('test_db');

27

Page 28: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

Drop and Create Database•A little better! Every drop and create

•functionality in the API has a method•that follows the dropAndCreate pattern:

$sm->dropAndCreateDatabase('test_db');

28

Page 29: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

Schema Representation

$platform = $em->getConnection()->getDatabasePlatform();

$schema = new \Doctrine\DBAL\Schema\Schema();$myTable = $schema->createTable("my_table");$myTable->addColumn("id", "integer", array("unsigned" => true));$myTable->addColumn("username", "string", array("length" => 32));$myTable->setPrimaryKey(array("id"));

// get queries to create this schema.$queries = $schema->toSql($platform);

Array( [0] => CREATE TABLE my_table (id INTEGER NOT NULL, username VARCHAR(32) NOT NULL, PRIMARY KEY("id")))

29

Page 30: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

Schema Representation

Array( [0] => DROP TABLE my_table)

Returns the reverse SQL of what toSql() returns

// ......

// get queries to safely delete this schema.$dropSchema = $schema->toDropSql($platform);

Array( [0] => DROP TABLE my_table)

30

Page 31: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

Comparing Schemas$fromSchema = new \Doctrine\DBAL\Schema\Schema();$myTable = $fromSchema->createTable("my_table");$myTable->addColumn("id", "integer", array("unsigned" => true));$myTable->addColumn("username", "string", array("length" => 32));$myTable->setPrimaryKey(array("id"));

$toSchema = new \Doctrine\DBAL\Schema\Schema();$myTable = $toSchema->createTable("my_table");$myTable->addColumn("id", "integer", array("unsigned" => true));$myTable->addColumn("username", "string", array("length" => 32));$myTable->addColumn("email", "string", array("length" => 255));$myTable->setPrimaryKey(array("id"));

$comparator = new \Doctrine\DBAL\Schema\Comparator();$schemaDiff = $comparator->compare($fromSchema, $toSchema);

// queries to get from one to another schema.$queries = $schemaDiff->toSql($platform);

print_r($queries);

ALTER TABLE my_table ADD email VARCHAR(255) NOT NULL

31

Page 32: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

ORMObject Relational Mapper

32

Page 33: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

What is ORM?

•“Technique for converting data between incompatible type systems in object-oriented programming languages.”

http://en.wikipedia.org/wiki/Object-relational_mapping

33

Page 34: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

The ORM is built on top of Common and DBAL

34

Page 35: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

ORM Goals

• Maintain transparency• Keep domain and persistence layer separated• Performance• Consistent and decoupled API• Well defined semantics

35

Page 36: Zend Framework 1 + Doctrine 2

http://www.doctrine-project.org/projects/orm/download

Zend Framework 1 + Doctrine 2

Download

36

Page 37: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

Architecture•Entities• Lightweight persistent domain object• Regular PHP class• Does not extend any base Doctrine class• Cannot be final or contain final methods

• Any two entities in a hierarchy of classes must not have a mapped property with the same name

• Supports inheritance, polymorphic associations and polymorphic queries.

• Both abstract and concrete classes can be entities• Entities may extend non-entity classes as well as entity classes, and non-entity classes may extend entity classes

37

Page 38: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

Architecture• No more base class required

• Values stored in object properties• Persistence is done transparently

namespace Entities;

class User{ private $id; private $name;}

38

Page 39: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

Architecture

• The EntityManager• Central access point to the ORM functionality provided by Doctrine 2. API is used to manage the persistence of your objects and to query for persistent objects.

• Employes transactional write behind strategy that delays the execution of SQL statements in order to execute them in the most efficient way

• Execute at end of transaction so that all write locks are quickly releases

• Internally an EntityManager uses a UnitOfWork to keep track of your objects

39

Page 40: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

Create EntityManager• Create a new EntityManager instance:

$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');

$em = \Doctrine\ORM\EntityManager::create($conn, $config);

40

Page 41: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

Map entities to RDBMS tables

• Entities are just regular PHP objects

namespace Entities;

class User{ private $id; private $name;}

41

Page 42: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

Map entities to RDBMS tables• Entities are just regular PHP objects

Mapped By:•Annotations

namespace Entities;

/** * @Entity @Table(name="users") */class User{ /** @Id @Column(type="integer") @GeneratedValue */ private $id;

/** @Column(length=50) */ private $name;}

42

Page 43: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

• Entities are just regular PHP objects:Mapped By:

•Annotations

•YAMLEntities\User: type: entity table: users id: id: type: integer generator: strategy: AUTO fields: name: type: string length: 255

43

Map entities to RDBMS tables

Page 44: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

• Entities are just regular PHP objects:Mapped By:

•Annotations

•YAML

•XML<?xml version="1.0" encoding="UTF-8"?><doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd">

<entity name="Entities\User" table="users"> <id name="id" type="integer"> <generator strategy="AUTO"/> </id> <field name="name" type="string" length="50"/> </entity>

</doctrine-mapping>

44

Map entities to RDBMS tables

Page 45: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

Mapping Performance• Only parsed once

• Cached using configured cache driver• Subsequent requests pull mapping information from configured cache driver

45

Page 46: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

Working with Objects• Use the $em to manage the persistence of your entities:

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

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

46

Page 47: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

Working with Objects• Updating an object:

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

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

47

Page 48: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

Working with Objects• Removing an object:

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

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

48

Page 49: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

Transactions• Implicit:

•EntityManager#flush() will begin and commit/rollback a transaction

$user = new User;$user->setName('George');$em->persist($user);$em->flush();

49

Page 50: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

Transactions• Explicit:

// $em instanceof EntityManager$em->getConnection()->beginTransaction(); // suspend auto-committry { //... do some work $user = new User; $user->setName('George'); $em->persist($user); $em->flush(); $em->getConnection()->commit();} catch (Exception $e) { $em->getConnection()->rollback(); $em->close(); throw $e;}

50

Page 51: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

Transactions• A more convenient explicit transaction:

// $em instanceof EntityManager$em->transactional(function($em) { //... do some work $user = new User; $user->setName('George'); $em->persist($user);});

51

Page 52: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

Transactions and Performance

for ($i = 0; $i < 20; ++$i) { $user = new User; $user->name = 'Jonathan H. Wage'; $em->persist($user);}

$s = microtime(true);$em->flush();$e = microtime(true);echo $e - $s;

52

Page 53: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

Transactions and Performance• How you use transactions can greatly affect performance. Here is the same thing using raw PHP code:

$s = microtime(true);for ($i = 0; $i < 20; ++$i) { mysql_query("INSERT INTO users (name) VALUES ('Jonathan H. Wage')", $link);}$e = microtime(true);echo $e - $s;

53

Page 54: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

Which is faster?• The one using no ORM, and no abstraction at all?

• Or the one using the Doctrine ORM?

54

Page 55: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

Which is faster?

• The one using no ORM, and no abstraction at all?

• Or the one using the Doctrine ORM?

• Doctrine2 wins! How?

Doctrine2 0.0094 seconds

mysql_query 0.0165 seconds

55

Page 56: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

Not Faster• Doctrine just automatically performed the inserts inside one transaction. Here is the code updated to use transactions:

$s = microtime(true);mysql_query('START TRANSACTION', $link);for ($i = 0; $i < 20; ++$i) { mysql_query("INSERT INTO users (name) VALUES ('Jonathan H. Wage')", $link);}mysql_query('COMMIT', $link);$e = microtime(true);echo $e - $s;

56

Page 57: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

Much Faster• Transactions matter and can affect performance greater than any code optimization!

Doctrine2 0.0094 seconds

mysql_query 0.0165 seconds0.0028

57

Page 58: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

Locking Support• Optimistic locking with integer:

class User{ // ... /** @Version @Column(type="integer") */ private $version; // ...}

58

Page 59: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

Locking Support• Optimistic locking with timestamp:

class User{ // ... /** @Version @Column(type="datetime") */ private $version; // ...}

59

Page 60: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

Locking Support• Verify version when finding:

use Doctrine\DBAL\LockMode;use Doctrine\ORM\OptimisticLockException;

$theEntityId = 1;$expectedVersion = 184;

try { $entity = $em->find('User', $theEntityId, LockMode::OPTIMISTIC, $expectedVersion);

// do the work

$em->flush();} catch(OptimisticLockException $e) { echo "Sorry, but someone else has already changed this entity. Please apply the changes again!";}

60

Page 61: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

Locking Support• Example implementation:

$post = $em->find('BlogPost', 123456);

echo '<input type="hidden" name="id" value="' . $post->getId() . '" />';echo '<input type="hidden" name="version" value="' . $post->getCurrentVersion() . '" />';

$postId = (int) $_GET['id'];$postVersion = (int) $_GET['version'];

$post = $em->find('BlogPost', $postId, \Doctrine\DBAL\LockMode::OPTIMISTIC, $postVersion);

61

Page 62: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

DQLDoctrine Query Language

62

Page 63: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

DQL• DQL stands for Doctrine Query Language and is an Object Query Language derivate that is very similar to the Hibernate Query Language (HQL) or the Java Persistence Query Language (JPQL).

• DQL provides powerful querying capabilities over your object model. Imagine all your objects lying around in some storage (like an object database). When writing DQL queries, think about querying that storage to find a certain subset of your objects.

63

Page 64: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

DQL Parser• Parser completely re-written from scratch

• Parsed by top down recursive descent lexer parser that constructs an AST(Abstract Syntax Tree)

• Platform specific SQL is generated from AST

64

Page 65: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

Doctrine Query Language

$q = $em->createQuery('SELECT u FROM User u');$users = $q->execute();

65

Page 66: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

Query Builder

• Same query built using the QueryBuilder

$qb = $em->createQueryBuilder() ->select('u') ->from('User', 'u');

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

66

Page 67: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

More Examples

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

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

$query = $qb->getQuery();

67

Page 68: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

Executing Queries• Executing and getting results

$users = $query->execute();

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

68

Page 69: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

Executing Queries• Execute query and iterate over results keeping memory usage low:

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

69

Page 70: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

Result Cache• Optionally cache the results of your queries in your driver of choice:

$cacheDriver = new \Doctrine\Common\Cache\ApcCache();$config->setResultCacheImpl($cacheDriver);

$query = $em->createQuery('select u from Entities\User u');$query->useResultCache(true, 3600, 'my_query_name');

$users = $query->execute();

$users = $query->execute(); // 2nd time pulls from cache

70

Page 71: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

Inheritance• Doctrine supports mapping entities that use inheritance with the following strategies:

Mapped Superclass Single Table Inheritance Class Table Inheritance

71

Page 72: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

Mapped Superclasses

/** @MappedSuperclass */abstract class MappedSuperclassBase{ /** @Column(type="integer") */ private $mapped1; /** @Column(type="string") */ private $mapped2; /** * @OneToOne(targetEntity="MappedSuperclassRelated1") * @JoinColumn(name="related1_id", referencedColumnName="id") */ private $mappedRelated1;

// ... more fields and methods}

/** @Entity */class EntitySubClass extends MappedSuperclassBase{ /** @Id @Column(type="integer") */ private $id; /** @Column(type="string") */ private $name;

// ... more fields and methods}

72

Page 73: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

Single Table Inheritance

/** * @Entity * @InheritanceType("SINGLE_TABLE") * @DiscriminatorColumn(name="discr", type="string") * @DiscriminatorMap({"person" = "Person", "employee" = "Employee"}) */class Person{ // ...}

/** * @Entity */class Employee extends Person{ // ...}

73

Page 74: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

Single Table Inheritance• All entities share one table.

• To distinguish which row represents which type in the hierarchy a so-called discriminator column is used.

74

Page 75: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

Class Table Inheritance

/** * @Entity * @InheritanceType("JOINED") * @DiscriminatorColumn(name="discr", type="string") * @DiscriminatorMap({"person" = "Person", "employee" = "Employee"}) */class Person{ // ...}

/** @Entity */class Employee extends Person{ // ...}

75

Page 76: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

Class Table Inheritance• Each class in a hierarchy is mapped to several tables: its own table and the tables of all parent classes.

• The table of a child class is linked to the table of a parent class through a foreign key constraint.

• A discriminator column is used in the topmost table of the hierarchy because this is the easiest way to achieve polymorphic queries.

76

Page 77: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

Bulk Inserts with Domain• Insert 10000 objects batches of 20:

$batchSize = 20;for ($i = 1; $i <= 10000; ++$i) { $user = new User; $user->setStatus('user'); $user->setUsername('user' . $i); $user->setName('Mr.Smith-' . $i); $em->persist($user); if ($i % $batchSize == 0) { $em->flush(); $em->clear(); // Detaches all objects from Doctrine! }}

77

Page 78: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

Bulk Update with DQL

$q = $em->createQuery('update Manager m set m.salary = m.salary * 0.9');$numUpdated = $q->execute();

78

Page 79: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

Bulk Update with Domain

• Update objects in batches of 20:

$batchSize = 20;$i = 0;$q = $em->createQuery('select u from User u');$iterableResult = $q->iterate();foreach($iterableResult AS $row) { $user = $row[0]; $user->increaseCredit(); $user->calculateNewBonuses(); if (($i % $batchSize) == 0) { $em->flush(); // Executes all updates. $em->clear(); // Detaches all objects from Doctrine! } ++$i;}

79

Page 80: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

Bulk Delete with DQL

$q = $em->createQuery('delete from Manager m where m.salary > 100000');$numDeleted = $q->execute();

80

Page 81: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

Bulk Delete with Domain

$batchSize = 20;$i = 0;$q = $em->createQuery('select u from User u');$iterableResult = $q->iterate();while (($row = $iterableResult->next()) !== false) { $em->remove($row[0]); if (($i % $batchSize) == 0) { $em->flush(); // Executes all deletions. $em->clear(); // Detaches all objects from Doctrine! } ++$i;}

81

Page 82: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

Events•Doctrine triggers events throughout the

•lifecycle of objects it manages: preRemove postRemove prePersist postPersist preUpdate postUpdate preLoad postLoad

82

Page 83: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

Example

/** * @Entity * @HasLifecycleCallbacks */class BlogPost{ // ...

/** @PreUpdate */ public function prePersist() { $this->createdAt = new DateTime(); }

/** @PreUpdate */ public function preUpdate() { $this->updatedAt = new DateTime(); }}

83

Page 84: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

Using Raw SQL• Write a raw SQL string

• Map the result set of the SQL query using a ResultSetMapping instance

84

Page 85: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

Using Raw SQL

$sql = 'SELECT id, name FROM users WHERE username = ?';

$rsm = new ResultSetMapping;$rsm->addEntityResult('User', 'u');$rsm->addFieldResult('u', 'id', 'id');$rsm->addFieldResult('u', 'name', 'name');

$query = $this->_em->createNativeQuery($sql, $rsm);$query->setParameter(1, 'jwage');

$users = $query->getResult();

85

Page 86: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

Why use an object mapper?

86

Page 87: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

Encapsulate your domain in an object oriented interface

Encapsulation

87

Page 88: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

The organization of your domain logic in an OO way improved maintainability

Maintainability

88

Page 89: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

Keeping a clean OO domain model makes your business logic easily testable for improved stability

Testability

89

Page 90: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

Write portable and thin application controller code and fat models.

Portability

90

Page 91: Zend Framework 1 + Doctrine 2

The Doctrine Project

Demo Time

91

Page 92: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

What we are going to accomplish•Start with a vanilla Zend Framework Project

•Ensure all dependencies are met•Configure out application to utilize Doctrine•Create an Entity (in our library) with Annotations•Generate the Database

•Generate Proxies + Repositories•Create a Controller for basic crud•Talk about what would happen next

92

Page 93: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

Where To Get The Code•http://github.com/ralphschindler/NOLASnowball

Self contained ProjectBranches:

•master - Clean ZF Project, with ZF embedded in library/ folder

•non-model-artifacts - Authentication service, Login form

•doctrine2-managed– Has following libraries: Doctrine2, Symfony (Copied into library), Bisna (ZF1 +

Doctrine2 Integration library)

– Has the following entity created: NOLASnowball\Entity\Stand

– Has the proper application.ini settings

– Has scripts/doctrine.php setup for easy use

•doctrine2-managed-crud– Created Stand Controller, actions are complete, view scripts complete

– Proxies & Repositories are generated

– Assumes you’ve generated the SQL (locally would need to change db credentials)

93

Page 94: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

Lets look at code!•Demo time

94

Page 95: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

Exercises and Things To Implement•flush() could be a postDispatch() function call

•All interaction with Entities could be moved into a Service LayerOnce such implementation: https://github.com/guilhermeblanco/ZF1-Doctrine2-ServiceLayer

•Add relationships, and alter forms accordingly

95

Page 96: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

Recommended Reading•Doctrine Website & Manual (Also has download)

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

•Zend Framework Web & Manual (With download)http://framework.zend.com/

•Ralph Schindler’s Sample Applicationhttps://github.com/ralphschindler/NOLASnowball

•Guilherme Blanco’s ZF1 + D2 Integration codehttps://github.com/guilhermeblanco/ZendFramework1-Doctrine2

96

Page 97: Zend Framework 1 + Doctrine 2

Zend Framework 1 + Doctrine 2

Questions?•Q & A Time

97