February 16th, 2010

Doctrine 2: Jonathan Wage at #sflive2010

I'm attending Symfony Live. Below are my notes from Jon Wage's presentation on the upcoming Doctrine 2.0. My more opinionated, less explanatory comments tend to be in parentheses. Everything paraphrased brutally to keep up.



"Not the same old PHP ORM"

100% re-written codebase for PHP 5.3

"Are you scared? You shouldn't be! Change is a good thing!"

5,000 records hydrate in 1.4 seconds. Whee!

10,000 records in 3.5 seconds.

Twice the data and still faster than Doctrine 1.

% improvement over Doctrine 2 is big. PHP 5.3 is a big win with a heavily OO framework. Better optimized hydration algorithm. New query and result caching implementations. All around more explicit and less magical code. Killed the magical aspect of Doctrine 1.

"The only valid measurement of code quality: WTFs/minute"

Good code: "WTF..... WTF"

Bad code: "WTF WTF WTF WTF WTF WTF"

Doctrine 1 magical features are both a blessing and a curse

Magic is great when it works but also causes pain. When it doesn't work it is hard to debug. Edge cases are hard to fix. Edge cases are hard to work around. Everything is okay until you try to go outside the magic box. Magic is slow.

Replacement: OOP. Object composition, inheritance, aggregation, containment, encapsulation, etc.

Will it have behaviors? Yes and no. No "model behaviors." Made up to work with Doctrine's "intrusive architecture." Tries to do things PHP doesn't allow, results in a lot of problems (like sfMixin did).

If you could do it in 1 you can do it in 2, just differnetly. Natural OOP that wraps/extends Doctrine code or is meant to be wrapped or extended by your entities. (Injection? Factories? How do you apply two extensions?)

What did we use to build it? phpUnit 3.4.10 unit testing. Phing. Symfony YAML. Sismo. Subversion. Jira. And Trac.

Doctrine 2 Architecture

"Everything is an entity." A lightweight persistent domain object, a regular PHP class. You don't extend a base doctrine class. No final methods.
namespace Entities;
class User
{  
  private $id;
  private $name;
  private $address;
}
Note use of namespaces.

EntityManager is central access point to ORM. Used to query for persistent objects. Employs transactional write behind strategy that delays execution of SQL to execute it in the most efficient way.

Tests ran against multiple databases. Sqlite, MySQL, Oracle, PgSQL, more to come.

Unit Testing

Impressive #s of test cases and assertions. Tests run in a few seconds instead of 30-40 seconds. Very granular, explicit unit tests, not many functional tests. Easier to debug. Continuously integrated by Sismo.

No, Sismo is not available yet, bug Fabien! (We have a pkcommit script of our own that doesn't let you commit without a warning unless you pass symfony tests, which is a nice poor man's version)

Database Abstraction Layer

Standalone package and namespace (Doctrine\DBAL).

Can be used standalone.

Much improved over Doctrine 1 in regards to the API for database introspection and schema management. (Can you alter a table in a more db-portable way wrt type names?)

Hopefully defacto standard DBAL for PHP 5.3 in the future like MDB was for 4.x
prepare($sql)
executeUpdate($sql, $params)
fetchAll... fetchBoth($sql, $params) - both assoc and flat...


DBAL Introspection
listDatabases
listFunctions
listSequences
listTableColumns($tableName)
Schema Representation
$schema = new \Doctrine\DBAL\Schema\Schema();  createTable("my_table");
Replacing diff tool with the ability to compare schema objects.

Doctrine 2 Annotations
/** @Id @Column(type="integer") @GeneratedValue */
private $id;
This gets parsed out. Alternative to schema.yml. (Lets you write everything in one place, so perhaps more convenient.)

Things to Notice

Entities no longer require extending a heavy base class Domain model has no magic, is not imposed on by Doctrine and is defined by raw PHP objects and normal OO Big performance impact Easier to understand due to less magic. As Fabien says, "Kill the magic..."

(We've introduced a little magic here and there in our own stuff, maybe it's less evil within a shop than without.)

Doctrine 2 *does* allow YAML.
Entities\Address:
  type: entity
  table: addresses
  id:
    id:
      type: integer
etc
You can do XML too

PHP "use" all necessary namespaces and classes

Recommended very long use statement, but it's in the bootstrap function Then require the Doctrine ClassLoader Core classes, entity classes, proxy classes Configure your Doctrine implementation by talking to the ORM configuration object

(A class called Configuration? Won't this tend to clash with other stuff, like Symfony 2? I know about namespaces but Configuration means we'll have to deambiguate every time)

Entity manager...
$connectionOptions = array('driver' => ... )
$em = EntityManger::create($options, $config);
Use a closure to lazily load the EntityManager...?
$user = new User;
$user->setName('Jon Wage');
$em->persist($user);
$em->flush();  createQuery('select u from MyProject\Model\User u');
$users = $q->execute();
Cache Drivers

fetch, contains, save, delete methods similar to sfCache classes

Wrap existing Symfony, ZF, etc cache driver instances with the Doctrine interface

Slick ways to clear part of the cache:
deleteByRegex($regex)
delteByPrefix($prefix)
deleteBySuffix($suffix)
These are used with the result caching feature. (Which we should probably leverage in Apostrophe, and use the above delete methods to avoid inconsistency. This stuff has been backported to 1.2)
$query->useResultCache(true, 3600, 'my_query_name'); 
      
Check out another article
February 11th, 2010
Apostrophe 1.0!
February 10th, 2010
Oh So Close!