Is there a canonical way of encapsulating the urlManager rules within a module itself?
In my case, I have a medium sized application split into six modules with roughly 110 url rules split between them. All modules are not active in all configurations so it seems appropriate to encapsulate a particular module's URL rules within the module itself.
I've tried a few things but since a module's init() and preinit() normally happen after the request (and the assignment of get/post variables), I can't reliably use them. So far, the "best" solution I've come up with has been to extend CWebModule, implement a static getUrlRules() interface method and call that method inside the application's onBeginRequest(). See below:
GetUrlRules.php
interface GetUrlRules { /** * Method to return urlManager-parseable url rules * @return array An array of urlRules for this object */ public static function getUrlRules(); }
MyWebModule.php
abstract class MyWebModule extends CWebModule implements GetUrlRules { }
main.php
... 'onBeginRequest => array('ModuleManager', 'getUrlRules'), ...
ModuleManager.php
class ModuleManager { /** * Method to add the url rules of each module to the main application rules (intended for * onBeginRequest) * @return boolean Returns true if successful */ public static function getUrlRules() { $method = 'getUrlRules'; $app = Yii::app(); $urlManager = $app->getUrlManager(); foreach ($app->getModules() as $moduleName => $config) { if (is_callable($moduleName . '::' . $method)) { $urlManager->addRules($moduleName::$method()); } } return TRUE; } }
class MyModule extends MyWebModule { public static function getUrlRules() { return array( '<module>/users/edit/<id:\d+>' => '<module>/users/edit', '<module>/users/disable/<id:\d+>' => '<module>/users/disable', '<module>/users/add' => '<module>/user/add', ); } }
While all of this works, it just feels very hack-ish and I'm hoping someone might have an idea of a simpler more elegant way to achieve the same effect. This was based on something I saw months ago (but couldn't find again today) that used a non-static module method to achieve the same effect. In that instance, the user executed a Yii:app()->getModule($moduleName) inside the ModuleManager and called the getUrlRules method. This looked something like the following:
ModuleManager.php
... foreach ($app->getModules() as $moduleName => $config) { $module = Yii::app()->getModule($moduleName); $urlManager->addRules($module->getUrlRules()) ...
Since the getModule() call initializes each module the getUrlRules method is unnecessary; at that point everything for adding url's could be put inside each module's init(). In some ways, the adapted approach (using CWebModule::init()), is ideal because we don't have to check if the getUrlRules method exists. The downside to this approach is that it blows the lazy-intention of the autoloader to pieces by forcing every module to load/instantiate onBeforeRequest.
Are there any ideas out there on the 'right' way to approach this issue?