Well, it’s been a while since I’ve done a PHP update hasn’t it? Well alls well here in CStat, I have a quickie for you folks.
Recently at work I had the need to run a script before every single controller (namely to add a plugin folder to Dwoo) for a specific module that I did not desire for any other modules.
I could have subclassed all my controllers to extend a custom action controller that handled this in the init() method, however I’m lazy so I wrote a quick Zend Controller Plugin to handle this for me.
What it does is grabs the module name from the request object on routeShutdown (the routeShutdown happens after the route has been parsed and the request object has been generated, but before the action controller is instantiated and executed). From there it uses Zend_Font_Controller::getInstance()->getModuleDirectory(...) to grab the literal path to the module directory, tacks on our init file name (in our case “init.php”), and if it exists execute it in its own clean environment.
The init.php file has access to $this, where $this is an instance of the controller plugin (in particular we can do $this->getRequest() inside the init script).
The code is as follows:
<?php
/**
* This checks the current request module's directory for an initFile (defaults
* to init.php) and runs it before the controller is loaded.
*
* @copyright 2009 Daniel Cousineau
* @license http://opensource.org/licenses/mit-license.php MIT License
* @version 0.1.0
*/
class My_Controller_Plugin_ModuleInit extends Zend_Controller_Plugin_Abstract
{
public static $initFileName = "init.php";
/**
* @param Zend_Controller_Request_Abstract $request
* @return null
*/
public function routeShutdown(Zend_Controller_Request_Abstract $request)
{
$moduleName = $request->getModuleName();
$moduleDirectory = Zend_Controller_Front::getInstance()->getModuleDirectory($moduleName);
//Trim the paths and filenames to prevent any problems
$initFile = rtrim($moduleDirectory,'/\') . '/' . ltrim(self::$initFileName,'/\');
$this->runInitFile($initFile);
}
/**
* Run the file in its own cleaned scope
*
* @param string $_initFile location of the input file
* @return null
*/
protected function runInitFile($_initFile)
{
if( file_exists($_initFile) )
include_once $_initFile;
}
}
As always all code I post on this blog is covered under the MIT license.
Comments 7
I actually had done the same thing a month or two back, only handled inside the dispatcher. This “feature” is something we are considering in 1.8 along with the Zend_Application initiative.
You can see my prototype here:
http://svn.ralphschindler.com/repo/Scratch/ZFApp+ModuleInitSupport/library/ZendL/Controller/Dispatcher/Standard.php
Posted 07 Jan 2009 at 11:03 am ¶Personally I think we’re best off with this functionality as a Controller Plugin that is included/enabled by default (essentially mimicking the ViewRenderer). (A) we’ll be consistent with the standard built by the ViewRenderer, (B) keeps ZF modular (easy to extend), and (C) easy to disable as well as maintain.
However your approach of placing it as a custom dispatcher works well too.
Posted 07 Jan 2009 at 1:37 pm ¶Well, let me further explain the ideas behind putting it inside the addControllerDirectory(). In my mind there is a single driving factor: reusable modules. In order for them to be drop in and go, some modules might need to have a place to bootstrap themselves, and this typically means prior to any “dispatching”. This then allows modules (that are bootstrappable), to be able to inject routes (something that needs to happen before routeStartup), and to effectively “bootstrap” all their needs (even throw bootstrap exceptions).
To do this, the module would need to be hooked in during addControllerDirectory time. The reasons for putting it inside the dispatcher is so that there is no prerequisite for any given reusable module to have the moduleInit plugin enabled.. it can simple expect to work in most, if not all, ZF FrontController environments.
Consider this the way that the FrontController (during bootstrap), delegating module bootstrap back to the Module, instead of forcing developers to add code to their bootstrap to make it run.
-ralph
Posted 07 Jan 2009 at 2:25 pm ¶Ah, I see what you’re saying.
I could easily modify my plugin to do a routeStartup(), I think you’re still able to inject a route… However that would obviously have to dig through every module and include a file (in this case it may be best to move towards a file that contains a module class).
My code only needed to run only when in the specific module.
Posted 07 Jan 2009 at 2:40 pm ¶Adding the Module loader to the addControllerDirectory would cause it be loaded on all requests, even if that request is not for that module – since the routing class has yet to determine what the requested module is and you’d need to add the controllerDirectory in order to add it to the list of valid modules?
For the router plugin, certainly a good exercise of thought, another might be if that all respective application module controllers extended a base controller, then just doing that in the preDispatch ‘could’ suffice? Reason for saying that is that a router plugin would not of been the first association of where ‘to look’, maybe.
Posted 09 Jan 2009 at 12:38 pm ¶Can we Build a CMS in zend framework where controller directory is not used.
Posted 23 Jan 2009 at 3:04 am ¶waiting for reply
@Amit: Frankly you will need at least a controller directory, however what you can do is have your controller directory hidden away and use the module-specific errorcontroller plugin (http://www.toosweettobesour.com/2008/08/11/quickie-module-specific-error-controllers-in-zend-framework-15/) to select views to display.
Posted 28 Jan 2009 at 7:34 pm ¶Post a Comment