Zend Framework: models auto-loading
Zend Framework (ZF) is the MVC framework. Of course you can use its components in a non-MVC way (and I actually do so in my WP UMapper plugin), but in that case, I suppose, you do not have to worry about models auto-loading.
Frameworks make our coding life easier, and ZF is not an exception – you need very little code to get your application skeleton working. And when it comes to MVC, ZF handles almost everything – your action controllers are triggered, your views are loaded, w/o you having to worry about them. Not the same with models. If you comply to directory layout advised by ZF, you have «models» folder, but framework doesn’t interact with it in any way.
Models are simply classes, containing application logic, and to initialize one of them you have to make sure that:
a) containing directory is withing include_path
b) you require_once the class file before using it
I wanted my models to be available without any additional hassle, so I decided to find a way to auto-load models from within my controller actions.
What I did is the following:
1. Extend Zend_Controller_Action
I extended abstract controller action class, and let it remain abstract:
abstract class Afc_Controller_Action extends Zend_Controller_Action {}
Every controller action now should be a subclass of Afc_Controller_Action, instead of Zend_Controller_Action. That way, if I need to put some method common for all my controllers, I could put it into Afc_Controller_Action, and all of them would inherit the functionality. One good example of such functionality might be auto-loading the model ![]()
2. Add loadModel() method into abstract controller action
Here is the full code of my base controller action (there are quite a few other methods, but they are not necessary for this discussion):
abstract class Afc_Controller_Action extends Zend_Controller_Action { /** * Loads specified model from module’s “models” directory * * @param string $class Model classname * @param string $module Module name. By default, current module is assumed * @throws Zend_Exception In case model class not found exception would be thrown * @return string Class name loaded */ public function loadModel($class, $module = null) { $modelDir = $this->getFrontController()->getModuleDirectory($module) . DIRECTORY_SEPARATOR . ‘models’; Zend_Loader::loadClass($class, $modelDir); // if we got here - then file is included return $class; } }
I doubt it could be easier! getModuleDirectory() returns me the full directory path to my modules folder, within which (according to ZF directory recommendations) I have my models:
application
modules
default <---- module name
controller
models <----- models directory
Account <----- I separate models by controllers
Profile.php <----- model
views
Please note that I set modules directory in my bootstrap with:
$frontController = Zend_Controller_Front::getInstance(); $frontController->addModuleDirectory(FULL_PATH_TO_MODULES);
Here is sample action which uses model auto-loading:
class AccountController extends Afc_Controller_Action { /** * /default/account/profile/ * * @return void */ public function profileAction() { try { // application/modules/default/models/Account/Profile.php is loaded $model = $this->loadModel(‘Account_Profile’); } catch (Zend_Exception $e) { // module is not found } } }
That’s it, we auto-loading our models w/o any additional efforts to locate the models directory.
3. (Optional) Enable class auto-loading
This one is not strictly necessary, it just goes in line with model auto-loading, as it allows to auto-load any other classes within your include_path. At the beginning of bootstrap process:
require_once ‘Zend/Loader.php’; Zend_Loader::registerAutoload();
Now if class is not found, Zend_Loader would try to locate it. According to ZF naming convention underscores would be considered directory separators. So, if I try to initialize My_Cool_Class() Zend_Loader would look for My/Cool/Class.php within include_path directories (well it would actually include that file, and if it is within include_path, then, obviously, it would be included).
On UMapper, I added our library path into include_path within bootstrap file, so I have no require_once() left in my code, and classes are loaded when they are necessary, not when the file with tons of require_once() is loaded.
Alternatives:
Are there any alternative approaches? Plenty! Here what I have considered (and ruled out due to various reasons) when looking for a solution:
a) Zend_Load_PluginLoader - plugin loader is used extensively in Zend_Form_* as it helps loading filters, validators, and decorators. I considered the idea to just register prefix path for all my model folders, but plugin loader is not a good option performance-wise. If you looking for such a solution reading respective section of the original documentation should be enough to get you going.
b) Zend_Load subclassing - based on class name analysis (say MyModelNameModel) decide that what needs to be loaded is the model, and look within “models” folder. The detailed solution could be found here. I did like the idea, but solution seemed to be patchy to me. Granted, Zend_Loader is the way to load classes adhering to certain convention, but if “underscores are directory separators” assumption is logical enough “all models should have keyword Model in class name” is not that intuitive. What is worse - it is not a universal convention.
c) Use controller plugin. Just extend Zend_Controller_Plugin_Abstract and put include_path changing logic into preDispatch(). Not a bad solution, but I didn’t want to update include_path with “models” folders from my modules. Ideally, my include_path should contain reference to ZF, my own library, and current folder! And right now it is exactly like this.
Address: http://www.phpmag.ru/2009/03/23/zend-framework-models-auto-loading/
You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.



Leave a Reply