Zend Framework has resource autoloading, so I keep the post for historical reasons only.
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.




[...] Yet another way would be to extend your action controller, so that it has method for locating and loading the module (as generally models are needed within actions). Here is small article on how to do this: ZF: models auto-loading. [...]
A couple notes.
First, it’s generally better _not_ to create base classes extending Zend_Controller_Action; this is what action helpers were designed for, and they provide a much more re-usable solution.
Second, you may want to check out Zend_Loader_Autoloader_Resource (http://framework.zend.com/wiki/display/ZFPROP/Zend_Loader_Autoloader_Resource+-+Matthew+Weier+O'Phinney) which will be released with ZF 1.8.0. This solution provides autoloading for the various resources in your application tree, and greatly simplifies the entire process. You can grab it currently from ZF’s incubator in subversion.
[...] PHP Magazine » Zend Framework: models auto-loading [...]
Matthew,
Thanks for your response, indeed action helpers sould solve the issue more elegantly – it seems I overlooked them, would review the manual and try to come up with a better solution.
I actually, reviewed your proposal when looked for solution, but didn’t know that it would be available as soon as ZF-1.8. Nice work with autoloader.
Hum, simple solution, but for now I go for the straightforward solution of loading models using php autoload/Zend_Loader.
On app startup, run through all your available modules and do following:
$modPath = $modDir . “controllers”;
$modelPath = $modDir . “models”; $front->addControllerDirectory($modPath,$modName);
ini_set(‘include_path’,ini_get(‘include_path’)
. PATH_SEPARATOR . $modelPath);
This allows usage of models from any module by any module. You can then use the Zend Autoloader to grab anything sitting in subdirectories of models, e.g.
Form_MyForm => models/Form/MyForm.php
Install_MyModInstall =>models/Install/MyModInstall.php
Service_MyBusinessService =>models/Service/MyBusinessService.php
I’ll check out Zend_Loader_Autoloader_Resource when it arrives, but above has been working flawlessly for over a year now
[...] the PHPMag.ru site comes a recent post looking at a technique for creating auto-loading models in your Zend Framework application. When [...]
Zend_Loader::loadClass does change your include_path with $dirs provided, so updating include_path with alternative solution c) is the same thing
[...] the PHPMag.ru site comes a recent post looking at a technique for creating auto-loading models in your Zend Framework application. When [...]
Agreed use a Zend Controller Action Helper Plugin instead of given solution (http://www.spotsec.com/blogs/archive/zend-model-loading-modelloader.html).
I’d like to add that just using PEAR naming convention might actually be the easiest solution.
$users = My_Model_Users()
Is elegant and means you can easily get to the model from view helpers and controllers, and also from other modules.
Of course you could use a more elaborate loader, but using the PEAR naming convention is simple.
You could also just add a models folder to your include path and use:
$story = new News_Story()
For a News Module.
How do i dynamically load the models? I have the following structure:
–application
—-modules
——front
——–models
——–controllers
What do i have to write to load a Category.php from front/models in the front/controllers/IndexController.php
[...] Address: http://www.phpmag.ru/2009/03/23/zend-framework-models-auto-loading/ [...]
[...] schreibt ?ber das automatische Laden von Models in einer Zend Framework [...]
[...] http://www.phpmag.ru/2009/03/23/zend-framework-models-auto-loading/ 唱首情歌给丹听 [...]
@grabur: I agree with you on simply using Pear naming convention.
I’ve used Zend_Loader_Autoloader_Resource and at the end of the day – I simply don’t need it. There is no need to reinvent the PATH and __autoloader.
Thanks.
Thumbs Up ! this solved my problem. I am new to zend and learning it. Was wondering, how to load a class.