I’m quite a fan of Propel, having used it with Symfony since the 1.2.x days. That version was relatively slow, as it used its own data access layer; it wasn’t until 1.3 that it switched to the much faster PDO approach. Nevertheless, the ability to create objects and persist them, to look up items without (much) reference to SQL, and to have all of this integrate with Netbeans’ code autocompleter made coding the database layer of an application much less of a fiddle than it used to be.
That said, switching to an ORM in complex applications does not usually come without a pain overhead. It was on the Symfony forum that someone offered the following (paraphrased) sage advice: “You start off liking Propel, as it makes db access easy. You then run into problems as you can’t do something in Propel – such as certain kinds of join – and you start to hate it for its limitations. You then work out how to overcome its limitations whilst still reaping its benefits, and you end up liking it again”.
Since I’ve been there, done that, and got the T-shirt, I thought the pain barrier for the current version, 1.5.x, would be much reduced. Though I think Symfony is great, for my current project I want to take a sparse approach: just Propel/PHP to start with, then maybe some Zend modules added later. This means that I won’t get the hand-holding that Symfony does, such as auto-loading all the necessary library files! So, the following are some thoughts on my experience. Hopefully too this’ll add to the sparse Google results for the latest version – searches are awash primarily with questions about much older versions. Has everyone jumped ship and moved to Doctrine instead? 😉
Installation
OK, here we go. Propel has – at least in the past – tended to rely too much on PEAR. I’m not a fan of this method, as it makes installation of software on servers much harder than it needs to be, and much less self-contained. I’ve fought long and arduous battles with PEAR trying to get it to play nice with proxies and content filters, just so it can download the updates it needs. With that in mind, I started off manually downloading Propel plus its dependencies (Phing and PEAR::Log), and arranging them in a lib/ folder in my project. Unfortunately there is not much documentation on this approach, though it is mentioned; I really wanted some solid instructions on how to link Propel to its dependencies, to no avail.
Thankfully a third-party blog came to the rescue, and suggested that despite concerns about PEAR, Phing should be installed this way; since it is only required for the build phase and not for runtime, it does not need to be contained in the final distribution. (Actually, the software project I’m working on will need to do its own building, so I’ll need to install Phing without PEAR at some point. However, the first priority was to get things working!).
At the time of writing, Phing 2.4.4 was current. Unfortunately, this breaks backwards compatibility with earlier versions, thus breaking the build phase with a stream of errors. The new user – myself included here – will be inclined to think they’ve not set a config option correctly! This post shows how to fix it – load a previous version until the bug is ironed out.
Thankfully it seems that PEAR::Log is an optional dependency, and I think for run-time only. Hence the user just trying to get things working need not worry about this to start with.
Building the model
It is useful to add the Propel command, propel-gen, to the path. This can be done thus, on a Unix-like machine (on a Mac, it’s in ~/.profile):
export PATH=~/:/opt/local/bin:/opt/local/sbin:/opt/local/lib/postgresql84/bin:~/Development/Personal/P2PT/lib/propel-1.5/generator/bin:$PATH
The instructions on building the model turn out to be quite good; the problem was that, having used an earlier version under the symfony command-line, I didn’t think I needed to read them properly. Firstly, I spent a fair bit of time calling “propel-gen <project-folder>”, which no longer works (to be fair, the Phing bug confused things here also). Once I worked out this bit, I had these working quite easily:
cd ~/Development/Personal/P2PT/database propel-gen om # build model propel-gen sql # generate sql to create tables propel-gen insert-sql # insert sql into database
The SQL would not run inside phpPgAdmin, since it contains DROP TABLE statements for tables that do not exist. Running the insert-sql task fixes this, since that does not stop on failing statements. It seems that introducing this sensible feature for PostgreSQL has run into technical issues, but then it’s not a big problem.
Run-time test
Critically, I added runtime-conf.xml but missed the instruction to generate the run-time version of it. This is simply thus:
propel-gen convert-conf
This file should be included at runtime. After a bit of playing about, I created a test script which seems mostly to work:
<?php // Set up some paths & schema info $projectPath = realpath( dirname( __FILE__ ) . DIRECTORY_SEPARATOR . '..' ); $schemaName = 'database'; $modelPath = $projectPath . "/database/build/classes"; // Init propel require_once $projectPath . '/lib/propel-1.5/runtime/lib/Propel.php'; Propel::init($projectPath . "/database/build/conf/${schemaName}-conf.php"); // Add the generated 'classes' directory to the include path set_include_path($modelPath . PATH_SEPARATOR . get_include_path()); // Autoloading seems to be broken on static methods and constants - so we need // to include the following item manually: require_once $modelPath . "/${schemaName}/NodePeer.php"; $node = new Node(); $node->setName('My Node'); $node->setHash(sha1($node->getName())); $node->save(); $nodes = NodePeer::doSelect(new Criteria()); echo 'Node count: ' . count($nodes) . "\n";
So, onto working out how to use the new Query feature! Obviously if you use this code, you’ll need to swap out a pathname or two, and of course change the model references to your own.
As per the code comments though, I expected model classes not to require explicit loading. For some reason, the autoloader seems to be choking on static references (raised here). I’m not sure if there’s something about my PHP config, or whether there’s a bug in PHP – will update if I find a fix.
The only other item to fix is to see if all the above can be made to work without PEAR. Will blog this for now, and again will update if I find a way!
Update, 23rd Feb
Both pending issues are resolved. Model class auto-loading works fine, except for classes that exist inside Propel already – my choice of table name, “node”, was unfortunate, since this generated a peer class called NodePeer, which exists as an interface in the Propel core. Switching on the namespace feature (with PHP 5.3+, obviously) would fix that – as would choosing a non-clashing name!
And yes, everything can be made to work without PEAR. Just ensure that “(phing-root)/bin” is in the system path, which will make the phing command accessible to Propel.
Hi Jon
Could you have a look at my situation with Propel please? I’m hoping your experience with it can help me with the following:
http://stackoverflow.com/questions/19246852/propel-how-to-generate-uppercase-columns
Thank you