Loading models within modules in the Zend Framework

Modularity is an important design principle, one of its goal is to design systems that are easier to extend, understand and manage. In 2006, Zend Framework introduced a “conventional modular directory structure” that allows users to group applications into self-contained units. The problem with this is that the framework doesn’t offer any method for loading models within a module, reason why I came up with this solution.

Let’s go back in time a bit…

19 June 2007

Matthew Weier O’Phinney wrote:

We haven’t spent much time on a generic model interface. We suggest placing module specific models in a models subdirectory of your module simply for grouping purposes. Consider, for instance, if you decided to distribute a module, or wanted to restrict access in your repository for certain developers to work only on certain modules, all the code is then in one place. We don’t have any methods yet for automatically including models within a module. It’s fairly easy to do it manually:

require_once dirname(__FILE__) . '/models/MyModel.php';

03 Dec 2007

Ralph Schindler wrote:

The future of ModelLoader will be revisited once PluginLoader is fully documented.

16 Apr 2008

Bradley Holt wrote:

Zend Framework provides two primary ways in which it can be extended: either through components or modules. Components are library items either in your own namespace or in the Zend namespace. Since components are library items, there's a fair bit of "glue" needed to get them working. Modules are ready-to-run code that can be dropped into your application that you can have up-and-running with minimal setup. If you find yourself introducing a lot of cross-module dependencies then perhaps you would be better of creating components instead and then using these components from within your modules.

I would like to have some more guidance from Zend here on best practices for writing MVC modules. If lots of people started following the same practices, we could end up with a situation where there a lot of well-written, modular applications out there that can be wired up to quickly build your application. For example, grab a wiki module and a blog module and throw them into a custom Zend Framework application and quickly have a website with all of these features integrated.

19 Jul 2008

Neil Garb wrote:

If you're like me, you like to use different classes to represent the same model in different modules. The rationale is quite simple: by using different classes you're encapsulating module-specific business logic within that module. For instance, a user model in an admin module may require functions related to access control, whereas the model's counterpart in a website module might need functionality to send messages to other users.

But if you're like me you've also encountered the difficulty of using classes with the same name with Zend_Loader's registerAutoload(). The problem is that registerAutoload() uses PHP's include path, in which path ordering is significant. This means that you can't, in your bootstrap, just set the include path to include the model directories for each module.

Present Day

I think Bradley hit the nail on the head when he said:

If lots of people started following the same practices, we could end up with a situation where there a lot of well-written, modular applications out there that can be wired up to quickly build your application.

Exactly. I really think it's time to create a Zend_Controller_Directory class and move all the module-specific functionality away from Zend_Controller_Front.

For example:

Zend_Controller_Directory

public function addModuleDirectory() {}
public function setDefaultModule() {}
public function getDefaultModule() {}
public function setControllersDirectoryName() {}
public function setModelsDirectoryName() {}
public function setRoutesDirectoryName() {}
...

Zend_Controller_Action

public function getModel() {}
public function getModule() {}
public function getContainer() {}
public function addDependency() {}
...

Bootstrapper

require_once 'Zend/Controller/Directory/Standard.php';
$directory = new Zend_Controller_Directory_Standard();
$directory->setControllersDirectoryName('controllers');
$directory->setModelsDirectoryName('models');
$directory->setRoutesDirectoryName('routes');
$directory->setDefaultModule('default');
$directory->addModuleDirectory('../application/modules');

require_once 'Zend/Controller/Front.php';
$frontController = Zend_Controller_Front::getInstance();
$frontController->throwExceptions(true);
$frontController->setDirectory($directory);

Invoking models

class Blog_IndexController extends Zend_Controller_Action
{
  public function indexAction()
  {
    $model = $this->getModel('PostModel');
    $userModel = $this->getModel('UserModel', 'user');
  }
}

However, if you are only interested in loading models within modules, here's an example of how I'm doing it.

About these ads

14 thoughts on “Loading models within modules in the Zend Framework

  1. Interesting ideas! I’m not sure if it’s necessary to have a Zend_Module_Front and a Zend_Module_Action. I’m not sure from what you’ve written here what value these add beyond just using Zend_Controller_Front and Zend_Controller_Action. Every Zend Framework MVC application uses the module feature of Zend Framework – it’s just that some only use the default module. I’m not sure if I’d want to implement my other modules in a very different way than how I implement my default module. But, I’d like to hear more about this idea!

  2. In my opinion, the front controller of the framework needs to be refactored. It’s doing too much. The non-grouped controllers features overlaps with the modules functionality. If you think about it, Zend_Controller is breaking a basic design principle: separation of concerns. Some people want tomatoes, others potatoes :)

    Modularity and user generated content is the way forward I think. Symfony’s architecture is a good example of this. They have 230 modules and 120 contributors.

    We have enough libraries, now we need people contributing modules. So I’m suggesting moving to phase 2.

  3. OK, interesting. So the default module would use the new Zend_Controller_Front and Zend_Controller_Action just like any other module would? And, Zend_Controller_Front and Zend_Controller_Action would be refactored to do less than what they’re currently doing?

    I think it would be great to have lots of Zend Framework modules built by the community. However, I don’t think it should be encouraged until some clear best practices have emerged that will allow them to be loosely coupled. Most PHP applications today aren’t designed to be integrated with one another. Most are monolithic applications that work great on their own, but good luck trying to integrate them in a sane way with other applications. I think Zend Framework modules have a _huge_ potential to solve this problem, but it’s important it’s done right so we don’t just end up back in the same spot we’re in now. This is a pretty big topic :-)

  4. Oops, I meant “the default module would use the new *Zend_Module_Front* and *Zend_Module_Action* just like any other module would? And, Zend_Controller_Front and Zend_Controller_Action would be refactored to do less than what they’re currently doing?”

  5. I think you’re definitely on the right track here. I think this would simplify making truly encapsulated modules AS WELL AS making the Front Controller simpler to explain and work with to those unfamiliar with it.

  6. Right now I ‘m using something like this to load my module models:

    $model = Mmx_Loader::loadModel(‘Modulename_Modelname’);

    It will be great to see some clear best practices on to setup Zend Framework modules. But I think it will need a hire level framework (Zend Application Framework foe ex.) because all those modules will have to share some component: same bootstrap, db, auth, acl, log, error controller …

  7. @Bradley: I agree with what you said, modules have a huge potential. I think the problem is that, at the moment, it’s difficult to extend the functionality of the front controller. For example, the addModuleDirectory() method doesn’t store the value of the path to the modules. So if you want to get the path to the modules, you can’t, there’s no getModuleDirectory() method.

    Update: I’ve created a Zend_Controller_Directory proposal that addresses this issue.

  8. I use a controller plugin to manipulate the include path in order to load module-specific models. I’ve just stuck up some example code in case anyone is interested – http://www.tfountain.co.uk/blog/2008/8/15/zend-framework-module-models/

    I like the idea of a Zend_Module_* set of components as a way to organise modules into packages that could be dropped into other apps. I think routing is the problem here though.

    One thing I like about the routing implementation in ZF is that it is flexible enough to allow routes to be defined in a number of different ways depending on coder preference and application needs. But in order for this whole thing to work, there would need to be a standard way for modules to define routes so that the application could load them.

    Your Zend_Module example class allows definition of a routes ‘directory’ – so would this contain a routes.ini file containing the module’s routes? Or did you have something else in mind?

  9. the way i have been working is, after creating a front controller helper and extending the dispatcher, my modules follow the pear convention using a vendor and a module.
    for example:
    Vendor_Module
    Vendor_Module_Controller
    etc…
    i already have
    Devel_Blog:
    Devel_Blog_Article
    Devel_Blog_Article_Comment
    Devel_Blog_Controller_Article
    Devel_Blog_Controller_Index
    Devel_Blog_Admin_Controller_Index

    Devel_Cms:
    Devel_Cms_Page
    Devel_Cms_Controller_Index
    etc….

    what i mean is that I add the vendor and module namespace (hoping i can port this to php 5.3). even my db table names use the vendor_module convention, also my Zend_Session_Namespace follows the Vendor_Module convention…

    I tried to add you to my gtalk contacts, so we can discuss this further

  10. Pingback: pejotr.log

  11. I’m not sure I’m right but loading model with:
    $sharedModel = $this->getModel(‘Blog_PostsModel’, ‘blog’);
    will load /appilication/blog/models/Blog/PostsModel.php, is that what you wanted to achieve ?

  12. You are right, thanks pejotr. I was using Zend_Loader::loadClass() instead of Zend_Loader::loadFile(), and the first one replaces the underscores with a directory separator. There’s no need to pass the name of the class now, just the filename.

    Example:

    $model = $this->getModel(‘IndexModel’);
    $sharedModel = $this->getModel(‘PostsModel’, ‘blog’);

  13. Interesting!

    I was just looking around to see if I could find any modules to use with my Zend app… :-)

    I have a app.ini similar to:

    [bind]
    acl.class = “LdapAcl”
    acl.rdn = “dc=example, dc=com”

    blog.class = “BlogModel”
    blog.config = “blog.ini”

    … and a controller plugin that (lazy-)populates a globally accessible model (Zend_Registry). Helpers in
    both Controller and View provide access to the same
    model so…

    $this->getModel(‘blog’)->get($this->getContext(‘blog.cur’)
    … will always give the same object.

    Extending this with modules could give:

    $this->getModel(‘blog:entry’)->get($this->getContext(‘blog:entry.cur’)

    … providing seamless access between modules.

    Erh… remembering some old python stuff i just miss the compact…

  14. Pingback: Top Posts 2008

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s