SlideShare a Scribd company logo
1 of 88
Download to read offline
Jonathan H. Wage | OpenSky
The Doctrine Project
Who am I?
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
I work at
What is OpenSky?
“a social commerce platform”
Based in New York and is a major open
source software advocate
http://www.shopopensky.com
OpenSky Technologies
PHP 5.3.2
Apache2
Symfony2
Doctrine2
jQuery
mule, stomp, hornetq
MongoDB
nginx
varnish
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)
Who is on the team?
• Roman S. Borschel
• Guilherme Blanco
• Benjamin Eberlei
• Bulat Shakirzyanov
• Jonathan H.Wage
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
Doctrine Libraries
- Database Abstraction Layer
- Database Migrations
- Object Relational Mapper
- MongoDB Object Document Manager
- CouchDB Object Document Manager
DBAL
Database Abstraction Layer
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
Can be used standalone
Evolved fork of PEAR MDB,
MDB2, Zend_Db, etc.
Download
You can download a standalone
package to get started using the DBAL:
http://www.doctrine-project.org/projects/dbal/download
Autoloader
To use any Doctrine library you must
register an autoloader:
use DoctrineCommonClassLoader;
require '/path/to/doctrine-common/lib/Doctrine/Common/ClassLoader.php';
$classLoader = new ClassLoader('DoctrineDBAL', '/path/to/doctrine-dbal/lib');
$classLoader->register();
Create a Connection
$config = new DoctrineDBALConfiguration();
//..
$connectionParams = array(
'dbname' => 'mydb',
'user' => 'user',
'password' => 'secret',
'host' => 'localhost',
'driver' => 'pdo_mysql',
);
$conn = DriverManager::getConnection($connectionParams);
Data API
prepare($sql) - Prepare a given sql statement and return the DoctrineDBALDriver
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.
Very Similar to PDO
$users = $conn->fetchAll('SELECT * FROM users');
Schema Manager
Learn about and modify your database
through the SchemaManager:
$sm = $conn->getSchemaManager();
Introspection API
listDatabases()
listFunctions()
listSequences()
listTableColumns($tableName)
listTableConstraints($tableName)
listTableDetails($tableName)
listTableForeignKeys($tableName)
listTableIndexes($tableName)
listTables()
Introspection API
$tables = $sm->listTables();
foreach ($tables as $table) {
$columns = $sm->listTableColumns($table);
// ...
}
DDL Statements
Progromatically issue DDL statements:
$columns = array(
'id' => array(
'type' => DoctrineDBALType::getType('integer'),
'autoincrement' => true,
'primary' => true,
'notnull' => true
),
'test' => array(
'type' => DoctrineDBALType::getType('string'),
'length' => 255
)
);
$options = array();
$sm->createTable('new_table', $columns, $options);
DDL Statements
Progromatically issue DDL statements:
$definition = array(
'name' => 'user_id_fk',
'local' => 'user_id',
'foreign' => 'id',
'foreignTable' => 'user'
);
$sm->createForeignKey('profile', $definition);
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
}
Drop and Create Database
try {
$sm->dropDatabase('test_db');
} catch (Exception $e) {}
$sm->createDatabase('test_db');
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');
Schema Representation
$platform = $em->getConnection()->getDatabasePlatform();
$schema = new DoctrineDBALSchemaSchema();
$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"))
)
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
)
Comparing Schemas
$fromSchema = new DoctrineDBALSchemaSchema();
$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 DoctrineDBALSchemaSchema();
$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 DoctrineDBALSchemaComparator();
$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
ORM
Object Relational Mapper
What is ORM?
“Technique for converting data between
incompatible type systems in object-
oriented programming languages.”
http://en.wikipedia.org/wiki/Object-relational_mapping
The ORM is built on top of
Common and DBAL
ORM Goals
- Maintain transparency
- Keep domain and persistence layer
separated
- Performance
- Consistent and decoupled API
- Well defined semantics
http://www.doctrine-project.org/projects/orm/download
Download
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
Architecture
- No more base class required
- Values stored in object properties
- Persistence is done transparently
namespace Entities;
class User
{
private $id;
private $name;
}
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
Create EntityManager
Create a new EntityManager instance:
$config = new DoctrineORMConfiguration();
$config->setMetadataCacheImpl(new DoctrineCommonCacheArrayCache);
$driverImpl = $config->newDefaultAnnotationDriver(array(__DIR__."/Entities"));
$config->setMetadataDriverImpl($driverImpl);
$config->setProxyDir(__DIR__ . '/Proxies');
$config->setProxyNamespace('Proxies');
$em = DoctrineORMEntityManager::create($conn, $config);
Map entities to RDBMS tables
Entities are just regular PHP objects
namespace Entities;
class User
{
private $id;
private $name;
}
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;
}
Map entities to RDBMS tables
Entities are just regular PHP objects:
Mapped By:
- Annotations
- YAML
EntitiesUser:
type: entity
table: users
id:
id:
type: integer
generator:
strategy: AUTO
fields:
name:
type: string
length: 255
Map entities to RDBMS tables
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="EntitiesUser" table="users">
<id name="id" type="integer">
<generator strategy="AUTO"/>
</id>
<field name="name" type="string" length="50"/>
</entity>
</doctrine-mapping>
Mapping Performance
- Only parsed once
- Cached using configured cache driver
- Subsequent requests pull mapping
information from configured cache
driver
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();
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
Working with Objects
Removing an object:
$user = $em->getRepository('User')
->find(array('name' => 'jwage'));
// schedule for deletion
$em->remove($user);
$em->flush(); // issues delete
Transactions
Implicit:
EntityManager#flush() will begin and commit/rollback a transaction
$user = new User;
$user->setName('George');
$em->persist($user);
$em->flush();
Transactions
Explicit:
// $em instanceof EntityManager
$em->getConnection()->beginTransaction(); // suspend auto-commit
try {
//... 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;
}
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);
});
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;
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;
Which is faster?
- The one using no ORM, and no
abstraction at all?
- Or the one using the Doctrine ORM?
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
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;
Much Faster
Transactions matter and can affect
performance greater than any code
optimization!
Doctrine2 0.0094 seconds
mysql_query 0.0165 seconds
0.0028
Locking Support
Optimistic locking with integer:
class User
{
// ...
/** @Version @Column(type="integer") */
private $version;
// ...
}
Locking Support
Optimistic locking with timestamp:
class User
{
// ...
/** @Version @Column(type="datetime") */
private $version;
// ...
}
Locking Support
Verify version when finding:
use DoctrineDBALLockMode;
use DoctrineORMOptimisticLockException;
$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!";
}
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, DoctrineDBALLockMode::OPTIMISTIC, $postVersion);
DQL
Doctrine Query Language
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.
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
Doctrine Query Language
$q = $em->createQuery('SELECT u FROM User u');
$users = $q->execute();
Query Builder
Same query built using the QueryBuilder
$qb = $em->createQueryBuilder()
->select('u')
->from('User', 'u');
$q = $qb->getQuery();
$users = $q->execute();
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();
Executing Queries
Executing and getting results
$users = $query->execute();
foreach ($users as $user) {
// ...
foreach ($user->getGroups() as $group) {
// ...
}
}
Executing Queries
Execute query and iterate over results
keeping memory usage low:
foreach ($query->iterate() as $user) {
// ...
foreach ($user->getGroups() as $group) {
// ...
}
}
Result Cache
Optionally cache the results of your
queries in your driver of choice:
$cacheDriver = new DoctrineCommonCacheApcCache();
$config->setResultCacheImpl($cacheDriver);
$query = $em->createQuery('select u from EntitiesUser u');
$query->useResultCache(true, 3600, 'my_query_name');
$users = $query->execute();
$users = $query->execute(); // 2nd time pulls from cache
Inheritance
Doctrine supports mapping entities that
use inheritance with the following
strategies:
- Mapped Superclass
- Single Table Inheritance
- Class Table Inheritance
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
}
Single Table Inheritance
/**
* @Entity
* @InheritanceType("SINGLE_TABLE")
* @DiscriminatorColumn(name="discr", type="string")
* @DiscriminatorMap({"person" = "Person", "employee" = "Employee"})
*/
class Person
{
// ...
}
/**
* @Entity
*/
class Employee extends Person
{
// ...
}
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.
Class Table Inheritance
/**
* @Entity
* @InheritanceType("JOINED")
* @DiscriminatorColumn(name="discr", type="string")
* @DiscriminatorMap({"person" = "Person", "employee" = "Employee"})
*/
class Person
{
// ...
}
/** @Entity */
class Employee extends Person
{
// ...
}
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.
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!
}
}
Bulk Update with DQL
$q = $em->createQuery('update Manager m set m.salary = m.salary * 0.9');
$numUpdated = $q->execute();
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;
}
Bulk Delete with DQL
$q = $em->createQuery('delete from Manager m where m.salary > 100000');
$numDeleted = $q->execute();
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;
}
Events
Doctrine triggers events throughout the
lifecycle of objects it manages:
- preRemove
- postRemove
- prePersist
- postPersist
- preUpdate
- postUpdate
- preLoad
- postLoad
Example
/**
* @Entity
* @HasLifecycleCallbacks
*/
class BlogPost
{
// ...
/** @PreUpdate */
public function prePersist()
{
$this->createdAt = new DateTime();
}
/** @PreUpdate */
public function preUpdate()
{
$this->updatedAt = new DateTime();
}
}
Using Raw SQL
- Write a raw SQL string
- Map the result set of the SQL query
using a ResultSetMapping instance
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();
Why use an object
mapper?
Encapsulate your domain in an object oriented interface
Encapsulation
The organization of your domain logic in an OO way
improved maintainability
Maintainability
Keeping a clean OO domain model makes your business
logic easily testable for improved stability
Testability
Write portable and thin application controller code and
fat models.
Portability
Questions?
- http://www.twitter.com/jwage
- http://www.facebook.com/jwage
- http://www.jwage.com
OpenSky is hiring! Inquire via e-mail at
jwage@theopenskyproject.com or in person
after this presentation!

More Related Content

What's hot

Crafting beautiful software
Crafting beautiful softwareCrafting beautiful software
Crafting beautiful softwareJorn Oomen
 
PHP 5.3 and Lithium: the most rad php framework
PHP 5.3 and Lithium: the most rad php frameworkPHP 5.3 and Lithium: the most rad php framework
PHP 5.3 and Lithium: the most rad php frameworkG Woo
 
Doctrine with Symfony - SymfonyCon 2019
Doctrine with Symfony - SymfonyCon 2019Doctrine with Symfony - SymfonyCon 2019
Doctrine with Symfony - SymfonyCon 2019julien pauli
 
Doctrine fixtures
Doctrine fixturesDoctrine fixtures
Doctrine fixturesBill Chang
 
Metaprogramovanie #1
Metaprogramovanie #1Metaprogramovanie #1
Metaprogramovanie #1Jano Suchal
 
Doctrine For Beginners
Doctrine For BeginnersDoctrine For Beginners
Doctrine For BeginnersJonathan Wage
 
Hacking hhvm
Hacking hhvmHacking hhvm
Hacking hhvmwajrcs
 
SPL: The Missing Link in Development
SPL: The Missing Link in DevelopmentSPL: The Missing Link in Development
SPL: The Missing Link in Developmentjsmith92
 
Who killed object oriented design?
Who killed object oriented design?Who killed object oriented design?
Who killed object oriented design?Amir Barylko
 
PHP 5.3 Overview
PHP 5.3 OverviewPHP 5.3 Overview
PHP 5.3 Overviewjsmith92
 
Design how your objects talk through mocking
Design how your objects talk through mockingDesign how your objects talk through mocking
Design how your objects talk through mockingKonstantin Kudryashov
 
Silex meets SOAP & REST
Silex meets SOAP & RESTSilex meets SOAP & REST
Silex meets SOAP & RESTHugo Hamon
 
Crud operations using aws dynamo db with flask ap is and boto3
Crud operations using aws dynamo db with flask ap is and boto3Crud operations using aws dynamo db with flask ap is and boto3
Crud operations using aws dynamo db with flask ap is and boto3Katy Slemon
 
Design Patterns avec PHP 5.3, Symfony et Pimple
Design Patterns avec PHP 5.3, Symfony et PimpleDesign Patterns avec PHP 5.3, Symfony et Pimple
Design Patterns avec PHP 5.3, Symfony et PimpleHugo Hamon
 
Internationalizing CakePHP Applications
Internationalizing CakePHP ApplicationsInternationalizing CakePHP Applications
Internationalizing CakePHP ApplicationsPierre MARTIN
 
Drupal Field API. Practical usage
Drupal Field API. Practical usageDrupal Field API. Practical usage
Drupal Field API. Practical usagePavel Makhrinsky
 

What's hot (20)

Crafting beautiful software
Crafting beautiful softwareCrafting beautiful software
Crafting beautiful software
 
PHP 5.3 and Lithium: the most rad php framework
PHP 5.3 and Lithium: the most rad php frameworkPHP 5.3 and Lithium: the most rad php framework
PHP 5.3 and Lithium: the most rad php framework
 
Doctrine with Symfony - SymfonyCon 2019
Doctrine with Symfony - SymfonyCon 2019Doctrine with Symfony - SymfonyCon 2019
Doctrine with Symfony - SymfonyCon 2019
 
Doctrine fixtures
Doctrine fixturesDoctrine fixtures
Doctrine fixtures
 
Lithium Best
Lithium Best Lithium Best
Lithium Best
 
Metaprogramovanie #1
Metaprogramovanie #1Metaprogramovanie #1
Metaprogramovanie #1
 
Doctrine For Beginners
Doctrine For BeginnersDoctrine For Beginners
Doctrine For Beginners
 
Hacking hhvm
Hacking hhvmHacking hhvm
Hacking hhvm
 
SPL: The Missing Link in Development
SPL: The Missing Link in DevelopmentSPL: The Missing Link in Development
SPL: The Missing Link in Development
 
Drupal Render API
Drupal Render APIDrupal Render API
Drupal Render API
 
Advanced Querying with CakePHP 3
Advanced Querying with CakePHP 3Advanced Querying with CakePHP 3
Advanced Querying with CakePHP 3
 
Who killed object oriented design?
Who killed object oriented design?Who killed object oriented design?
Who killed object oriented design?
 
PHP 5.3 Overview
PHP 5.3 OverviewPHP 5.3 Overview
PHP 5.3 Overview
 
Design how your objects talk through mocking
Design how your objects talk through mockingDesign how your objects talk through mocking
Design how your objects talk through mocking
 
Silex meets SOAP & REST
Silex meets SOAP & RESTSilex meets SOAP & REST
Silex meets SOAP & REST
 
Crud operations using aws dynamo db with flask ap is and boto3
Crud operations using aws dynamo db with flask ap is and boto3Crud operations using aws dynamo db with flask ap is and boto3
Crud operations using aws dynamo db with flask ap is and boto3
 
Drupal 8 migrate!
Drupal 8 migrate!Drupal 8 migrate!
Drupal 8 migrate!
 
Design Patterns avec PHP 5.3, Symfony et Pimple
Design Patterns avec PHP 5.3, Symfony et PimpleDesign Patterns avec PHP 5.3, Symfony et Pimple
Design Patterns avec PHP 5.3, Symfony et Pimple
 
Internationalizing CakePHP Applications
Internationalizing CakePHP ApplicationsInternationalizing CakePHP Applications
Internationalizing CakePHP Applications
 
Drupal Field API. Practical usage
Drupal Field API. Practical usageDrupal Field API. Practical usage
Drupal Field API. Practical usage
 

Similar to ZendCon2010 The Doctrine Project

Advanced Php - Macq Electronique 2010
Advanced Php - Macq Electronique 2010Advanced Php - Macq Electronique 2010
Advanced Php - Macq Electronique 2010Michelangelo van Dam
 
OOP Adventures with XOOPS
OOP Adventures with XOOPSOOP Adventures with XOOPS
OOP Adventures with XOOPSxoopsproject
 
symfony on action - WebTech 207
symfony on action - WebTech 207symfony on action - WebTech 207
symfony on action - WebTech 207patter
 
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQuery
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQueryRemedie: Building a desktop app with HTTP::Engine, SQLite and jQuery
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQueryTatsuhiko Miyagawa
 
Writing HTML5 Web Apps using Backbone.js and GAE
Writing HTML5 Web Apps using Backbone.js and GAEWriting HTML5 Web Apps using Backbone.js and GAE
Writing HTML5 Web Apps using Backbone.js and GAERon Reiter
 
ZendCon2010 Doctrine MongoDB ODM
ZendCon2010 Doctrine MongoDB ODMZendCon2010 Doctrine MongoDB ODM
ZendCon2010 Doctrine MongoDB ODMJonathan Wage
 
WebNet Conference 2012 - Designing complex applications using html5 and knock...
WebNet Conference 2012 - Designing complex applications using html5 and knock...WebNet Conference 2012 - Designing complex applications using html5 and knock...
WebNet Conference 2012 - Designing complex applications using html5 and knock...Fabio Franzini
 
Exploring Symfony's Code
Exploring Symfony's CodeExploring Symfony's Code
Exploring Symfony's CodeWildan Maulana
 
Power shell examples_v4
Power shell examples_v4Power shell examples_v4
Power shell examples_v4JoeDinaso
 
PHP - PDO Objects
PHP - PDO ObjectsPHP - PDO Objects
PHP - PDO ObjectsAJINKYA N
 
Basic Oops concept of PHP
Basic Oops concept of PHPBasic Oops concept of PHP
Basic Oops concept of PHPRohan Sharma
 
Oops concepts in php
Oops concepts in phpOops concepts in php
Oops concepts in phpCPD INDIA
 
FFW Gabrovo PMG - PHP OOP Part 3
FFW Gabrovo PMG - PHP OOP Part 3FFW Gabrovo PMG - PHP OOP Part 3
FFW Gabrovo PMG - PHP OOP Part 3Toni Kolev
 

Similar to ZendCon2010 The Doctrine Project (20)

Advanced Php - Macq Electronique 2010
Advanced Php - Macq Electronique 2010Advanced Php - Macq Electronique 2010
Advanced Php - Macq Electronique 2010
 
OOP Adventures with XOOPS
OOP Adventures with XOOPSOOP Adventures with XOOPS
OOP Adventures with XOOPS
 
symfony on action - WebTech 207
symfony on action - WebTech 207symfony on action - WebTech 207
symfony on action - WebTech 207
 
Django
DjangoDjango
Django
 
Lecture9_OOPHP_SPring2023.pptx
Lecture9_OOPHP_SPring2023.pptxLecture9_OOPHP_SPring2023.pptx
Lecture9_OOPHP_SPring2023.pptx
 
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQuery
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQueryRemedie: Building a desktop app with HTTP::Engine, SQLite and jQuery
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQuery
 
Writing HTML5 Web Apps using Backbone.js and GAE
Writing HTML5 Web Apps using Backbone.js and GAEWriting HTML5 Web Apps using Backbone.js and GAE
Writing HTML5 Web Apps using Backbone.js and GAE
 
ZendCon2010 Doctrine MongoDB ODM
ZendCon2010 Doctrine MongoDB ODMZendCon2010 Doctrine MongoDB ODM
ZendCon2010 Doctrine MongoDB ODM
 
Laravel intake 37 all days
Laravel intake 37 all daysLaravel intake 37 all days
Laravel intake 37 all days
 
WebNet Conference 2012 - Designing complex applications using html5 and knock...
WebNet Conference 2012 - Designing complex applications using html5 and knock...WebNet Conference 2012 - Designing complex applications using html5 and knock...
WebNet Conference 2012 - Designing complex applications using html5 and knock...
 
Oops in php
Oops in phpOops in php
Oops in php
 
Angular Schematics
Angular SchematicsAngular Schematics
Angular Schematics
 
Exploring Symfony's Code
Exploring Symfony's CodeExploring Symfony's Code
Exploring Symfony's Code
 
Introduction Php
Introduction PhpIntroduction Php
Introduction Php
 
Power shell examples_v4
Power shell examples_v4Power shell examples_v4
Power shell examples_v4
 
PHP - PDO Objects
PHP - PDO ObjectsPHP - PDO Objects
PHP - PDO Objects
 
Basic Oops concept of PHP
Basic Oops concept of PHPBasic Oops concept of PHP
Basic Oops concept of PHP
 
Oops concepts in php
Oops concepts in phpOops concepts in php
Oops concepts in php
 
Unittests für Dummies
Unittests für DummiesUnittests für Dummies
Unittests für Dummies
 
FFW Gabrovo PMG - PHP OOP Part 3
FFW Gabrovo PMG - PHP OOP Part 3FFW Gabrovo PMG - PHP OOP Part 3
FFW Gabrovo PMG - PHP OOP Part 3
 

More from Jonathan Wage

OpenSky Infrastructure
OpenSky InfrastructureOpenSky Infrastructure
OpenSky InfrastructureJonathan Wage
 
Doctrine In The Real World sflive2011 Paris
Doctrine In The Real World sflive2011 ParisDoctrine In The Real World sflive2011 Paris
Doctrine In The Real World sflive2011 ParisJonathan Wage
 
Symfony2 from the Trenches
Symfony2 from the TrenchesSymfony2 from the Trenches
Symfony2 from the TrenchesJonathan Wage
 
Doctrine in the Real World
Doctrine in the Real WorldDoctrine in the Real World
Doctrine in the Real WorldJonathan Wage
 
Symfony Day 2010 Doctrine MongoDB ODM
Symfony Day 2010 Doctrine MongoDB ODMSymfony Day 2010 Doctrine MongoDB ODM
Symfony Day 2010 Doctrine MongoDB ODMJonathan Wage
 
Doctrine MongoDB Object Document Mapper
Doctrine MongoDB Object Document MapperDoctrine MongoDB Object Document Mapper
Doctrine MongoDB Object Document MapperJonathan Wage
 
Symfony2 and Doctrine2 Integration
Symfony2 and Doctrine2 IntegrationSymfony2 and Doctrine2 Integration
Symfony2 and Doctrine2 IntegrationJonathan Wage
 
Doctrine 2 - Enterprise Persistence Layer For PHP
Doctrine 2 - Enterprise Persistence Layer For PHPDoctrine 2 - Enterprise Persistence Layer For PHP
Doctrine 2 - Enterprise Persistence Layer For PHPJonathan Wage
 
Introduction To Doctrine 2
Introduction To Doctrine 2Introduction To Doctrine 2
Introduction To Doctrine 2Jonathan Wage
 
Doctrine 2 - Not The Same Old Php Orm
Doctrine 2 - Not The Same Old Php OrmDoctrine 2 - Not The Same Old Php Orm
Doctrine 2 - Not The Same Old Php OrmJonathan Wage
 
Doctrine 2: Enterprise Persistence Layer for PHP
Doctrine 2: Enterprise Persistence Layer for PHPDoctrine 2: Enterprise Persistence Layer for PHP
Doctrine 2: Enterprise Persistence Layer for PHPJonathan Wage
 
Sympal A Cmf Based On Symfony
Sympal   A Cmf Based On SymfonySympal   A Cmf Based On Symfony
Sympal A Cmf Based On SymfonyJonathan Wage
 
Symfony 1.3 + Doctrine 1.2
Symfony 1.3 + Doctrine 1.2Symfony 1.3 + Doctrine 1.2
Symfony 1.3 + Doctrine 1.2Jonathan Wage
 
Sympal - The flexible Symfony CMS
Sympal - The flexible Symfony CMSSympal - The flexible Symfony CMS
Sympal - The flexible Symfony CMSJonathan Wage
 
What's new in Doctrine
What's new in DoctrineWhat's new in Doctrine
What's new in DoctrineJonathan Wage
 
Sympal - Symfony CMS Preview
Sympal - Symfony CMS PreviewSympal - Symfony CMS Preview
Sympal - Symfony CMS PreviewJonathan Wage
 
Doctrine Php Object Relational Mapper
Doctrine Php Object Relational MapperDoctrine Php Object Relational Mapper
Doctrine Php Object Relational MapperJonathan Wage
 
Sympal - The Flexible Symfony Cms
Sympal - The Flexible Symfony CmsSympal - The Flexible Symfony Cms
Sympal - The Flexible Symfony CmsJonathan Wage
 

More from Jonathan Wage (20)

OpenSky Infrastructure
OpenSky InfrastructureOpenSky Infrastructure
OpenSky Infrastructure
 
Doctrine In The Real World sflive2011 Paris
Doctrine In The Real World sflive2011 ParisDoctrine In The Real World sflive2011 Paris
Doctrine In The Real World sflive2011 Paris
 
Symfony2 from the Trenches
Symfony2 from the TrenchesSymfony2 from the Trenches
Symfony2 from the Trenches
 
Doctrine in the Real World
Doctrine in the Real WorldDoctrine in the Real World
Doctrine in the Real World
 
Symfony Day 2010 Doctrine MongoDB ODM
Symfony Day 2010 Doctrine MongoDB ODMSymfony Day 2010 Doctrine MongoDB ODM
Symfony Day 2010 Doctrine MongoDB ODM
 
Doctrine MongoDB Object Document Mapper
Doctrine MongoDB Object Document MapperDoctrine MongoDB Object Document Mapper
Doctrine MongoDB Object Document Mapper
 
Libertyvasion2010
Libertyvasion2010Libertyvasion2010
Libertyvasion2010
 
Symfony2 and Doctrine2 Integration
Symfony2 and Doctrine2 IntegrationSymfony2 and Doctrine2 Integration
Symfony2 and Doctrine2 Integration
 
Doctrine 2 - Enterprise Persistence Layer For PHP
Doctrine 2 - Enterprise Persistence Layer For PHPDoctrine 2 - Enterprise Persistence Layer For PHP
Doctrine 2 - Enterprise Persistence Layer For PHP
 
Introduction To Doctrine 2
Introduction To Doctrine 2Introduction To Doctrine 2
Introduction To Doctrine 2
 
Doctrine 2 - Not The Same Old Php Orm
Doctrine 2 - Not The Same Old Php OrmDoctrine 2 - Not The Same Old Php Orm
Doctrine 2 - Not The Same Old Php Orm
 
Doctrine 2: Enterprise Persistence Layer for PHP
Doctrine 2: Enterprise Persistence Layer for PHPDoctrine 2: Enterprise Persistence Layer for PHP
Doctrine 2: Enterprise Persistence Layer for PHP
 
Sympal A Cmf Based On Symfony
Sympal   A Cmf Based On SymfonySympal   A Cmf Based On Symfony
Sympal A Cmf Based On Symfony
 
Symfony 1.3 + Doctrine 1.2
Symfony 1.3 + Doctrine 1.2Symfony 1.3 + Doctrine 1.2
Symfony 1.3 + Doctrine 1.2
 
Sympal - The flexible Symfony CMS
Sympal - The flexible Symfony CMSSympal - The flexible Symfony CMS
Sympal - The flexible Symfony CMS
 
What's new in Doctrine
What's new in DoctrineWhat's new in Doctrine
What's new in Doctrine
 
What Is Doctrine?
What Is Doctrine?What Is Doctrine?
What Is Doctrine?
 
Sympal - Symfony CMS Preview
Sympal - Symfony CMS PreviewSympal - Symfony CMS Preview
Sympal - Symfony CMS Preview
 
Doctrine Php Object Relational Mapper
Doctrine Php Object Relational MapperDoctrine Php Object Relational Mapper
Doctrine Php Object Relational Mapper
 
Sympal - The Flexible Symfony Cms
Sympal - The Flexible Symfony CmsSympal - The Flexible Symfony Cms
Sympal - The Flexible Symfony Cms
 

Recently uploaded

Install Stable Diffusion in windows machine
Install Stable Diffusion in windows machineInstall Stable Diffusion in windows machine
Install Stable Diffusion in windows machinePadma Pradeep
 
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)Wonjun Hwang
 
CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):comworks
 
Developer Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLDeveloper Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLScyllaDB
 
Unleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubUnleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubKalema Edgar
 
Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Mattias Andersson
 
Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Commit University
 
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024BookNet Canada
 
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
"Federated learning: out of reach no matter how close",Oleksandr LapshynFwdays
 
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks..."LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...Fwdays
 
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024BookNet Canada
 
Artificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptxArtificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptxhariprasad279825
 
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...Patryk Bandurski
 
Search Engine Optimization SEO PDF for 2024.pdf
Search Engine Optimization SEO PDF for 2024.pdfSearch Engine Optimization SEO PDF for 2024.pdf
Search Engine Optimization SEO PDF for 2024.pdfRankYa
 
Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!Manik S Magar
 
"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii SoldatenkoFwdays
 
Dev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebDev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebUiPathCommunity
 
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmaticsKotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmaticscarlostorres15106
 
Streamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupStreamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupFlorian Wilhelm
 

Recently uploaded (20)

Install Stable Diffusion in windows machine
Install Stable Diffusion in windows machineInstall Stable Diffusion in windows machine
Install Stable Diffusion in windows machine
 
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
 
CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):
 
Developer Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLDeveloper Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQL
 
Unleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubUnleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding Club
 
Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?
 
Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!
 
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
 
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
 
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks..."LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
 
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
 
Artificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptxArtificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptx
 
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
 
DMCC Future of Trade Web3 - Special Edition
DMCC Future of Trade Web3 - Special EditionDMCC Future of Trade Web3 - Special Edition
DMCC Future of Trade Web3 - Special Edition
 
Search Engine Optimization SEO PDF for 2024.pdf
Search Engine Optimization SEO PDF for 2024.pdfSearch Engine Optimization SEO PDF for 2024.pdf
Search Engine Optimization SEO PDF for 2024.pdf
 
Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!
 
"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko
 
Dev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebDev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio Web
 
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmaticsKotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
 
Streamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupStreamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project Setup
 

ZendCon2010 The Doctrine Project

  • 1. Jonathan H. Wage | OpenSky The Doctrine Project
  • 2. Who am I? 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
  • 3. I work at What is OpenSky? “a social commerce platform” Based in New York and is a major open source software advocate http://www.shopopensky.com
  • 5. 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)
  • 6. Who is on the team? • Roman S. Borschel • Guilherme Blanco • Benjamin Eberlei • Bulat Shakirzyanov • Jonathan H.Wage
  • 7. 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
  • 8. Doctrine Libraries - Database Abstraction Layer - Database Migrations - Object Relational Mapper - MongoDB Object Document Manager - CouchDB Object Document Manager
  • 10. 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
  • 11. Can be used standalone
  • 12. Evolved fork of PEAR MDB, MDB2, Zend_Db, etc.
  • 13. Download You can download a standalone package to get started using the DBAL: http://www.doctrine-project.org/projects/dbal/download
  • 14. Autoloader To use any Doctrine library you must register an autoloader: use DoctrineCommonClassLoader; require '/path/to/doctrine-common/lib/Doctrine/Common/ClassLoader.php'; $classLoader = new ClassLoader('DoctrineDBAL', '/path/to/doctrine-dbal/lib'); $classLoader->register();
  • 15. Create a Connection $config = new DoctrineDBALConfiguration(); //.. $connectionParams = array( 'dbname' => 'mydb', 'user' => 'user', 'password' => 'secret', 'host' => 'localhost', 'driver' => 'pdo_mysql', ); $conn = DriverManager::getConnection($connectionParams);
  • 16. Data API prepare($sql) - Prepare a given sql statement and return the DoctrineDBALDriver 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.
  • 17. Very Similar to PDO $users = $conn->fetchAll('SELECT * FROM users');
  • 18. Schema Manager Learn about and modify your database through the SchemaManager: $sm = $conn->getSchemaManager();
  • 20. Introspection API $tables = $sm->listTables(); foreach ($tables as $table) { $columns = $sm->listTableColumns($table); // ... }
  • 21. DDL Statements Progromatically issue DDL statements: $columns = array( 'id' => array( 'type' => DoctrineDBALType::getType('integer'), 'autoincrement' => true, 'primary' => true, 'notnull' => true ), 'test' => array( 'type' => DoctrineDBALType::getType('string'), 'length' => 255 ) ); $options = array(); $sm->createTable('new_table', $columns, $options);
  • 22. DDL Statements Progromatically issue DDL statements: $definition = array( 'name' => 'user_id_fk', 'local' => 'user_id', 'foreign' => 'id', 'foreignTable' => 'user' ); $sm->createForeignKey('profile', $definition);
  • 23. 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 }
  • 24. Drop and Create Database try { $sm->dropDatabase('test_db'); } catch (Exception $e) {} $sm->createDatabase('test_db');
  • 25. 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');
  • 26. Schema Representation $platform = $em->getConnection()->getDatabasePlatform(); $schema = new DoctrineDBALSchemaSchema(); $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")) )
  • 27. 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 )
  • 28. Comparing Schemas $fromSchema = new DoctrineDBALSchemaSchema(); $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 DoctrineDBALSchemaSchema(); $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 DoctrineDBALSchemaComparator(); $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
  • 30. What is ORM? “Technique for converting data between incompatible type systems in object- oriented programming languages.” http://en.wikipedia.org/wiki/Object-relational_mapping
  • 31. The ORM is built on top of Common and DBAL
  • 32. ORM Goals - Maintain transparency - Keep domain and persistence layer separated - Performance - Consistent and decoupled API - Well defined semantics
  • 34. 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
  • 35. Architecture - No more base class required - Values stored in object properties - Persistence is done transparently namespace Entities; class User { private $id; private $name; }
  • 36. 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
  • 37. Create EntityManager Create a new EntityManager instance: $config = new DoctrineORMConfiguration(); $config->setMetadataCacheImpl(new DoctrineCommonCacheArrayCache); $driverImpl = $config->newDefaultAnnotationDriver(array(__DIR__."/Entities")); $config->setMetadataDriverImpl($driverImpl); $config->setProxyDir(__DIR__ . '/Proxies'); $config->setProxyNamespace('Proxies'); $em = DoctrineORMEntityManager::create($conn, $config);
  • 38. Map entities to RDBMS tables Entities are just regular PHP objects namespace Entities; class User { private $id; private $name; }
  • 39. 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; }
  • 40. Map entities to RDBMS tables Entities are just regular PHP objects: Mapped By: - Annotations - YAML EntitiesUser: type: entity table: users id: id: type: integer generator: strategy: AUTO fields: name: type: string length: 255
  • 41. Map entities to RDBMS tables 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="EntitiesUser" table="users"> <id name="id" type="integer"> <generator strategy="AUTO"/> </id> <field name="name" type="string" length="50"/> </entity> </doctrine-mapping>
  • 42. Mapping Performance - Only parsed once - Cached using configured cache driver - Subsequent requests pull mapping information from configured cache driver
  • 43. 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();
  • 44. 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
  • 45. Working with Objects Removing an object: $user = $em->getRepository('User') ->find(array('name' => 'jwage')); // schedule for deletion $em->remove($user); $em->flush(); // issues delete
  • 46. Transactions Implicit: EntityManager#flush() will begin and commit/rollback a transaction $user = new User; $user->setName('George'); $em->persist($user); $em->flush();
  • 47. Transactions Explicit: // $em instanceof EntityManager $em->getConnection()->beginTransaction(); // suspend auto-commit try { //... 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; }
  • 48. 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); });
  • 49. 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;
  • 50. 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;
  • 51. Which is faster? - The one using no ORM, and no abstraction at all? - Or the one using the Doctrine ORM?
  • 52. 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
  • 53. 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;
  • 54. Much Faster Transactions matter and can affect performance greater than any code optimization! Doctrine2 0.0094 seconds mysql_query 0.0165 seconds 0.0028
  • 55. Locking Support Optimistic locking with integer: class User { // ... /** @Version @Column(type="integer") */ private $version; // ... }
  • 56. Locking Support Optimistic locking with timestamp: class User { // ... /** @Version @Column(type="datetime") */ private $version; // ... }
  • 57. Locking Support Verify version when finding: use DoctrineDBALLockMode; use DoctrineORMOptimisticLockException; $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!"; }
  • 58. 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, DoctrineDBALLockMode::OPTIMISTIC, $postVersion);
  • 60. 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.
  • 61. 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
  • 62. Doctrine Query Language $q = $em->createQuery('SELECT u FROM User u'); $users = $q->execute();
  • 63. Query Builder Same query built using the QueryBuilder $qb = $em->createQueryBuilder() ->select('u') ->from('User', 'u'); $q = $qb->getQuery(); $users = $q->execute();
  • 64. 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();
  • 65. Executing Queries Executing and getting results $users = $query->execute(); foreach ($users as $user) { // ... foreach ($user->getGroups() as $group) { // ... } }
  • 66. Executing Queries Execute query and iterate over results keeping memory usage low: foreach ($query->iterate() as $user) { // ... foreach ($user->getGroups() as $group) { // ... } }
  • 67. Result Cache Optionally cache the results of your queries in your driver of choice: $cacheDriver = new DoctrineCommonCacheApcCache(); $config->setResultCacheImpl($cacheDriver); $query = $em->createQuery('select u from EntitiesUser u'); $query->useResultCache(true, 3600, 'my_query_name'); $users = $query->execute(); $users = $query->execute(); // 2nd time pulls from cache
  • 68. Inheritance Doctrine supports mapping entities that use inheritance with the following strategies: - Mapped Superclass - Single Table Inheritance - Class Table Inheritance
  • 69. 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 }
  • 70. Single Table Inheritance /** * @Entity * @InheritanceType("SINGLE_TABLE") * @DiscriminatorColumn(name="discr", type="string") * @DiscriminatorMap({"person" = "Person", "employee" = "Employee"}) */ class Person { // ... } /** * @Entity */ class Employee extends Person { // ... }
  • 71. 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.
  • 72. Class Table Inheritance /** * @Entity * @InheritanceType("JOINED") * @DiscriminatorColumn(name="discr", type="string") * @DiscriminatorMap({"person" = "Person", "employee" = "Employee"}) */ class Person { // ... } /** @Entity */ class Employee extends Person { // ... }
  • 73. 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.
  • 74. 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! } }
  • 75. Bulk Update with DQL $q = $em->createQuery('update Manager m set m.salary = m.salary * 0.9'); $numUpdated = $q->execute();
  • 76. 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; }
  • 77. Bulk Delete with DQL $q = $em->createQuery('delete from Manager m where m.salary > 100000'); $numDeleted = $q->execute();
  • 78. 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; }
  • 79. Events Doctrine triggers events throughout the lifecycle of objects it manages: - preRemove - postRemove - prePersist - postPersist - preUpdate - postUpdate - preLoad - postLoad
  • 80. Example /** * @Entity * @HasLifecycleCallbacks */ class BlogPost { // ... /** @PreUpdate */ public function prePersist() { $this->createdAt = new DateTime(); } /** @PreUpdate */ public function preUpdate() { $this->updatedAt = new DateTime(); } }
  • 81. Using Raw SQL - Write a raw SQL string - Map the result set of the SQL query using a ResultSetMapping instance
  • 82. 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();
  • 83. Why use an object mapper?
  • 84. Encapsulate your domain in an object oriented interface Encapsulation
  • 85. The organization of your domain logic in an OO way improved maintainability Maintainability
  • 86. Keeping a clean OO domain model makes your business logic easily testable for improved stability Testability
  • 87. Write portable and thin application controller code and fat models. Portability
  • 88. Questions? - http://www.twitter.com/jwage - http://www.facebook.com/jwage - http://www.jwage.com OpenSky is hiring! Inquire via e-mail at jwage@theopenskyproject.com or in person after this presentation!