PHP frameworks, Part 4: Ajax support

How Zend Framework, CakePHP, and symfony handle native code and third-party libraries

A common criticism of early versions on PHP was that they did not support Model-View-Controller (MVC)-style architectures. Today, developers can chose from many PHP frameworks. This "PHP frameworks" series takes a look at three widely used PHP frameworks — Zend, symfony, and CakePHP — examining their similarities and differences while building and extending a sample application in each of the three frameworks. Part 1 lays out the scope for the series and gets the prerequisites out of the way. In Part 2, you build the sample application in each of the three frameworks. In Part 3, you extend the application and look at exceptions to the rule. Here, take a look at how Ajax is supported in each of the frameworks.

Duane O'Brien, PHP developer, Freelance

Photograph of Duane O'BrienDuane O'Brien has been a technological Swiss Army knife since the Oregon Trail was text only. His favorite color is sushi. He has never been to the moon.



12 February 2008

Also available in Russian Japanese Vietnamese

About this series

This series is designed for PHP developers who want to start using a framework, but have not examined the available frameworks in detail. This series examines why the three frameworks under examination were chosen, how to install each, and you'll get a good handle on the test application that you're going to extend in each framework. It might sound like a lot, but don't worry — we break it down into manageable chunks.

Part 1 of this series lays out the scope for the series, introduces the frameworks being examined, covers their installation, and scopes out the first test application you will build. (Phew!)

Part 2 walks you through building the sample application in each of the three frameworks, highlighting their similarities and differences.

Part 3 starts with extending the test application, then deals with exceptions to the rule. All frameworks work well when doing the tasks for which they were designed. Needing to do something the framework wasn't built to do happens on every project. Part 3 looks at such instances.

Part 4 focuses primarily on Ajax support. The use of Ajax, using native code and third-party libraries, is examined — specifically, how each framework behaves and accepts specific popular libraries.

Part 5 deals with working outside the frameworks. A single task is identified (nightly update script), and the process for accomplishing this task is examined in each framework.


About this article

This article examines how each framework supports Ajax. We extend Blahg to add rudimentary post ranking using each framework's Ajax support, so we get some practical experience.

You should have already gone through Part 1, Part 2, and Part 3, which cover installation, prerequisites, the initial application build and extension for each framework. If you haven't, you should do so now.


Ajax in the Zend Framework

The Zend Framework doesn't include any kind of integrated Ajax support at this time. There are a number of libraries in the Zend Framework that can be of use in adding Ajax to your application, such as Zend_Json and Zend_XmlRpc. There are also some libraries included in the Zend Framework that help take advantage of existing Web APIs for popular applications, like Flickr. But if you want to add some Ajax to your application in Zend, you need to do it yourself.

The bad news is that you need to write more code to get to where you want to be. The good news is, by not integrating with a particular Ajax-related library, you can pick your favorite library (or none at all) and build up the Ajax functionality in any way you see fit.


Ajax in symfony

The symfony framework ships with the Prototype and script.aculo.us libraries. In Part 2, when we initialized the project and copied the contents of the Web directory into /column/protected/symfony, we moved a copy of these libraries into a place where they can be used in an application. (You should confirm this; it should be in the /column/htdocs/symfony/sf/prototype/js/ directory.)

By providing integration with Prototype, symfony can provide some helpers to help reduce the amount of code needed to write to get some Ajax into Blahg. But there is a flip side to this: If you want to use something besides Prototype, you'll need to do it all yourself.


Ajax in CakePHP

CakePHP provides integration for the Prototype and script.aculo.us libraries. We need to download any libraries we want to use (put them in /column/htdocs/cakephp/js). We really only need the Prototype library in order to use the Ajax helpers. The script.aculo.us library is for making things look pretty.

As with symfony, by providing integration with Prototype, CakePHP has made life a little easier when it comes to building up the Ajax functionality of Blahg. But the flip side is still the same: If you want to use something besides Prototype, you'll need to do it all yourself. The Ajax helpers won't help.

Note: If your preferred library is JQuery, use CakePHP's JavaScript helpers to help. This can lead to some pretty slick code if done correctly, but it's out of scope for this article.


Setting up the database

Start by creating a table to hold the ranks. Use the same SQL to create this table in each of the three databases.

Listing 1. Creating a table to hold the ranks
CREATE TABLE 'ranks' ( 
'id' INT( 10 ) NOT NULL AUTO_INCREMENT PRIMARY KEY ,
'post_id' INT( 10 ) NOT NULL ,
'rank' INT( 10 ) NOT NULL 
) ENGINE = MYISAM ;

Note: We don't need a whole table to hold just the ranks for a post. We could just do rank as another column in the posts table, if we change how the modified column was updated. We don't want the post data modified every time someone ranks a post, and as Blahg was designed, either MySQL or the framework will be doing just that. Besides, if we want to do more with ranks, like keep count of how many are submitted or when a post was last ranked, it starts to make sense to put them in their own table.


Adding post ranks to Blahg in Zend

We need to decide what to use to handle Ajax requests. We can probably roll our own solution without much difficulty, but for the sake of this article, each framework will use the Prototype library (see Resources). We need to create a directory to hold the library — one that can be accessed through the browser, such as /column/htdocs/zend/scripts/ (this is what is used in the code archive).

We need to make a basic ranks model called Ranks.php. It should be just a model shell, like we did for posts and comments. And of course, we need to make a ranks controller. We will not do any views for this — the controller is just going to echo out the results of any rank requests. We need two actions: readAction will look up the rank for a post, and writeAction will take in a rank submission and check to see if there is a row in the table for that post. If there is, the rank will be updated with a new value; otherwise, a new rank row will be inserted. Either way, the new rank will be output, so we can update the rank on the view. All of that is fairly straightforward and can be found in the code archive. Now we need to get some Ajax into the views.

Remember that we won't add any rank-specific views here. But we will need to modify the posts read view to include the Ajax code. Open the /column/protected/zend/views/scripts/post/read.php file. This is where we will do all of our work. For starters, we need to include the Prototype library in the head: <script type="text/javascript" src="/zend/scripts/prototype.js"></script>.

Before we write any JavaScript, put the other pieces we need in place. We need something to show the current rank. In the rank, we want a span with a set ID so we can update the rank value on request. Perhaps something like this, below the post: <h4>Rank: <span id="rank"></span></h4>.

Next, we need a pair of buttons (links would work, too) that the user can click to indicate that he either likes or dislikes a post. Give them clever and original names. You should put these below the rank: <input type='button' onclick='rankUp();' value='Hot' /> or <input type='button' onclick='rankDown()' value='Not' />.

To make the hidden post_id input field more easily available to Prototype, make sure the field has the ID post_id.

And finally, add an onLoad event to the body to get the current rank: <body onload='fetchRank();'>.

Not surprisingly, we need to write three JavaScript functions: rankUp, rankDown, and fetchRank. These will perform the actual Ajax requests. They are fairly simple and are going to look pretty much the same in the other frameworks. Put these functions in a <script> stanza in the header.

Listing 2. Three JavaScript functions
        function fetchRank() {
                var ajax = new Ajax.Request(
                        '/zend/rank/read',
                        {
                                method : 'get',
                                parameters : {'post_id' : $('post_id').value},
                                onComplete: parseRank
                        }
                );
        }
        function rankUp() {
                var ajax = new Ajax.Request(
                        '/zend/rank/write',
                        {
                            method : 'get',
                            parameters : {'post_id' : $('post_id').value, 'rank' : '1'},
                            onComplete: parseRank
                        }
                );
        }
        function rankDown() {
                var ajax = new Ajax.Request(
                        '/zend/rank/write',
                        {
                           method : 'get',
                           parameters : {'post_id' : $('post_id').value, 'rank' : '-1'},
                           onComplete: parseRank
                        }
                );
        }

You'll have noticed that each function calls parseRank on completion. That function just takes whatever the response from the Ajax request was and puts it into the rank span you created earlier.

Listing 3. Calling the parseRank function
        function parseRank(trans) {
                $('rank').innerHTML = trans.responseText;
        }

Once we make all these changes or import them from the code archive, we should be able to read a post in Blahg and rank the post. You can even cheat and rank the post many times over. So that's how we could do this in Zend. But how would we do the same thing in symfony?

Note: It would have probably made more sense to do that with Ajax.Updater rather than Ajax.Request. Try your hand at refactoring that if you're new to Ajax.


Adding post ranks to Blahg in symfony

To add post ranks in symfony, start in our schema.yml (it should be in /column/protected/sf_column/config) and define the ranks table.

Listing 4. Adding post ranks in symfony
  ranks :
    _attributes: {phpName: Rank }
    id:
    post_id:
    rank:    integer

Remember: Two-space indent to ranks and four-space indent to the field definitions.

What's next? Building the propel model and clearing the cache. Remember the symfony commands from the /column/protected/sf_column directory (your app's root).

Listing 5. Building the propel model and clearing the cache
php /column/src/symfony/data/bin/symfony propel-build-model

php /column/src/symfony/data/bin/symfony clear-cache

Once we run these commands, we will see the files Rank.php and RankPeer.php files in the /column/protected/sf_column/lib/model directory. Now go ahead and init the ranks module: php /column/src/symfony/data/bin/symfony init-module blahg rank.

The rank actions class (/column/protected/sf_column/apps/blahg/modules/rank/actions/actions.class.php) will contain two actions: executeRead and executeWrite. Once the rank is read or updated, we echo out the rank, calling exit at the end of each action (this keeps symfony from looking for associated view templates we don't need to build.) See how these actions were written in the code archive.

Finally, we need to update the post readSuccess template (/column/protected/sf_column/apps/blahg/modules/post/templates/readSuccess.php) to use the symfony JavaScript helper and include some Ajax links. Start by adding this line to the top of the file: <?php use_helper('Javascript') ?>. That line will include the Prototype library and give us access to a wide range of Ajax helpers. We can use one to create a call to load the initial rank and a couple links that will submit the ranks. Don't forget to add a span to display the current rank.

Listing 6. Creating a call to load the initial rank
<h4>Rank: <span id="rank"></span></h4>
<?php echo javascript_tag(remote_function(array('update' => 'rank', 
                                   'url' => 'rank/read?post_id=' . $id))) ?>
<?php echo link_to_remote('Hot', array('update' => 'rank', 
                    'url' => 'rank/write?post_id=' . $id . '&rank=1')) ?> or
<?php echo link_to_remote('Not', array('update' => 'rank', 
                       'url' => 'rank/write?post_id=' . $id . '&rank=-1')) ?>

That's all we need do. Or, to put it another way, we don't have to actually write any JavaScript. The symfony framework's helpers have done that for us. Don't believe me? Set everything up (or install the code from the code archive) and read a post in the symfony version of Blahg. Then view source. Do you remember writing any of that JavaScript?


Adding post ranks to CakePHP

Before you begin adding post ranking to CakePHP's version of Blahg, make sure you've downloaded Prototype into the /column/htdocs/cakephp/js directory. You'll need to modify that default layout template to include the Prototype library in the header. That's the default.ctp file in the /column/protected/cakephp/app/views/layouts/ directory. Add the following line in the header: <?php echo $javascript->link('prototype') ?>.

Before you can use it, you'll need to create a basic AppController that includes the JavaScript and Ajax helpers you will need down the line. Create the file app_controller.php in the /column/protected/cakephp/app directory. It should look just like Listing 7.

Listing 7. Creating the app_controller.php file
<?php

class AppController extends Controller {
        var $helpers = array('Html', 'Form', 'Javascript', 'Ajax');
}
?>

Note: Technically, this isn't true. We could just use those helpers in the posts and ranks controllers, but creating the basic app controller lets us use them in any controller without having to add them to the list of helpers. This is the correct way to overload the base AppController object.

Now we need to create a basic ranks model and ranks controller. These will look more or less like the models and controllers we've already built. When we create the rank model, we should set up the model associations between the rank and post models. It's not really necessary to add the model association to ranks unless we decide later to add more robust ranks functionality. In the code archive, both associations are set up as a matter of form.

In our ranks controller, we want to have a couple new things to help keep us sane. We want to include use the component RequestHandler and add the beforeFilter function. In the beforeFilter function, we check the RequestHandler and, if the request is via Ajax, turn off the debug code. Other than that, the controller looks like you probably expect by now. Two methods, read and write, look for a post_id and output a rank.

Note: The controller code in the code archive simply returns the rank, but if we were doing more complicated Ajax calls, we could create a template (such as views/ranks/viewrank.ctp) and render the view using $this->render('viewrank', 'ajax');. But since all we need is the current rank, that's overkill.

Now all we need to do is add the rank span and a couple Ajax links to the posts read view, and we are all set.

Listing 8. Adding the rank span
<h4>Rank: <span id="rank"><?php echo (int) $post['Rank']['rank'] ?></span></h4>
<?php echo $ajax->link('Hot', '/ranks/write/' . $post['Post']['id'] . '/1', array
('update' => 'rank')); ?> or
<?php echo $ajax->link('Not', '/ranks/write/' . $post['Post']['id'] . '/-1', array
('update' => 'rank')); ?>

What those Ajax links basically say is "call that URL via Ajax and update the DOM element rank with whatever comes back." (Good thing we turned off the debug messages.) Once we have everything in place, go ahead and load up a post and try it out. View source and think about all the JavaScript we didn't write. Is it not nifty?


Summary

You should have a basic understanding of how each framework provides (or doesn't provide) support for Ajax. Try your hand at working with more complicated Ajax bits, but this should get you well on your way.


Download

DescriptionNameSize
Part 4 sample codeos-php-fwk4.zip30KB

Resources

Learn

Get products and technologies

  • Prototype is a JavaScript framework that eases development of dynamic Web applications.
  • Download PHP V5.2.3.
  • Innovate your next open source development project with IBM trial software, available for download or on DVD.
  • Download IBM product evaluation versions, and get your hands on application development tools and middleware products from DB2®, Lotus®, Rational®, Tivoli®, and WebSphere®.

Discuss

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into Open source on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Open source
ArticleID=287093
ArticleTitle=PHP frameworks, Part 4: Ajax support
publish-date=02122008