Yii Command line - How to initialize my entire web app and call other controller actions via yiic?

1.9k views Asked by At

Update - I was able to solve this by adding application.controllers.* to config/console.php like so:

'import'=>array(
        'application.models.*',
        'application.components.*',
        'application.controllers.*',
        'application.extensions.CAdvancedArBehavior',
        'application.extensions.eUploadedImage.*',
       ),

then calling the actions in the cli command like this:

$myCtrl=new CopyNumberNewSegController(NULL);
$myCtrl->actionBatchImport($libraries,$action);

I have a simple yiic command in protected/commands/dBManagerCommand.php that I can run from the command line:

class DBManagerCommand extends CConsoleCommand
{
    public $verbose=false;
    public $divider="--------------------------------------------------------------------------\n";
    public $title="DB Manager Command\n";
    public function actionImportAnalysis($libraries=false,$action='incomplete') {
        echo $this->divider;
        echo $this->title;
        echo $this->divider;
        echo 'Importing '.$action ." analysis data from file system...\n";
        echo $this->divider;

        switch ($action) {

            case 'incomplete':

                CopyNumberNewSegController::batchImport($libraries,$action);


                break;
        }

        return 0;
    }
}

Running it produces the following error. Apparently it can't find my other controllers. I want to run methods from existing controllers in the rest of my application. This is run from the bash shell (ignore code colors)

yiic dbmanager importAnalysis --action=incomplete
--------------------------------------------------------------------------
DB Manager Command
--------------------------------------------------------------------------
Importing incomplete analysis from file system...
--------------------------------------------------------------------------
PHP Error[2]: include(CopyNumberNewSegController.php): failed to open stream: No such file or directory
    in file /var/www/html/mioncoseq/pub/framework/YiiBase.php at line 418
#0 /var/www/html/mioncoseq/pub/framework/YiiBase.php(418): autoload()
#1 unknown(0): autoload()
#2 /var/www/html/mioncoseq/pub/protected/commands/dBManagerCommand.php(21): spl_autoload_call()
#3 unknown(0): DBManagerCommand->actionImportAnalysis()
#4 /var/www/html/mioncoseq/pub/framework/console/CConsoleCommand.php(141): ReflectionMethod->invokeArgs()
#5 /var/www/html/mioncoseq/pub/framework/console/CConsoleCommandRunner.php(65): DBManagerCommand->run()
#6 /var/www/html/mioncoseq/pub/framework/console/CConsoleApplication.php(91): CConsoleCommandRunner->run()
#7 /var/www/html/mioncoseq/pub/framework/base/CApplication.php(162): CConsoleApplication->processRequest()
#8 /var/www/html/mioncoseq/pub/framework/yiic.php(33): CConsoleApplication->run()
#9 /var/www/html/mioncoseq/pub/protected/yiic.php(7): require_once()
#10 /var/www/html/mioncoseq/pub/protected/yiic(4): require_once()

So then how can i load those controller methods? I'm sure there is a proper way to do this that will initialize my entire app, without trying to manually include those controller files.

Thanks everyone!


In response to the criticism about misuse of MVC architecture below, I'm going to paste this response here because the comment field is not long enough.

Firstly, I wouldn't pre-load all controllers for the web interface, but for this purpose it solved the problem and works fine. The import method initially takes requests thru the controller but then calls model methods where appropriate for db and file system level logic and operations. (And yes I do have other import model classes dedicated for this purpose, mostly because of behavior-related memory leaks in Yii).

The idea that one should not do any logic in a controller is just wrong, imo. In this case I've chosen to make import methods in controllers that do some input filtering logic, because there are a large number of inputs that the user (human or cron) needs to submit, and this needs to occur before we get to the task of parsing files into the db. This controller import method parses files based on user input and passes the pre-filtered data to the model method. I've chosen to do this because file input data is actually untrusted user input and I needed to do some pre-processing based on the user request params. Yes you could do this in a model method, but it would result in either processing request data in the model, or passing a large number of arguments and data around, which is a hassle and increases memory usage.

1

There are 1 answers

3
Michael Härtl On

Usually problems like this are a sign of a suboptimal architecture. I'd say it's one of the most frequent developer mistakes to have too much model related code in the controller, when working with a MVC framework.

Assumedly your code will batch import data from a file (or some other data source). This is a model task! So you should write a method in the related model class, e.g. a public static import($filename). Inside that method you should also avoid tight coupling to other components (e.g. Yii::app()->user which is not available in CLI).

If it's a more complex task, you could even write a dedicated class for your import. That class could represent an import job and provide methods like import() or getError() to fetch the error status of the import.

Decoupling classes like this increases reusability a lot: You now can use that model method or import class from both, your controller action and your CLI command.