Zend_Controller

The Redirector action helper

Following on from the discussion on the FlashMessenger action helper, I thought I’d also cover another supplied helper: Redirector.

Redirector does what it says on the tin and redirects the user to another page. I mostly use this when coming back from filling a form in, so that the user is then redirected to another page. In admin systems, this is usually a list page. On front end websites, this is usually a thank you page. Though for log-in forms, I tend to try and return the user to where they were going!

It’s used in a controller action method like this:

$urlOptions = array('controller'=>'index''action'=>'index');
$this->_helper->redirector->gotoRoute($urlOptions);

gotoRoute() takes the same set of parameters are the url() view helper which is not a surprise as they both proxy through to the Front Controller’s router object. It’s handy though as one you know one, you know the other :)

If you are using the default route, then you can use gotoSimple(). For example to redirect to the news controller’s list action, you would do:

$this->_helper->redirector->gotoSimple('list''news');

The gotoSimple() method signature is:

gotoSimple($action$controller null$module null, array $params = array());

As you can see, it provides defaults for the controller and module and params parameters so you only need to set them if you need to. This works well for admin system as I tend to be redirecting within the same controller (from the edit or delete action to index, usually).

You can also use the Redirector with an absolute URL, by using the gotoUrl() method:

$url 'http://www.akrabat.com';
$this->_helper->redirector->gotoUrl($url);

I tend to use this one much less frequently – so infrequently, that I can’t think of a use-case off the top of my head :)

By default, Redirector sets a 302 status code, however you can also set a 301 if you want to:

$this->_helpers->redirector->setCode(301);

There are a few other options that can be set like setExit() and setUseAbsoluteUri(), but to be honest, I don’t think I’ve ever used them!

I find that I use Redirector fairly frequently as its gotoRoute() uses the same parameters as url() which makes it easy to remember how to use it. Like url(), it also benefits from remembering which route was used to get you to the current page and reuses that when creating the next one which is handy.

Author: Rob…

Zend Framework context switching for HTML content

I tried to stay away from using javascript as much as I could, but even I could not escape from developing AJAX feature rich applications. I have chosen jQuery as my poison.

But I had already build my apps using static HTML output generated by Zend Framework, so how could I add this richness to my apps without refactoring most of my code ? Simple, by using Zend_Controller_Action_Helper_ContextSwitch, ZendX_JQuery and some minor adjustments to my view scripts.

If you have downloaded the latest Zend Framework package, you’ll notice in the extras/library directory the extra jQuery component, so make sure you also include that library path to your application’s include path.

The following code was tested with Zend Framework version 1.10.3.

In your controller, you have actions that displays a form and one that processes it. So, to have it processed without refreshing the page in the browser, put the following lines in your controller:

public function init()
{
    $this->_helper->contextSwitch()
         ->setContext(
             ‘html’, array(
                 ‘suffix’    => ‘html’,
                 ‘headers’   => array(
                     ‘Content-Type’ => ‘text/html; Charset=UTF-8′,
                 ),
             )
         )
         ->addActionContext(‘index’, array(‘html’,'xml’, ‘json’))
         ->setAutoJsonSerialization(true)
         ->initContext();
}

This context switch routine allows me to use my data in HTML, XML and JSON format. Both XML and JSON are default output formats supported by Zend Framework, but the documentation didn’t contain a proper example on how to output HTML.

Consider you already have put a lot of time in rendering a table according to the specific form parameters, you don’t want to re-do this work to display it again to use XML or JSON formatted data. Easiest (not best) way is to have that data returned in your view without the rest of the layout spoiling your AJAX requests.

Using the context switcher defined above, we only need to add a view template using the “actionname.html.phtml” naming convention. So for indexAction it would have index.phtml for normal displaying, index.xml.phtml for XML generated output, index.json.phtml for json-formated data and index.html.phtml to have your content returned without layout.

In our requesting view template we now only have to add a container where to put that data. Since I return the full generated table, I wrap my “table” tag in a “div” block.

<div id=”tableContainer”>
<table>…</table>
</div>

In jQuery I use the following function to load data inside that container:

function loadData(link, placeholder)
{
    if (-1 === link.indexOf(‘/format/html’) && -1 === link.indexOf(‘format=html’)) {
        link += ‘/format/html’;
    }
    jQuery.get(link, function(data) {
        var container = ‘#’ + placeholder;
        jQuery(container).html(data);
    });

    return false;
}

A common link like

<a href=”/path/to/next/page”>next page</a>

becomes

<a onClick=”loadData(this.href, ‘tableContainer’);” href=”/path/to/next/page”>next page</a>

And with ZendX_JQuery you don’t even need to load all the jQuery scripts, Zend Framework does this for you.

First modify your bootstrapper so your view uses the jQuery components:

protected function _initView()
{
    // Initialize view
    $view = new Zend_View();
       
    // Adding jQuery view helpers
    $view->addHelperPath(“ZendX/JQuery/View/Helper”, “ZendX_JQuery_View_Helper”);

    // Add it to the ViewRenderer
    $viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper(
        ‘ViewRenderer’
    );
       
    $view->doctype(‘XHTML1_STRICT’);
    $view->headMeta()->appendHttpEquiv(‘Content-Type’, ‘text/html; Charset=UTF-8′);


    // Add a stylesheet for JQuery parts, only when it’s required
    $view->jQuery()->addStyleSheet(

        $view->baseUrl(‘/css/ui-lightness/jquery-ui-1.7.2.custom.css’)
    );
 

    $viewRenderer->setView($view);
    Zend_Controller_Action_HelperBroker::addHelper($viewRenderer);

    // Return it, so that it can be stored by the bootstrap
    return $view;
}

In your layout template, you can add between the head tags the following call:

<head>
    <?php echo $this->JQuery() ?>
</head>

That’s all there is to it. Now you can add nice date pickers and other awesome jQuery and jQuery UI elments to your apps.

I know it’s not the best way to AJAX-ify your apps, but it’s the easiest way to convert a non-javascripted app into a more user friendly application. A nice side-effect is your app remains working for non-javascript browsers and web spiders (like those for search engines).

If you want to learn more about ZendX_JQuery, be sure to check out a presentation given by Dennis De Cock at one of the PHPBenelux UG meetings. His session has inspired me to take a deeper look at jQuery as a javascript library and ZendX_JQuery as the tool to master it.

Author:

Testing Zend Framework Action Controllers With Mocks

In this post I’ll demonstrate a unit test technique for testing Zend Framework Action Controllers using Mock Objects. Unit testing controllers independently has a number of advantages:

  1. You can develop controllers test-first (TDD).
  2. It allows you to develop and test all of your controller code before developing any of the view scripts.
  3. It helps you quickly identify problems in the controller, rather than problems in one of the combination of Model, View and Controller.

The Action Controller I’m going to test has only one method, profileAction():

tests/application/controllers/UserController.php

class UserController extends Zend_Controller_Action
{
    public function profileAction()
    {
        $this->view->userId = $this->_getParam('user_id');
        return $this->render();
    }
}

tests/application/ControllerTestCase.php

class ControllerTestCase extends Zend_Test_PHPUnit_ControllerTestCase
{
    public $application;

    public function setUp()
    {
        $this->application = new Zend_Application(
            APPLICATION_ENV,
            APPLICATION_PATH . '/config/application.ini'
        );

        $this->bootstrap = array($this, 'bootstrap');
        parent::setUp();
    }

    public function tearDown()
    {
        Zend_Controller_Front::getInstance()->resetInstance();

        $this->resetRequest();
        $this->resetResponse();

        $this->request->setPost(array());
        $this->request->setQuery(array());
    }

    public function bootstrap()
    {
        $this->application->bootstrap();
    }
}

tests/application/controllers/UserControllerTest.php

require_once TESTS_PATH . '/application/ControllerTestCase.php';
require_once APPLICATION_PATH . '/controllers/UserController.php';

class UserControllerTest extends ControllerTestCase
{
    public function testStubRenderMethodCall()
    {
        $request = $this->getRequest()
            ->setRequestUri('/user/profile/1')
            ->setParams(array('user_id'=>1))
            ->setPathInfo(null);

        $response = $this->getResponse();

        $this->getFrontController()
            ->setRequest($request)
            ->setResponse($response)
            ->throwExceptions(true)
            ->returnResponse(false);

        $controller = $this->getMock(
            'UserController',
            array('render'),
            array($request, $response, $request->getParams())
        );
        $controller->expects($this->once())
                 ->method('render')
                 ->will($this->returnValue(true));

        $this->assertTrue($controller->profileAction());
        $this->assertTrue($controller->view->user_id == 1);
    }
}

You can go further making both the tests and the implementation more sophisticated. The main point is that you can build and test a controller in a way that doesn’t require a view script to be written to do so.

Zend Framework Known Issues

By default Zend_Test_PHPUnit_ControllerTestCase sets the redirector exit value to false, leading to unexpected behavior when unit testing your code. For that reason, make sure you always add a return statement after calling a utility method:

class UserController extends Zend_Controller_Action
{
    public function profileAction()
    {
        if (null == $this->_getParam('user_id', null) {
            return $this->_redirect('/');
        }
        return $this->render();
    }
}

If you want the Front Controller to throw exceptions, you have no other choice than to overwrite the dispatch method and pass a boolean TRUE to the throwExceptions() method:

class ControllerTestCase extends Zend_Test_PHPUnit_ControllerTestCase
{
    ...

    public function dispatch($url = null)
    {
        // redirector should not exit
        $redirector = Zend_Controller_Action_HelperBroker::getStaticHelper('redirector');
        $redirector->setExit(false);

        // json helper should not exit
        $json = Zend_Controller_Action_HelperBroker::getStaticHelper('json');
        $json->suppressExit = true;

        $request = $this->getRequest();
        if (null !== $url) {
            $request->setRequestUri($url);
        }
        $request->setPathInfo(null);

        $this->getFrontController()
             ->setRequest($request)
             ->setResponse($this->getResponse())
             ->throwExceptions(true)
             ->returnResponse(false);

        $this->getFrontController()->dispatch();
    }

    ...
}

The Dispatcher not only violates the DRY principle but also suffers from amnesia. The problem is that it doesn’t store the instance of the Action Controller, instead, it destroys it (Zend_Controller_Dispatcher_Standard Line 305). You can easily get around this issue by extending the standard dispatcher and overwriting the dispatch() method:

class Zf_Controller_Dispatcher_Standard extends Zend_Controller_Dispatcher_Standard
{
    ...

    public function dispatch($url = null)
    {
        ...
        Zend_Registry::set('Zend_Controller_Action', $controller);

        // Destroy the page controller instance and reflection objects
        $controller = null;
    }

This will allow you to access the view object after dispatching the request:

class ExampleControllerTest extends ControllerTestCase
{
    public function testDefaultActionRendersViewObject()
    {
        $this->dispatch('/');

        $controller = Zend_Registry::get('Zend_Controller_Action');

        $this->assertEquals('ExampleController', get_class($controller));
        $this->assertTrue(isset($controller->view));
    }

Links

PHPUnit: Testing Zend Framework Controllers
PHPUnit: Mock Objects

Posted in Agile Development, Frameworks, PHP

Author: Federico

by News Robot on August 27, 2010 in News, No Comments »
tags: ,

Zend Framework’s Flash Messenger action helper

I’ve talked about Zend Framework’s action helpers before, but haven’t covered any of the action helpers that are supplied with Zend Framework.

FlashMessenger is a helper that allows you to store messages between requests. The most common use I have for it is for a “saved” message after doing an edit of an item that then redirects back to a list.

This is how it’s used:

Storing a message

Storing to the FlashMessenger is easy. This is my typical usage within an action controller:


$this->_helper->flashMessenger->addMessage('Task saved');
$this->_helper->redirector('index');

This code adds the message “Task saved” to the FlashMessenger and then redirects the user the index action, which in this case is a list of tasks. As should be obvious from the name of the method, you can add multiple messages and they will all be stored for retrieval after the next redirect.

The FlashMessenger will store the message that you’ve added for one hop, or number of requests. This means that the message will be available for retrieval on the next request, but unavailable on the request afterwards. This is very useful and it means that if someone refreshes the task list by hitting F5, then the “Task saved” message does not reappear.

Retrieving the stored messages

Retrieving the stored messages is similarly simple:


$this->view->messages $this->_helper->flashMessenger->getMessages();

This will create an array of messages in your view object which you can then loop over in your view script:

<?php if (count($this->messages)) : ?>
<ul id="messages">
<?php foreach ($this->messages as $message) : ?>
    <li><?php echo $this->escape($message); ?></li>
<?php endforeach; ?>
</div>
<?php endif; ?>

and that’s all there is to the FlashMessenger.

Author: Rob…

Bradley Holt’s Blog: Front Controller Pattern in Zend Framework

After giving a presentation about Zend Framework Bradley Holt realized that there is often too much material to cover in the time available. His takeaway was to start blogging about Zend Framework. In is first post in the series about Zend Framework he talks about front controller implementation in Zend Framework.

Like many other frameworks, Zend Framework provides an implementation of the Front Controller pattern. This means that all HTTP requests can be sent through a centralized point. This allows you to take advantage of Zend Framework’s routing and Model-View-Controller (MVC) components, if you so choose.