Quote from: rthrash at Feb 12, 2006, 04:52 PM
Jason is doing a lot of work with ORM patterns in his Tattoo demo that should start surfacing sometime soon (this month for sure).
Correct; I just finished my second major commit on the Tattoo line of development (if you have SVN access, you can check it out in my branch), and should have some significant announcements either this week or next, once I get some basic manager pages worked out, and get a few snippets converted.
A little history; after reviewing POG and a host of other ORM tools/frameworks for PHP, I chose something called Propel (
http://propel.phpdb.org/). This selection was based on Propel’s extensive feature list and a 2-week proof of concept project I completed around Christmas time (in which I implemented the current MODx data layer and code using Propel). Though some of the other frameworks provided similar functionality to Propel, the overall maturity of the Propel architecture drew me in. It’s based on a similar open-source project from the Apache project, called Torque (
http://db.apache.org/torque), implemented in Java, and is supplemented by several other very useful projects, which I am also making extensive use of in developing Tattoo.
These supporting projects include an impressive build-framework called Phing, based on Apache Ant, a very popular XML-based build tool for Java (both Ant and Phing build files integrate deeply into Eclipse). Propel uses Phing to organize and control the class generation facilities, and will allow us to easily build automated nightly builds and accomplish other complex development automation with very little effort. For instance, I can describe a new table column we might want to add to one or more tables/objects in our data model using Propel’s XML schema, open the project’s /_model/build.xml file in Eclipse, right click on the Propel Build task and select Run as Ant Task; no more than five seconds later, all of the base classes for the object model are regenerated with the new getters/setters for the new attribute/column, and are ready to use in the Tattoo API. Very slick stuff.
The DB access layer in Propel is based on yet another open source project, called Creole. Creole offers support for 5 major RDBMS currently (including PostgreSQL, Oracle and MS SQL Server), and 2 different data access models: the core model based on Java’s JDBC API and another called Jargon, based on the traditional ASP-like RecordSet API. In my current code, this gives you three different ways to interact with the data, as I reimplemented the DBAPI to utilize Creole conventions with little or no change to the public API methods. So take your pick on directly accessing the data layer, if you must. ;-)
Quote from: sottwell at Feb 12, 2006, 05:42 PM
It does look like for really good ORM stuff you need PHP 5. Digging into this, I see it won’t save me from having to work out my own joins for complex queries. <sigh> But it’s still a pretty nice tool for more basic queries. I see where the basis for the MODx DBAPI came from.
Agreed; I originally was going to produce Tattoo with a PHP5 and PHP4 run-time, but the advanced OO features of PHP5, especially Reflection and Exception handling, are IMO essential to producing a truly useful OO version of MODx. So I do think it’s best to leave PHP4 to the still evolving MODx codebase while we explore the potential of PHP5 with Tattoo.
And speaking of complex joins, one of the best features of Propel is the auto-generation of joins to tables related by foreign-key constraints in the Propel schema. For instance, consider this simple way of retrieving all the Elements related to the current Element by a column called `parent_guid`:
$crit= new Criteria();
$crit->add(ElementPeer :: getColumnName(ElementPeer :: PARENT_GUID), null, Criteria :: ISNOTNULL);
if ($this->getElementsRelatedByParentGuid($crit)) {
...
NOTE: Elements will replace all current MODx "resource" components, from Templates to Chunks to Snippets to Plugins, plus a whole lot more, as you will see in the upcoming days
Or consider retrieving every Resource object in the database, and preloading all related objects by foreign keys, using a single query (which would be full of LEFT JOIN statements, like your original example Susan):
$criteria= new Criteria();
$critContextKey= $criteria->getNewCriterion(ResourcePeer :: getColumnName(ResourcePeer :: CONTEXT_KEY), $obj->getKey());
$critContextKey->addOr($criteria->getNewCriterion(ResourcePeer :: getColumnName(ResourcePeer :: CONTEXT_KEY), NULL, Criteria :: ISNULL));
$criteria->add($critContextKey);
$collResources= $cms->getCollection('tattoo.Resource', $criteria, 'doSelectJoinAll');
NOTE: Resource in Tattoo is the name for any web resource that can be accessed via URL (replaces site_content or documents and weblinks from MODx)
Now, a couple of things in this example may seem like they are a little-bit on the side of overkill, such as the getColumnName method required to get the actual physical table column name, but this was necessary to ensure that the table name with the appropriate table_prefix is added to the column name when working with complex joins. Overall however, with a little OO experience, this approach is the only way to make your objects and code truly portable across disparate DB platforms, and IMO, is much simpler than trying to figure out which table names and columns to include in a string of SQL code.
I welcome any comments or feedback on ORM in general, or the Tattoo/Propel work in progress. More soon...