Console Autoload Not Working Properly, Compared To Web Application

Background:

I got a web application, and now I’m extending it with console access for cron jobs in the future.

I’m using 2 separate config files, “main.php” for the Yii Web Application, “console.php” for the Yii Console Application.

Problem:

I’m using modules extensively.

I have build my own "Default Module".

All the other modules extend from this Default Module.

In my web application I make this work by editing the "main.php" config file like this:




// Web Application config


'modules'=>array(

		'default',

		'clients',

		'household'

	),


'import'=>array(                 

		'application.components.*',                 

		'application.models.*',    

		'application.helpers.*',   

	), 



This works, great!

But I want to do the same for the Console part, so to the console config, I add this same snippet of code but it doesn’t work…:

When executing my code I will get the following error:

I have found a way to fix it though, by adding the following to my "console.php":


	

// Console Application config


'modules'=>array(

		'default',

		'clients',

		'household'

	)


'import'=>array(                 

		'application.components.*',                 

		'application.models.*',    

		'application.modules.default.controllers.*',  // Line I have to add

		'application.modules.default.models.*',     // Line I have to add

		'application.modules.default.views.*',     // Line I have to add

		'application.helpers.*',   

	),  



So this solution makes it work,

but why do I have to do this for the Console and not for the Web ?

My question:

Why do I have to manually add the controllers, models, and views directories of my Default module like this in my "console.php" config file in the IMPORT array,

while I do not have to do this in my Web application "main.php" config file?

I “solved” it this way, but it’s dirtier and I don’t trust it, because in the future if I’m going to extend from other modules, I might run into the same problems.

Could someone help me with the correct solution, or point out the problem ?

Maybe my initial post was a bit unclear.

I’ve done an Edit and added the Error message to it.

I am really stuck on this one, and I can’t find any answer.

Excuse my bump :)

I wish I could help you, but I am having a very similar problem, and I cannot find a solution anywhere. Maybe someone will be able to help one or both of us.

In my situation, I have a set of helper functions in a file named utilities.php located in the components folder. In my web application, I can call these functions normally and everything works fine. But in the console application I am developing now, I get:

Fatal error: Call to undefined function getpreferredoptions() in /path/to/public_html/protected/commands/MyCommand.php on line 9

I copied the import section of my main.php config file into console.php:


<?php


// This is the configuration for yiic console application.

// Any writable CConsoleApplication properties can be configured here.

return array(

	'basePath'=>dirname(__FILE__).DIRECTORY_SEPARATOR.'..',

	'name'=>'Polli Team Console Components',


	// autoloading model and component classes

	'import'=>array(

		'application.models.*',

		'application.components.*',

		'application.controllers.*',

		'application.commands.*',

		'ext.yii-mail.YiiMailMessage',

		'application.modules.moneyshot.models.*',

	),

);

Like I said, the web app correctly imports utilities.php, but the console application will not. I even added ‘application.components.utilities’ to console.php, and it still does not work.

I’m dead in the water on this application until I get an answer, and I cannot find anything anywhere.

A little late, but I ran into this problem, too. In my module’s class I do this (the module is called dominion):




class DominionModule extends CWebModule {

    public function __construct($id,$parent,$config = null) {

        parent::__construct($id,$parent,$config);

        $this->setImport(array('dominion.models.*'

                              ,'dominion.controllers.*'

                              ,'dominion.components.*'));

    }

}; // class DominionModule extends CWebModule



I’m thinking in a complex application with many modules this could get out of hand quickly and become a performance issue. In my case this module implements one part of my site (the url will always be /dominion/…). I wonder if I can prevent the module from being loaded unless the url includes /dominion/. That’s a project for another day.

Edit: I was forgetting to call the parent constructor.

Replying to myself, hurr, of course the autoloader will only load my module on demand. I have a chicken-and-an-egg problem, though, where I want my dominion module to be loaded when the url starts with /dominion/ and I want my module to control its own url structure (via CUrlManager::addRules()). The problem is I have to invoke that method in CWebApplication’s onBeginRequest; at that point, CHttpRequest hasn’t parsed the request at all. If I enumerate the application’s configured modules and call getModule() on them, it invokes the autoloadeer on all the module. Including gii, which overrides my custom CWebUser class, along with some other nastiness.

In my DominionModule class I have an addRules() method which adds rules to CUrlManager. But I only want that if the url starts with /dominion/. I guess it’s sensible for my onBeginRequest handler to get the first part of the url to look for ‘dominion’. I could be a little smarter about it and take the first part of the url and compare it to the application’s modules, and if it matches, call getModule() on that, check method_exists to see if it has an addRules method, and then invoke it. Here’s what I wound up with for my onBeginRequest:




    function() {

        $uri = $_SERVER['REQUEST_URI'];

        // get rid of GET params & anchor names

        $uri = preg_replace('/[?#].*$/','',$uri);

        // replace duplicate slashes.

        $uri = preg_replace('{/{2,}}','/',$uri);

        // remove leading/trailing slashes

        $uri = trim($uri,'/');

        // get the first part of the uri

        $uri = explode('/',$uri,2);

        if(empty($uri)) {

            return;

        }

        foreach(Yii::app()->modules as $id => $config) {

            if($id == $uri[0]) {

                // i.e., this module name matches the first part of the uri

                $module = Yii::app()->getModule($id);

                if(method_exists($module,'addRules')) {

                    // i.e., this module has a method named addRules

                    $module->addRules();

                }

                return;

            }

        }

    }



I’m not terribly pleased with this solution, because it feels ambiguous with the url manager, and in the onBeginRequest since the url hasn’t been parsed, I have no way of knowing if the first component of the url is path info. I would love to hear other solutions to this problem.

You are not late.

This topic is still unsolved and I haven’t found a solution thus far :(

I really hope someone can help us with this.

OP: I thought you were going to create a minimal test application that we can use to look into this problem?

Use ‘preload’ ?

Maybe this helps: https://github.com/yiisoft/yii/issues/2344#issuecomment-16380420

I accidentally found out why helpers file is not included for console application. I believe whatever CeBe said is correct. The reason helpers file is included for my web application because I have




require_once( dirname(__FILE__) . '/../components/helpers.php');



in config.main.php

I bet if I put the require_once in console.php. It will work.