View Rendering from Console App

I’m just building some console functionality into a half existing web application and came across the seemingly common problem of how to render views without a controller reference. I’ve seen plenty of fine implementations and they work no problem.

The problem is, how do I use existing functionality (namely components and models), of the web app, within the console app which need to render views. Not just for the purpose of output to a browser but for mail, XML among other things as well. They currently use Yii::app()->controller to get their controller instance, this obviously doesn’t work in console apps, and even the hacky quick fix on Yii::app()->controller = MyConsoleCommand doesn’t work because controller is read only.

What’s the best standard way of rendering a view when outside the scope of the request handler (Whether Controller or Command). Something like Yii::app()->renderViewFile() would be fine, doesn’t use latyouts, just parses a file alias and renders it, returns the content.

Any Ideas?

OK I’ve managed to find a work around. It’s a little horrible and I’m hoping there’s some standardized way of rendering that I’ve missed.

I’ve overridden the CConsoleApplicaton class, which is harder than it sounds because you have to copy the yiic index files to use a custom Yii base class so you can provide the overriden createConsoleApplication method.

The application can now continue to use Yii::app()->controller->renderPartial/Text etc. so it means very little change to the current code.

The new EConsoleApplication looks like this:





class EConsoleApplication extends CConsoleApplication

{

	private $_controller = false;

   	public function getController(){

		if ($this->_controller === false)

			$this->_controller = new Controller('site');

	

		return $this->_controller;

	}

	

	public function getViewRenderer(){

		return null;

	}

	

	public function getViewPath(){

		return $this->getBasePath().DIRECTORY_SEPARATOR.'views';

	}

	public function getTheme(){

		return NULL;

	}

}



It grabs the default controller and passes off view rendering to it. You cannot use render using this method (by design because you’re in a console) but you can use render partial etc.

this isn’t ideal as it means overwriting the yiic application files with the framework ones, the yii class and the consoleApplication component.

Not great to say all I want is to be able to render a view in my application without having to do console specific code.

Yii-mail has a great and simple example for this.




// if Yii::app()->controller doesn't exist create a dummy 

// controller to render the view (needed in the console app)

if(isset(Yii::app()->controller))

    $controller = Yii::app()->controller;

else

    $controller = new CController('YiiMail');


// renderPartial won't work with CConsoleApplication, so use 

// renderInternal - this requires that we use an actual path to the 

// view rather than the usual alias

$viewPath = Yii::getPathOfAlias(Yii::app()->mail->viewPath.'.'.$this->view).'.php';

$body = $controller->renderInternal($viewPath, array_merge($body, array('mail'=>$this)), true);	

hi, thanks for the help.

Yeh that’s pretty much what I arrived at for rendering a view in the console specific models but I’m not keen on putting this chunk everywhere, I’d rather put it somewhere shared and share a common rendering method between console and web because I have so much shared functionality between my console and web app.

But the only real thing console and web apps share is the CApplication Component and as I said this gets messy to override. I was just hoping Yii had some nice hidden feature that I didn’t know about for common rendering but perhaps not.

Thanks

Hi

I know it’s a very old topic, but I got here today trying to find a solution to my problem. Then realized I have solved it somewhere in my old code, and found it!

Maybe it will be useful to someone ;)




if(isset(Yii::app()->controller)) // browser

{

    $body = Yii::app()->controller->renderPartial('/your_views_subfolder/'.$view, array('data' => $data), true);

}

else // console

{

    $controller = new CController('context');


    $body = $controller->renderInternal(__DIR__.'/../views/your_views_subfolder/'.$view.'.php', array('data' => $data), true);

}



1 Like