URL Rules in module definition

Hi.

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?

:lol:

i think your solution is better ; some time initialize a module is expensive . if just for collect the url rules from different module your method is enough , the static method is proper .

for the method "public static function getUrlRules()" i just think if use cache is better ?

Given how a request is processed I think you are using the only way of doing things right now.

"onBeginRequest" is the only place you can interfere with configuration before the url manager is requested to generate the route.

We can’t always have things as clean as we would like :)

Ideally only just enough should be done to determine whether a module needs to be loaded or not.

When the module is loaded it’s configuration should be merged with the global config.

That would be a lot cleaner imo.