Lazy class loading with Zend_Loader_PluginLoader
In applications built on top of a library of reusable code, you may want to load your custom class components before those of the library. For example, your library has a user model of Lib_Model_User. In general, this class is enough for your needs, but for a particular case, you’d like to use a custom implementation specific to your application called Custom_Model_User. Furthermore, say you have a controller that needs the user model to perform some action, say Lib_Controller_User. Typically, your code for that controller might look something like this:
1 2 3 4 5 6 7 8 9 10 | class Lib_Controller_User extends Lib_Controller { public function registerAction() { if (!empty($_POST)) { $model = new Lib_Model_User; $model->save($_POST); } } } |
For your particular app, you want to use Custom_Model_User instead of Lib_Model_User… in order to do this, you’d probably subclass Lib_Controller_User and override the registerAction method. This definitely works, but there are a few better ways.
You could inject the model at some point before registerAction gets run. This might look something like:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | class Lib_Controller_User extends Lib_Controller { protected $_model = null; public function __construct($model = null) { if ($model === null) { $model = new Lib_Model_User; } $this->setModel($model); } public function setModel($model) { $this->_model = $model; } public function registerAction() { if (!empty($_POST)) { $this->_model->save($_POST); } } } // In your bootstrap process $user_controller->setModel(new Custom_User_Model); |
This works fine, but I think that Zend_Plugin_Loader gives you a much nicer way of achieving this. Here is the same code refactored to use the plugin loader instead:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | class Lib_Controller_User extends Lib_Controller { protected $_modelLoader = null; public function registerAction() { if (!empty($_POST)) { $this->_getModel('user')->save($_POST); } } protected function _getModel($name) { if ($this->_modelLoader === null) { $pl = new Zend_Loader_PluginLoader; $pl->addPrefixPath('Lib_Model', 'Lib/Model/'); $pl->addPrefixPath('Custom_Model', 'Custom/Model/'); $this->_modelLoader = $pl; } $model = $this->_modelLoader->load(ucfirst($name)); return new $model; } } |
This will first look to see if Custom_Model_User exists, if it’s not found, it will load Lib_Model_User instead. It lazy loads your models using the order you specify. This makes your controller a lot more reusable and cuts down on the amount of bootstrap code you need. Note, if you wanted to use this in a real world scenario, you’d probably want to specify your custom loader paths through configuration instead.
Zend Framework makes use of plugin loaders quite a bit. Zend_Form is a great example because it allows you to specify where to load elements when using the available factory methods like Zend_Form::createElement and Zend_Form::addElement. Doing this makes code much more flexible, I like the approach.