view loading "by hand" in zend - php

I am using the zend framework and trying to create and render a view from inside a controller. Normally this process is handled by the framework but I thought I could do it myself too as this part of the documentation states.
Unfortunately there is something wrong as the framework is still trying to load the default view as well. Here's my controller
<?php
class ViewController extends Zend_Controller_Action {
private $viewsFolder = null;
public function init()
{
$this->viewsFolder = realpath(dirname(__FILE__)) . '/../views/custom/';
}
public function indexAction()
{
// using a custom view (initialization and rendering executed by hand)
$view = new Zend_View();
$view->setScriptPath($this->viewsFolder);
$view->assign(array(
"dev_name" => "Fabs",
"framework" => "Zend frmwrk"
));
echo $view->render('customView.phtml');
}
}
and here is the error I get
Message: script 'view/index.phtml' not found in path (/home/ftestolin/stuff/rubrica/application/views/scripts/)
It looks like the normal view rendering cannot be suppressed. Any idea how to do it?

Probably better to disable the ViewRenderer rather than remove it. In controller:
$this->_helper->viewRenderer->setNoRender(true);
Remember that the ViewRenderer is where Zend_Form instances pull their default view for their own rendering. Removing the ViewRenderer means that it has to be re-instantiated later when the form needs to render. But when it does so, it recreates a brand new Zend_View instance. Any settings you have applied to your view - say, at bootstrap, setting doctype, etc - will be lost.

ok I will answer myself :)
one has to prevent the normal behaviour which is handled by Zend_Controller_Action_Helper_ViewRenderer
you can do it like that in the controller and then do your View instantiation business.
$this->_helper->removeHelper('viewRenderer'); // stop the default views rendering process
cheers

Related

Zend Framework: Method after controller action, but before view rendered

I'm new to Zend framework and can't find a clear answer on this. I essentially want some code to execute after a controller's logic for a page, but before the layout and view are rendered.
For example, I want to auto-refresh the flash messages and provide them to the layout/view automatically, so that I don't need to do so in every controller. This obviously needs to happen after the controller code has executed since it might add messages.
$this->view->messages = $this->_helper->flashMessenger->getMessages();
The easiest way to do this is with a controller plugin, see http://framework.zend.com/manual/1.12/en/zend.controller.plugins.html. The postDispatch() method runs after your controller code but before the page is rendered.
I just use:
public function init()
{
if ($this->getHelper('FlashMessenger')->hasMessages()) {
$this->view->messages = $this->getHelper('FlashMessenger')->getMessages();
}
}
I use this in init() and it works fine. You could use it in postDispatch(), preDispatch() or dispatchLoopShutdown() if you like. A controller plugin would not be out of line, I just haven't gotten around to doing one yet.

How to get controller from within controller plugin in zendframework 2?

I am writing a controller plugin in zf2.
I use the following method to get controller from within plugin, but it returns null.
$controller = $this->getController()
Any suggestion?
Note, this answer was based on my experience with ZF1, and a quick look at the ZF2 code. Check out this answer.
I haven't played with ZF2 yet, but if the dispatch process and plugins are similar to ZF1, a plugin can't access the controller (at least not in a trivial way) as the controller isn't even instantiated for some of the plugin hooks.
Update: Took a quick look at some of the stock ZF2 controller plugins (as I can't seem to find official docs on creating a custom plugin), and see checks like the following:
$controller = $this->getController();
if (!$controller || !method_exists($controller, 'plugin')) {
//...
So it seems like the controller may not be set in some cases. Since the plugins also support (what I understand to be) an event listener, my guess is that they still can be used at various times in the response process, which may be before a controller is assigned.
Hopefully someone who's used ZF2 can come along and set me straight; but perhaps I've at least pointed you in a somewhat reasonable direction.
There are two options for which you have no controller set in your plugin.
You call the plugin from the plugin manager prior to dispatch, so no controller is set yet
You call the controller inside the plugin during __construct()
For the first one, a typical example is an onBootstrap() method in a module class where obviously you have no controller:
public function onBootstrap($e)
{
$app = $e->getApplication();
$sm = $app->getServiceManager();
$plugins = $sm->get('ControllerPluginManager');
$plugin = $plugins->get('my-plugin');
// $plugin->getController() === null
}
This seems an obvious example, but there are other occasions where you are mistakenly assuming a controller exists already (for example, during run of the application, at the route phase; the dispatch still has to come).
The second example is because the controller is injected with setter injection. The setter is called after construction. In pseudo code, this happens:
$plugin = new $class;
$plugin->setController($controller);
If you have a plugin like this:
use Zend\Mvc\Controller\Plugin\AbstractPlugin;
class MyPlugin extends AbstractPlugin
{
public function __construct()
{
// $this->getController() === null
}
}
You notice there is no controller set at that phase.

How to make Zend automatically switch view and layout with contexts?

I have a mobile site that I added detection to for iPhones and other iOS devices. The iOS page needs a different layout and views than the regular pages (which are actually for older mobile devices). So, I have some code that does mobile detection, that part was easy. What I'd like to do is make it so that Zend automagically finds and uses the correct layout and view when an iOS device is detected, but that has turned out to be surprisingly hard...
I needed it to be up and running ASAP, so I did a quick and dirty hack that worked: in each action function, I have a simple If statement that detects if the iOS boolean flag has been set (which happens in the controller's init), and if so, overrides the layout and view explicitly. Existing code (in the actions):
if ($_SESSION['user']['iPhone']) {
$this->_helper->layout->setLayout('osriphone'); // 'osr' is the name of the app
$this->_helper->viewRenderer->setRender('iphone/index');
}
So this works, but it's kinda ugly and hacky and has to be put in each action, and each action's Renderer has to be set, etc. I got to reading about the Zend ContextSwitch, and that seemed like exactly the kind of thing I should use (I'm still kind of new to Zend), so I started messing around with it, but can't quite figure it out.
In the controller's init, I'm initializing the ContextSwitch, adding a context for 'iphone' and setting the suffix to 'iphone', and now what I'd like to do is have a single place where it detects if the user is an iOS device and sets the context to 'iphone', and that should make it automatically use the correct layout and view. New code (in the controller's init):
$this->_helper->contextSwitch()->initContext();
$contextSwitch = $this->_helper->getHelper('contextSwitch');
$contextSwitch->addContext('iphone', array('suffix' => 'iphone'));
$contextSwitch->setAutoDisableLayout(false);
if ($_SESSION['user']['iPhone']) {
//$this->_currentContext = 'iphone'; // Doesn't work.
//$contextSwitch->initContext('iphone'); // Doesn't work.
//$contextSwitch->setContext('iPhone'); // Not the function I'm looking for...
// What to put here, or am I barking up the wrong tree?
}
I did some reading on the contextSwitcher, and it seems like there is a lot of stuff on, e.g. setting it to be specific to each particular action (which I don't need; this needs to happen on every action in my app), and going through and modifying all the links to something like /osr/format/iphone to switch the context (which I also don't really need or want; it's already a mobile site, and I'd like the layout/view switch to be totally transparent to the user and handled only from the backend as it is with my quick and dirty hack). These seem like basically an equal amount of code to my quick and dirty hack. So... Anyone have some suggestions? I'm really hoping for just a single line like "$contextSwitch->setContext('iphone');" that I could use in an If statement in my controller's init, but the Zend documentation is awful, and I can't seem to find any examples of people doing something like this on Google or SO.
Ok I think I figured out how to put this into a plugin:
The Plugin:
//This is my own namespace for ZF 1.x library, use your own
class My_Controller_Plugin_Ios extends Zend_Controller_Plugin_Abstract {
public function preDispatch(Zend_Controller_Request_Abstract $request) {
parent::preDispatch($request);
if ($_SESSION['user']['iPhone']) {
$this->_helper->layout->setLayout('osriphone');
$this->_helper->viewRenderer->setRender('iphone/index');
}
}
}
register the plugin in your application.ini
resources.frontController.plugins.ios = "My_Controller_Plugin_Ios"
I think that's all there is to it. Although you may want to look into the userAgent plugin
ContextSwitch operates off the "format" property in the request object (by default). You need to set it somewhere in your app
$requestObject->setParam('format', 'iphone').
I'd set it in a bootstrap, or more appropriately, a controller plugin, but where it goes really depends on your app.
I don't use Zend ContextSwitch so I can't really help there, but you could use some inheritance in your controllers to set all layouts in just a couple of lines. Even though it might still be classed as a "hack" it is a way better hack
Now whenever you execute a action Zend first fires a number of other functions within the framework first, such as the routing, the preDispatch, Action helpers and so on. It also fires a number of things after the action such as PostDispatch. This can be used to your advantage.
First create a controller called something like "mainController" and let it extend Zend_Controller_action and in this controller create a function called predispatch()
Second. Extend your normal controllers to mainController. Since we now have a function called predispatch() Zend will automatically fire this on every controller, and if you do your iPhone/iOS check there it will automagically be performed on every action on every controller, as long as you don't overwrite the method in your controller (you can make this method final to prevent this). You can offcourse use a multitude of different non-Zend functions and/or helpers within the mainctroller to make the code as compact and reusable as possible Se example code below:
<?php
/**
*Maincontroller
*/
class MainController extends Zend_Controller_Action
{
/**
* Predispatch function is called everytime an action is called
*/
final public function preDispatch(){
//for security reasons, make sure that no one access mainController directly
$this->request = $this->getRequest();
if (strtolower($this->request->controller)=='main')
$this->_redirect('/index/index/');
//Check for iPhone
if ($_SESSION['user']['iPhone']) {
$this->_helper->layout->setLayout('osriphone'); // 'osr' is the name of the app
$this->_helper->viewRenderer->setRender('iphone/index');
}
}
}
<?php
/**
*Othercontroller
*/
class OtherController extends MainController
{
/**
* The correct layout for IndexAction is already set by the inherited preDispatch
*/
public function indexAction(){
/* YOUR CODE HERE */
}
}
For a good overview of the dispatch process check these links (same picture in both):
http://nethands.de/download/zenddispatch_en.pdf
http://img.docstoccdn.com/thumb/orig/22437345.png

Passing variables to layout after bootstrapping layout resource

I had to create method in Boostrap which bootstraps Layout resource and registers some view helpers.
protected function _initViewHelpers() {
$this->bootstrap('layout');
$layout = $this->getResource('layout');
$view = $layout->getView();
$view->registerHelper(new Application_View_Helper_LoadMenu, 'loadMenu');
$view->registerHelper(new Application_View_Helper_InfoLink, 'infoLink');
$view->registerHelper(new Application_View_Helper_InfoData, 'infoData');
}
Now, I am passing some variables to layout (to Zend_View instance, as always), but layout doesn't recognize that it has them.
When I move code which registers helpers, to init() method in controller, everything is ok. Is it ZF error or I did sth wrong?
In Your Controller (or wherever you have view)
$view->layout()->some_var = "Some Value";
In Your Layout
<?php echo $this->layout()->some_var; ?>
if i'm missing some part of your question let me know.
Edit: failing the above, the other correct way to do this would be to use the placeholder helper (http://framework.zend.com/manual/en/zend.view.helpers.html#zend.view.helpers.initial.placeholder)
Edit 2: Make sure you are bootstrapping view as well.
$this->bootstrap('view');
$view = $this->getResource('view');
Do your view-helpers implement the setView() method, either directly or perhaps as subclasses of Zend_View_Helper_Abstract?
If you look at the code for Zend_View_Abstract::registerHelper($helper, $name) method, you will see that it checks for the presence of a setView() method on the helper. If it finds such a method, then it calls $helper->setView($this), where $this is the $view.
This is where the connection takes place. In the absence of this call, it seems that although the view will be aware of the helper (after all, you did just register it), the helper will be unaware of the view. If the helper tries to access the view, it ends up creating a new view object, which is not the one you configured way back in Bootstrap.
tl;dr: There is probably no need to explicitly register the helpers. With the default resource-autoloader in place and the class/method naming convention you seem to be using, you can probably allow the built-in plugin-loader to handle all the instantiation. Simply call $this->myHelperMethod() in your layouts or view-scripts and all should be cool.

Zend Framework - Set No Layout for Controller

I have a Controller that I want to use for ajax scripts to call and set session variables, get information, etc. How do I set it so that that particular controller doesn't use the default layout (specifically NO layout) so that it can send XML/JSON messages back and forth?
Like anything to do with Zend_Framework and Zend_Application, there are multiple ways to do this, but on the last few pure Zend gigs I've done, I've seen people using the following (from an action method in you controller)
$this->_helper->layout()->disableLayout();
This shuts off of the layout. If you wanted to turn off your view as well, you could use
$this->_helper->viewRenderer->setNoRender(true);
again, from an action method in the controller.
in your controller ...
public function init() {
if ($this->getRequest()->isXmlHttpRequest()) {
// no Layout
$this->_helper->layout()->disableLayout();
// no views
$this->_helper->viewRenderer->setNoRender(true);
}
}
In your controller action, try
$this->_helper->layout->disableLayout();

Categories