Level: Intermediate Duane O'Brien (d@duaneobrien.com), PHP developer, Freelance
12 Feb 2008 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.
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 | Description | Name | Size | Download method |
|---|
| Part 4 sample code | os-php-fwk4.zip | 30KB | HTTP |
|---|
Resources Learn
-
Read William Graham's blog for an example of using Ajax and Zend:
Ajax
101: A Simple Example of Using Ajax with the Zend Framework.
-
For a tutorial that will show you step by step how to create an Ajax-powered symfony
application in minutes, see "Easy Ajax in symfony."
-
See the Zend Framework manual.
-
Read the DevNetwork Forums discussion on Zend and Ajax.
-
For a good tutorial series on using the Zend Framework, read "Understanding
the Zend Framework, Part 1."
-
Another good Zend tutorial is "Getting
Started with the Zend Framework."
-
Get the symfony documentation.
-
Check out the "My first symfony project"
tutorial.
-
Get the CakePHP Manual. (Note: The manual is
written to CakePHP V1.1. There may be some variance as you are using CakePHP V1.2).
-
Read the five-part series "Cook
up Web sites fast with CakePHP."
-
Read the Wikipedia entry for an overview of software
frameworks.
-
Get an overview of Model-View-Controller architecture from Wikipedia.
-
PHP.net is the central resource for PHP developers.
-
Check out the "Recommended PHP reading list."
-
Browse all the PHP content on developerWorks.
-
Expand your PHP skills by checking out IBM developerWorks' PHP project resources.
-
To listen to interesting interviews and discussions for software developers, check out developerWorks podcasts.
-
Using a database with PHP? Check out the Zend Core for
IBM, a seamless, out-of-the-box, easy-to-install PHP development and production environment that supports IBM DB2 V9.
-
Stay current with developerWorks' Technical events and webcasts.
-
Check out upcoming conferences, trade shows, webcasts, and other Events around the world that are of interest to IBM open source developers.
-
Visit the developerWorks Open source zone for extensive how-to information, tools, and project updates to help you develop with open source technologies and use them with IBM's products.
-
Watch and learn about IBM and open source technologies and product functions with the no-cost developerWorks On demand demos.
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
About the author  | |  | Duane 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. |
Rate this page
|