Skip to end of metadata
Go to start of metadata

<ac:macro ac:name="unmigrated-inline-wiki-markup"><ac:plain-text-body><![CDATA[

Proposal Superceded
This proposal has been superceded by the Zend_Loader_Autoloader_Resource proposal.

<ac:macro ac:name="unmigrated-inline-wiki-markup"><ac:plain-text-body><![CDATA[

Zend Framework: Zend_Controller_Action_Helper_ResourceLoader Component Proposal

Proposed Component Name Zend_Controller_Action_Helper_ResourceLoader
Developer Notes http://framework.zend.com/wiki/display/ZFDEV/Zend_Controller_Action_Helper_ResourceLoader
Proposers Graham Anderson
Zend Liaison Matthew Weier O'Phinney
Revision 1.2 - 9th Sept 2008: Initial Draft. (wiki revision: 13)

Table of Contents

1. Overview

The goal of this helper class is provide a standard way to load and share resource classes between Zend Framework modules where the application developer wishes to store such classes inside the module directory. This will help application developers create re-usable modules containing discreet functionality which in turn will benefit the Zend Framework community through greater code sharing.

Currently to share model and form classes among ZF MVC modules, a developer must typically use one of two strategies.

1. Develop his/her own loader class, perhaps extending or using Zend_Loader and using path spec functionality or maybe using the PluginLoader, or do some other thing like manipulate include paths via a dispatch loop plugin.

2. Store models, forms and other classes outside of a module directory, this allows quite easy sharing of resource type classes between modules, but increases the complexity of using re-using modules.

While these strategies work they are many and varied and there is no current official helper that performs loading functionality.

This class will provide a ZF standard way to load models, forms and other resource classes that are stored inside the module directory.

2. References

3. Component Requirements, Constraints, and Acceptance Criteria

  • This component will extend Zend_Controller_Action_Helper_Abstract
  • This component will use Zend_Loader_PluginLoader / Zend_Loader_PluginLoader_Interface
  • This component will load model, form and other class files and definitions into the current php execution cycle
  • This component will provide a mechanism for custom resource types and related plugin loaders.
  • This component will return class names of loaded resource classes
  • This component will not instantiate or return objects
Useful Information
To fully satisfy some of the requirements from comments, it may be necessary to implement the plugin loader interface and consume a loader class with functionality more suited to the scope of this proposal.

4. Dependencies on Other Framework Components

  • Zend_Loader_PluginLoader / Zend_Loader_PluginLoader_Interface

5. Theory of Operation

Consider an MVC application with the following 'users' module. Instead of storing the models and forms in a more general location e.g. application/models, application/forms or My/Forms/MyForm.php, storing the resource classes under the module reduces complexity for re-using and sharing modules.

It should be noted that this directory structure need not be used but is provided for the sake of example, though it is intended for a default module directory to be used for model and form resources.

application/
    modules/
        default
        users/
            controllers
            forms/
                LoginForm.php
                AccountDetailsForm.php
                RegisterForm.php
            models/
                UserModel.php
                GroupModel.php
            db/
                UserTable.php
                UserTableRow.php
            views

Resource class naming

Example class naming using resources from the above example. Classes are named to provide information as to which module and resource type they belong to e.g. Modulename_Resourcetype_* this also reflects the function of the resource class and the general location within the module.

This allows us to use data containing details of known application modules and this helpers internal list of known resource types to assemble prefixes for each plugin loader.

Now our loader helper class can use prefix paths and the Zend_Loader_PluginLoader class to load these resource classes.

6. Milestones / Tasks

  • Working prototype [DONE] http://andtech.googlecode.com/svn/trunk/
  • Draught initial proposal [DONE]
  • Collect initial feedback and refine proposal/prototype [DONE]
  • Submit proposal for review [DONE]
  • Pending review outcome - inclusion to laboratory/incubator
  • Docs & unit tests

7. Class Index

  • Zend_Controller_Action_Helper_ResourceLoader

8. Use Cases

UC-01

See theory of operation, further use cases to follow

9. Class Skeletons

Zend_Controller_Action_Helper_ResourceLoader

]]></ac:plain-text-body></ac:macro>

]]></ac:plain-text-body></ac:macro>

Labels:
None
Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.
  1. Aug 26, 2008

    <p>I very much like the idea of thie proposal. I do <em>not</em> like the naming conventions you suggest. I'd prefer something along the line of:</p>
    <ul>
    <li>Users_Form_Login</li>
    <li>Users_Table_User</li>
    <li>Users_Model_User</li>
    <li>Users_Model_Group</li>
    </ul>

    <p>This better reflects both the directory hierarchy and the taxonomy of the classes.</p>

    1. Aug 27, 2008

      <p>That sounds sensible but just to clarify, do you mean for a class to be loaded in this fashion:</p>

      <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
      $modelClass = $this->_helper->resourceLoader('Model_User');
      $modelClass = $this->_helper->resourceLoader()->loadModel('Model_User');

      ]]></ac:plain-text-body></ac:macro>

      <p>or in this way:</p>

      <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
      $modelClass = $this->_helper->resourceLoader('User'); // default is to try and load a model
      $formClass = $this->_helper->resourceLoader('Login', 'Form');
      $formClass = $this->_helper->resourceLoader()->loadForm('Login');
      ]]></ac:plain-text-body></ac:macro>

      <p>Using $dirs = Zend_Controller_Front::getInstance()->getControllerDirectory(); provides initially a rather handy associative list of module names and paths, so it's simple to use either "Users_" or "Users_Model_" as the prefix for the plugin loader prefix paths stack.</p>

      1. Aug 27, 2008

        <p>I actually like both. BTW, Zend_Controller_Front has a getModuleDirectory() method now that maps modules to the base directory of the module (not the controllers directory).</p>

        <p>Part of the reason I suggest this change it possible for you to use the PluginLoader easily with the component – if the class names follow this format, you can easily specify different plugin resource types, and have it automagically do lookups. Additionally, this would allow somebody else to create a component using a PluginLoader that can find the same set of classes. Alternately, a developer could write up their own PluginLoader implmentation (we have a PluginLoader interface) and attach it to the component – giving full customization.</p>

        1. Aug 27, 2008

          fc

          <p>Matthew, do you think it's to late or unnecessary to introduce a Directory class or something else to deal with directory names and paths? I've spent some time working with legacy code and migrating applications and I think it could be very helpful. Having the getModuleDirectory() method inside the front controller doesn't help much, because although I can extend the Zend_Controller_Front class, other ZF components will still be getting an instance of the parent class: Zend_Controller_Front::getInstance(). That's one of the reasons why I suggested moving all this to a different class, that way the front controller delegates this responsibility to the Directory object and we can extend it.</p>

          1. Aug 28, 2008

            <p>Ifind your proposal interesting, but it's not something we can introduce until 2.0 at the earliest – it represents a fairly large BC break, even if it makes the component more flexible.</p>

            <p>We'll discuss more on this in the coming months.</p>

        2. Aug 27, 2008

          <p>In the first version of my prototype code for this, when I had initially kept the domain of the helper to models only, I had additional setters/getters and configuration of the PluginLoader, with a view to allowing developers to use their own plugin loader.</p>

          <p>I'd removed them as the domain of the loader helper changed from models only to models + forms + X. I wanted to keep the initial proposal as simple as possible. However I think that as long as the customization is limited to specifying additional resource types (with optional custom plugin loaders using the implementing the interface) adding this would not only be desirable but perhaps should be a <em>requirement</em> of the design/proposal.</p>

          <p>So unless there's more comments on the naming of the resource classes, I'll update the proposal later today to reflect encouraging or enforcing prefixes/class names in the form Modulename_Resourcetype_* for at least the default functionality (models & forms).</p>

          1. Aug 28, 2008

            <p>By the way, the way we accomplished multiple plugin types in Zend_Form was to have multiple plugin loaders I would suggest going this route with ResourceLoader.</p>

      2. Aug 27, 2008

        <p>Question: how does the resource loader know what module to load the resource from?</p>

        1. Aug 27, 2008

          <p>An associative array of prefixes and paths is assembled for each resource type, this will be used by a plugin loader object which assembles the class name, checks to see if the class exists already and so on and if not loads it. The prefix will contain the name of the module, the classname will also contain the name of the module as the prefix must match a substring of the resource class name. This is similar to the way in which other plugin classes are loaded elsewhere in the framework.</p>

          <p>Module names and paths to modules are derived from querying the front controller object.</p>

          <p><a class="external-link" href="http://framework.zend.com/apidoc/core/Zend_Loader/PluginLoader/Zend_Loader_PluginLoader.html">http://framework.zend.com/apidoc/core/Zend_Loader/PluginLoader/Zend_Loader_PluginLoader.html</a></p>

          <p>As stated in the proposal, the resource loader would be dependent on Zend_Loader_PluginLoader, or at very least a plugin loader implementing Zend_Loader_PluginLoader_Interface.</p>

          1. Aug 28, 2008

            fc

            <p>Graham, nice proposal.</p>

            <p>> Module names and paths to modules are derived from querying the front controller object.</p>

            <p>Is there a better way of doing this, instead of getting the value from the singleton? What do you think?</p>

            1. Aug 28, 2008

              <p>Well at the moment, as far as I'm aware, the front controller is the only place in the whole framework where a list of available modules is held, and even this is indirectly by asking for the location of the controller directories. As Matthew pointed out, he's added a getModuleDirectory() to the front controller for 1.6 but but as you probably already know this behaves slightly differently from getControllerDirectory(); when no name parameter is passed to the method, one returns an array, the other returns a string module name.</p>

              <p>Now to me it's not a bad thing as I think it's important not to have too restrictive or very well defined concepts of what a ZF MVC module should be. After all ZF is a "do as you please" not "do as I say" type of framework, and also one can argue that in ZF MVC the controller location is the only thing of importance within the scope of a module.</p>

              <p>Having said that though, there is one thing that does come to mind and this probably can be considered a use case for your <a href="http://framework.zend.com/wiki/display/ZFPROP/Zend_Controller_Directory+-+Federico+Cargnelutti" title="Zend_Controller_Directory proposal - Federico Cargnelutti">Zend_Controller_Directory</a> proposal. I had meant to comment on this but I'm somewhat trying to marshal my thoughts on it.</p>

              <p>If it's reasonable to consider that not every developer will follow the same layout structure and conventions for resource classes such as models, forms, db tables etc; and that because of this expectation we can accommodate such customization in <strong>this</strong> proposal. Then, in my opinion, it is also reasonable to consider that not every developer will follow the same layout structure for their MVC modules as a whole, and that we should try to accommodate this also if we hope to promote the re-use and sharing of ZF application code & modules.</p>

              <p>If this is the case, then abstraction of the MVC directory structure code becomes more desirable. Although this is a larger discussion for a different proposal <ac:emoticon ac:name="smile" /> So if I focus on this proposal then it's not a huge deal where the location of the modules is taken from, as this can easily be re-factored in future without breaking BC. </p>

              1. Aug 28, 2008

                <p>I agree with matthew about the naming of resources. I personally feel that the module should play an important part in the name of the resource being loaded, as well as the type of resource. So you could end up with:</p>
                <ul class="alternate">
                <li>Blog_Form_Comment</li>
                <li>Blog_Model_Post</li>
                </ul>

                <p>It should be easy from such names to get to the desired resource. I also feel that there should be a strong push towards a convention for the placement of such resources within ZF projects. Many php developers traditionally seem to love doing things their own way. Freedom is great, but conventions do make life easier in the end. That being said, of course custom loaders/inflectors should be available.</p>

              2. Aug 28, 2008

                fc

                <p>> Now to me it's not a bad thing</p>

                <p>I'm looking after the core files, and the fact that I can't extend the front controller and overwrite the getModuleDirectory() method is a bad thing. And if you move this method to the Dispatcher, then I have to create my own Dispatcher to extend it, and that's also a bad thing.</p>

                <p>> If it's reasonable to consider that not every developer will follow the same layout structure and conventions for resource classes such as models, forms, db tables etc.</p>

                <p>True, but the Directory class I'm suggesting has nothing to do with loading resources, it solves other issues. Also, keep in mind that a module not only has php classes, it can have: Images, videos, XHTML, CSS, XML, INI, PHP files (configuration, i18n, routes), etc.</p>

                <p>I like this proposal, but if you use something that I can't extend, I'm afraid I wont be able to use it.</p>

                1. Aug 28, 2008

                  <p>"...and the fact that I can't extend the front controller and overwrite the getModuleDirectory() method is a bad thing" – this is actually untrue, and I've blogged how you can do it.</p>

                  <p>__construct() and $_instance are declared protected. If you want to substitute your own implementation, you simply need to override getInstance() and $_instance, and ensure that your own class is called the first time getInstance is called.</p>

                  <p>So, to begin with, you'd have the following:</p>
                  <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
                  class My_Controller_Front extends Zend_Controller_Front
                  {
                  protected $_instance;

                  public function getInstance()
                  {
                  if (null === self::$_instance)

                  Unknown macro: { self}

                  return self::$_instance;
                  }

                  ...
                  }
                  ]]></ac:plain-text-body></ac:macro>

                  <p>Then, in your bootstrap, you simply need to ensure that your own instance is created first:</p>

                  <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
                  require_once 'Zend/Loader.php';
                  Zend_Loader::registerAutoload();

                  $front = My_Controller_Front::getInstance();
                  // ... do some stuff
                  ]]></ac:plain-text-body></ac:macro>

                  <p>Later calls to Zend_Controller_Front::getInstance() will then get the My_Controller_Front instance.</p>

                  <p>As for your assertions that "if you use something that I can't extend," let's see where the proposal goes before you rise to judgment, shall we?</p>

                  1. Aug 28, 2008

                    fc

                    <p>Matthew, that's exactly what I'm doing. I think the Zend_Controller package is excellent, and that's why I don't want to move away from it.</p>

                    <p>You are right, what I meant by "if I can't extend, I can't use" is that now that I've extended some of the Zend_Controller classes, I find myself in a position where I might need to extend any new class that gets added to the Zend_Controller. And that's the problem I'm facing, the more I move away from the Zend_Controller package, the more flexibility I need. And of course, that's something I want to avoid.</p>

                2. Aug 29, 2008

                  <blockquote>
                  <p>True, but the Directory class I'm suggesting has nothing to do with loading resources, it solves other issues. Also, keep in mind that a module not only has php classes, it can have: Images, videos, XHTML, CSS, XML, INI, PHP files (configuration, i18n, routes), etc.</p></blockquote>

                  <p>I meant that if it's proper to allow customization of paths and so on in <strong>this</strong> proposal, because a developer might want to have a different layout within a module, then to me that's also an argument <strong>for</strong> your proposal to abstract the paths logic away from the front controller <ac:emoticon ac:name="smile" /></p>

          2. Aug 29, 2008

            <p>So, since both the module name and the resource type are included in the class name, would this mean that one module can load resources from another module?</p>

            1. Aug 30, 2008

              <p>Yes, having a stack paths in the plugin loader allows this. So if you take the example module from the proposal, you could use the functionality that the users module resource classes provide anywhere in your app;</p>

              <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
              class Foo_BarController extends Zend_Controller_Action {

              public function init()

              Unknown macro: { $groupClass = $this->_helper->resourceLoader('Group'); $this->_group = new $groupClass(); }

              public function fooAction()

              Unknown macro: { $this->view->groupMembership = $this->_group->getGroupMembership($user); }

              }
              ]]></ac:plain-text-body></ac:macro>

              1. Aug 30, 2008

                fc

                <p>Something to keep in mind: What if a core developer wants to reduce the interdependencies between modules and allow other developers to load module-specific resources only. Would he/she be able to disable this option? </p>

                <p>For example:</p>

                <ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
                public function setDisableLoadExternalResources($flag)
                {
                $this->_disableLoadExternalResources = (bool) $flag;
                return $this;
                }

                public function loadExternalResourcesIsDisabled()
                {
                return $this->_disableLoadExternalResources;
                }
                ]]></ac:plain-text-body></ac:macro>

                1. Aug 31, 2008

                  <p>I can see where this will be handy, larger teams/projects may have policies that require certain modules to be independant, for QA or some such thing...</p>

                  <p>So we have a potential scenario where one module needs to be independent, and so the lead coder sets the flag to disable loading resources from external modules.</p>

                  <p>Now what happens if the module for which loading external resources is disabled, then pushes a new dispatch request onto the action stack? or does a _forward? As helper objects persist in the helper stack after their initial use, the 'disable load external' flag may be still set for a module that might want to load resources from yet another different module.</p>

                  <p>Federico, what do you think should be the expected behaviour of the flag? Matthew, do you have any thoughts on this?</p>

                  <p>We might be able to use the postDispatch() hook to toggle the flag back to it's previous state, so it might possible be enabled in one dispatch loop, but then toggled back to disable in the next.</p>

              2. Aug 30, 2008

                <p>And what assurances do I have that the Group model from the Users module is actually what gets loaded? What if some other module (for whatever reason) has a model named Group? There are times when a view/action-helper-like stack is useful (I use it frequently when I want to override core ZF view/action helpers), but I don't think this is one of them. There seems to be no way for me to explicitly request that the Group model from the Users module be loaded (and not simply the first model named Group that matches a prefix in the stack).</p>

                <p>I guess I was looking for something a little more like the <a href="http://framework.zend.com/wiki/display/ZFPROP/Zend_Controller_Action_Helper_ModelLoader?focusedCommentId=38704#comment-38704">ModelLoader proposal</a>, where the loader uses information contained within the class name that the user provides to determine what module to load from. This, however would require that the full class name be passed, which, I realize, is the opposite of how the PluginLoaders usually work.</p>

                1. Aug 31, 2008

                  <p>I do think that a developer or development team would make efforts to not have such collision, much like I personaly wouldn't have two database table classes named 'Users_Table_User' and 'Admin_Table_User'. On the other hand, there's the situation of trying to re-use a module from a third party. That module might contain a similarly named resource class.</p>

                  <p>My opinion is that this is a scenario where the problem will only arise using third party modules. To what lengths should this helper go to to help mitigate, prevent or deal with such situations? If at all. I think that for all but the most simple of modules using every framework default, at least some form of manual integration is necessary right now anyway.</p>

                  <p>I don't know that this is an area that needs addressed, I would like to try and solicit some more input and feedback on this but in the meantime here's a possible method of dealing with this using the current proposed design. </p>

                  <h4>Behaviour change through call parameter inference</h4>

                  <p>Note, I do not mean language type inference here, rather inferring meaning from the content or structure of the parameter.</p>

                  <p>Since all the relevant information to satisfy a more specific call to the resource loader is contained either in an array index or in a prefix string this would be possible to do, but, it might not be very elegant. There's a small but audible alarm bell ringing in the back of my mind about this.</p>

                  <p>For example, one possible solution may be to inspect the string passed as the resource name.</p>
                  <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
                  // example 1, both are equivalent calls
                  $this->_helper->resourceLoader('Group');
                  $this->_helper->resourceLoader('Group', 'model');
                  ]]></ac:plain-text-body></ac:macro>
                  <p>or</p>
                  <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
                  // example 2
                  $this->_helper->resourceLoader('Model_Group', 'users');
                  ]]></ac:plain-text-body></ac:macro>

                  <p>Since both are valid substrings of the resource class name, and an optional second argument for loadResource() is proposed anyway, the second argument could possibly take inference from the first.</p>

                  <p>Example 1 would then mean, load the 'Group' resource from the first module path that matches. Load a model as either the lack of second parameter or the structure of the first parameter infers a model class.</p>

                  <p>Example 2 would mean load the 'Group' resource from 'users' module, the structure of the first parameter infers that the second parameter is a module name.</p>

  2. Aug 29, 2008

    <p>Proposal updated to v1.1</p>

    <ul>
    <li>Added requirement to allow for custom resource types/plugin loaders</li>
    <li>Changed naming of example resource classes, added short note on rational behind class naming</li>
    <li>Updated example of using loader in theory of operation</li>
    </ul>

    <p>If there are no further comments at this stage on naming, scope of operation, customization I'll aim to modify the class skeleton and publish a prototype within a day or so. Use cases will be added at this point.</p>

  3. Sep 09, 2008

    <p>Proposal updated to v1.2</p>

    <ul>
    <li>Modified class skeleton to reflect previous comments</li>
    <li>Prototype published to SVN - <a class="external-link" href="http://andtech.googlecode.com/svn/trunk/">http://andtech.googlecode.com/svn/trunk/</a></li>
    </ul>

    <p>Restricting loading to the current module and/or specifying the module to load from is implemented in the most basic fashion in the prototype. While the way it works in the prototype could obviously be improved, it looks like there's a valid argument for perhaps using a different implementation of Zend_Loader_PluginLoader_Interface, or perhaps a more structured definition of a "resource type" within a class of it's own. This would perhaps allow greater flexibility for customizing resource loading.</p>

  4. Oct 31, 2008

    <p>Hi Graham,</p>

    <p>Your proposal looks good. Am I correct in assuming it's pretty much "done", seeing as it hasn't received updates in a while? If so, I'm actually considering implementing your prototype in an application. In any case you should definitely move your proposal to Ready For Review. Although, judging from the state of the proposal and comments, you might just as well move it to Ready For Recommendation directly <ac:emoticon ac:name="smile" /></p>

    1. Oct 31, 2008

      <p>P.S.: I've moved it to Ready for Review for you <ac:emoticon ac:name="smile" /></p>

  5. Nov 04, 2008

    <p>A question that just occurred to me... why is this an action helper? There are probably other instances where you will need to load models as well. For example, in other models.</p>

    <p>Having an action helper for this makes complete sense and is a good thing. However, I would like to propose that this functionality is refactored into Zend_Loader_ResourceLoader and the ResourceLoader action helper actually uses the Zend_Loader_ResourceLoader.</p>

    1. Nov 19, 2008

      fc

      <p>In theory the Controller updates and moves data between models, so you should delegate this responsibility to the Controller. There's some logic behind this, the direction of the data flow defines how data flows through the system, therefore it makes the system easier to understand and maintain.</p>

    2. Nov 25, 2008

      <p>There's a couple of things that might make more sense to have in another class consumed by the action helper. Your point about loading models from models is noted, thanks. I'll wait and see how this goes forward in the proposal process as I feel this proposal is fleshed out enough for consideration at this point.</p>

  6. Dec 29, 2008

    <ac:macro ac:name="note"><ac:parameter ac:name="title">Zend Comment</ac:parameter><ac:rich-text-body>
    <p>This proposal is being superceded in large part by <ac:link><ri:page ri:content-title="Zend_Loader_Autoloader_Resource - Matthew Weier O'Phinney" /></ac:link>; our recommendation is for Graham to work with Matthew to see that proposal to completion.</p>

    <p>In the meantime, this proposal will be archived, and a note added to it pointing reviewers to the new proposal.</p></ac:rich-text-body></ac:macro>