modules in console applications

I just experienced some inconsistencies between web applications and console applications. It turns out, that console applications don’t define aliases for modules. So when trying to use some component from an module in a console application, the following code will cause an exception:




public function behaviors()

{

  return array(

    'someBehavior' => array(

      'class' => 'moduleId.behaviors.SomeBehavior',

    ),

  );

}



The same works well in web applications: “moduleId” is recognized as alias, the module will be loaded and initialized, everything’s okay.

Since the alias isn’t recognized in a console app, you have to use another alias like this:




public function behaviors()

{

  return array(

    'someBehavior' => array(

      'class' => 'application.modules.moduleId.behaviors.SomeBehavior',

    ),

  );

}



But this only leads to more problems: The module won’t get loaded nor initialized. Since my module does some important stuff during initialization (setting imports, configuring the components it ships, …) bypassing the initialization is a serious problem.

So now I wonder, why console applications don’t support modules in the same way web applications do?

Are you sure, you configured these modules also in your console config file?

In other words:

‘config/main.php’ is not used at all; ‘config/console.php’ is.

A very common oversight.

I have done that numerous times… :)

I just double checked, and I do use the correct config.

I think the root of the problem is YiiBase::getPathOfAlias. If you look at its source, you can see it only searches for a module named like the first piece of the alias string ($rootAlias) if it is called from a CWebApplication.

Check CModule::setModules() (this is called when you have ‘modules’ in your configuration). You’ll see that an alias is added for every module. It’s the same for web and console applications: both extend CModule.

Ah, I see. But this is only done if you don’t specify a class element in your module config.

My modules definition looks like this (from the signals module’s demo app):




  'modules'=>array(

    'signals'=>array(

      //'class'=>'application.modules.signals-1_0.SignalsModule',

      'class'=>'application.modules.signals-trunk.SignalsModule',

      'components'=>array(

        'db' => array(

          'class'=>'CDbConnection',

          'connectionString'=>'sqlite:'.dirname(__FILE__).'/../data/signals.s3db',

        ),

      ),

      'signals'=>array(

        'Post\\onPostPublished',

      ),

    ),

  ),



So, because I override the default naming convention, yii doesn’t set an alias for me.

For CWebApplications, YiiBase::getPathOfAlias() handles this case, the module will be created although the alias name is missing. For CConsoleApplications, this doesn’t work.

Now I wonder why yii doesn’t set an alias if I provide a class name?

Couldn’t CModule::setModules() work like this:




if(is_int($id))

{

  [...]

}

// --- mod start -------------------

if(isset($module['class']))

{

  // alias without the last section

  $aliasBase = substr( $module['class'], 0, strrpos($module['class'],'.')-1 );

  // path to the module class

  $moduleBasePath = YiiBase::getPathOfAlias( $aliasBase );


  Yii::setPathOfAlias( $id, $moduleBasePath );

}

else

{

// --- mod end ---------------------

  Yii::setPathOfAlias($id,$this->getModulePath().DIRECTORY_SEPARATOR.$id);

  $module['class']=$id.'.'.ucfirst($id).'Module';

}

[...]



To me, this looks like a bug, since the guide states that:

Seems to make sense. Open a ticket?

I’ll open one. Thanks for your help!

//EDIT:

Issue: http://code.google.com/p/yii/issues/detail?id=2570

//EDIT2:

Since qiang decided not fix it: Any ideas on how to target the issue?

To deal with this situation, I use the onBeginRequest event within my config to manually preload any modules that I need to perform their own initialization. With such a process, you can ensure that your unconventional modules can be depended upon to initialize properly.

Since I’m able to depend upon module init running for my modules at Yii startup, I can even add custom module routes, aliases, etc etc, within the module’s [pre]init methods.