"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.
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.
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
fetchAll... fetchBoth($sql, $params) - both assoc and flat...
$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 */
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.
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)
$connectionOptions = array('driver' => ... )
$em = EntityManger::create($options, $config);
Use a closure to lazily load the EntityManager...?
$user = new User;
$em->flush(); createQuery('select u from MyProject\Model\User u');
$users = $q->execute();
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:
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');