Skip to main content

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.

Comments

  1. So if you want cleanly-separated event bindings, don't use Zend Framework? ;-)

    ReplyDelete
  2. Thanks DragonBe for your article, I didn't know ZendX_JQuery yet and it seems to be a nice solution here. I also like your loadData() function, short and easy.

    ReplyDelete
  3. Pretty useful article - thanks! I'm working on something similar these days and I think this will help me a lot :)

    BTW jQuery offers an even shorter API to AJAX-load HTML content into a container you could do something like:

    jQuery('#' + placeholder).load(link);

    Instead of most of the code in loadData()
    - see http://api.jquery.com/load/

    ReplyDelete
  4. @PHPGangsta,


    Glad to have inspired you. Once you've build stuff yourself, blog about it and share the knowledge.

    @Shahar,

    Thanks for the jQuery load() hint, I didn't know about it but I'm sure going to use it from now on.

    ReplyDelete
  5. This comment has been removed by a blog administrator.

    ReplyDelete
  6. @Hire PHP Programmer,


    You're more then welcome to publish your services on my blog, but then we need to negotiate advertisement fees and stuff like that, for which I don't have the time for.

    If you have a true commitment to the community, post something valuable like code samples and add a link to your company at the bottom. But stay on topic.

    ReplyDelete
  7. There seems to be a simpler method

    in init() of controller put

    $AjaxContext = $this->_helper->getHelper('AjaxContext');
    $AjaxContext->addActionContext('index','html')
    )
    ->initContext();

    Then write ajax request handling code in index.ajax.phtml file

    ReplyDelete
  8. Do this code runs under all PHP 5 modules.

    ReplyDelete

Post a Comment

Popular posts from this blog

Speeding up database calls with PDO and iterators

When you review lots of code, you often wonder why things were written the way they were. Especially when making expensive calls to a database, I still see things that could and should be improved.
No framework development When working with a framework, mostly these database calls are optimized for the developer and abstract the complex logic to improve and optimize the retrieval and usage of data. But then developers need to build something without a framework and end up using the basics of PHP in a sub-optimal way.

$pdo = new \PDO( $config['db']['dsn'], $config['db']['username'], $config['db']['password'] ); $sql = 'SELECT * FROM `gen_contact` ORDER BY `contact_modified` DESC'; $stmt = $pdo->prepare($sql); $stmt->execute(); $data = $stmt->fetchAll(\PDO::FETCH_OBJ); echo 'Getting the contacts that changed the last 3 months' . PHP_EOL; foreach ($data as $row) { $dt = new \DateTime('2015-04-…

Deploy Docker containers fast to Microsoft Azure

DEPLOY DOCKER CONTAINERS FAST TO MICROSOFT AZURE It’s hard to ignore the fact thatDockeris a way to move forward for rapid application development, distributed architectures and microservices. For developersDockeroffers great advantages as they can build their containers specifically for the task they work on. They grab a base image of a container, modify it for their purpose and prepare the functionality inside the container. Quality, testing and security teams now have a single instance to look at and ensure all functional and regulatory requirements are met. System engineers now don’t have to worry about providing a system with the required specs as the container is already provisioned for that purpose. But where do you deploy yourDockercontainers? You can set up your existing bare metal infrastructure to allow them to run containers, but this also means you need to learn about securing your container infrastructure, which is not an easy task. Luckily “the cloud” offers container …

PHP Arrays - Associative Arrays or Hash Maps

Associative array or hash maps are listings of key and value pairs with a posibility to nest additional keys and values. An associative array is a very powerful construct within PHP.

In our previous article we discussed simple arrays, which in their turn are indexed associative arrays under the hood. Take the following example:

$array = [
'apple',
'banana',
'chocolate',
]; 

Is in fact an indexed associative array under the hood:

$array = [
0 => 'apple',
1 => 'banana',
2 => 'chocolate',
]; 

But associative arrays can be so much more than just an indexed array, and you will find many database operations returning arrays where the fields of a table are the keys in the array while their values are also the values within the array.

$productRowData = [
'product_id' => 1234,
'brand_id' => 321,
'product_name' => 'Our awesome product',
'prodcut_description' => 'This is our most awesome product.&#…