Propel 1.3 has served me very well over the past few years and I’m absolutely confident that recent projects would not have been delivered so quickly without it. However, when stuff gets a little crazy, you need to hydrate your objects using hand-crafted SQL. Building up a SQL in a variable and passing it between functions can be a nightmare to get right. Since I was already using Zend Framework, I decided to try and get Propel objects back from a Zend_Db_Select generated query. The Relational API in the upcoming 1.5 release of Propel would make this possible without the need for hacks and workarounds…
The quick fix
The quickest way to combine the two is to use something like:
BookPeer::populateObjects($select->from('Books') ->where('author = ?', 'foo') ->order('title ASC') ->query() ->getDriverStatement());
The magic part is the call to “getDriverStatement” which returns the PDOStatement composed by Zend_Db_Statement_Pdo. The only downside to this is that the query will be executed by Zend_Db’s connection to the database, rather than Propel’s. If you can live with that then you may stop reading. If, like me, this sort of negligent waste of resources keeps you awake at night, then you need a rather unsightly hack.
Sleepless nights
Zend_Db doesn’t support any kind of “setConnection” method, where you pass in a PDO object. While Propel does support this, it only accepts an instance of PropelPDO. Rather than extending Propel, I thought the easiest way around this would be to create a Zend_Db adapter which, upon calling getConnection(), simply delegates back to Propel::getConnection().
The new adapter required a little hacking to get it right. There are a few methods to castrate because, understandably, the abstract adapter does a lot of checking to make sure it’s configured correctly. Instead, we’re using a PDO connection, configured elsewhere so any configuration parameters would simply get in the way.
class App_Db_Adapter_Propel_Mysql extends Zend_Db_Adapter_Pdo_Mysql { // configuration-less construction public function __construct() { parent::__construct(array()); } public function getConnection() { $this->_connect(); return $this->_connection; } // always use Propel's connection protected function _connect() { if (!$this->_connection) { $this->_connection = Propel::getConnection(); } } // allow for no parameters to the constructor protected function __checkRequiredOptions(array $config) {} }
Now we can use the Zend_Db factory (or, because the new adapter does not require any configuration, you could technically just instantiate it directly) to get our new adapter. The select object will use that adapter and we’ve avoided duplicate db connection attempts.
The finished article
$config = array('adapterNamespace'=>'App_Db_Adapter'); $select = Zend_Db::factory('Propel_Mysql', $config)->select(); BookPeer::populateObjects($select->from('Books') ->where('author = ?', 'foo') ->order('title ASC') ->query() ->getDriverStatement());
Tags: mysql, php, propel, zend-db, zend-framework
[...] http://dev.fuzzee.co.uk/2010/04/combining-zend_db_select-and-propel-1-3/ [...]
[...] This post was mentioned on Twitter by Adrian Hardy. Adrian Hardy said: Gasp! my first technical blog post in over 6 months. Mixing Zend_Db_Select with Propel 1.3 : http://bit.ly/asiJ5F [...]