Article

Learn symfony: a Beginner's Tutorial

Page: 1 2 3 4 5 6 7

Ajax

Almost every new web application that's launched these days has its share of cool visual effects and XMLHttpRequests. If the Internet looks more and more like a desktop application, it's not because this was impossible before. It's because the task was horribly complicated. You had to write thousands of lines of JavaScript code, and duplicate it for IE, and then spend weeks in browser debugging your code. Today, building Ajax effects is incredibly simple once you have a framework that handles it for you. With symfony, and without even writing JavaScript code, you can create an Ajax form that's compatible with all the main browsers, and with a single function call.

We'll illustrate this technique as we deal with the comments for the photo album. The schema we created early in this tutorial already has a comment table for the storage of comments, but we need to create an interface to allow users to add comments when viewing a picture. Instead of doing it the traditional way, we'll use an Ajax form in the public/photo page. Open the photoSuccess.php template again and add at its end the following code:

// in apps/frontend/modules/public/templates/photoSuccess.php      
...      
<?php use_helper('Javascript'); ?>      
<div id="comments">      
 <h2>Comments</h2>      
 <?php foreach($photo->getComments() as $comment): ?>      
   <?php include_partial('comment', array('comment' => $comment)) ?>      
 <?php endforeach; ?>      
 <div id="updateDiv">      
   <?php echo link_to_function('Add a comment', visual_effect('toggle_blind', 'addComment')) ?>      
   <?php echo form_remote_tag(array(      
     'url'    => 'public/addComment',      
     'update' => 'updateDiv',      
     'complete' => visual_effect('highlight', 'updateDiv'),      
   ), 'id=addComment style=display:none;') ?>      
     <?php echo input_hidden_tag('photo_id', $photo->getId()) ?>      
     <?php echo label_for('author', 'Your name') ?>      
     <?php echo input_tag('author') ?><br />      
     <?php echo label_for('body', 'Your comment') ?>      
     <?php echo textarea_tag('body') ?><br />      
     <?php echo submit_tag('submit') ?>      
   </form>      
 </div>      
</div>

The include_partial() helper appears again in the fifth line of this listing, this time to include a _comment.php partial. Create this file and write in the code that's necessary to display the details of a comment:

// in apps/frontend/modules/photo/templates/_comment.php      
<?php echo use_helper('Date') ?>      
<div class="comment">      
<p class="details">      
 <?php echo $comment->getAuthor() ?> said      
 <?php echo distance_of_time_in_words($comment->getPhoto()->getCreatedAt('U'), $comment->getCreatedAt('U')) ?> after      
</p>      
<p class="body"><?php echo $comment->getBody() ?></p>      
</div>

The use_helper() call that appears in both scripts includes a set of additional template functions called a helper group. The JavaScript helper group contains Ajax and visual effects helpers (like visual_effect()); the Date helper group contains date-related helpers (like distance_of_time_in_words()). The most common helpers belong to helper groups that are always loaded; for instance, link_to() belongs to the Url helper group, which is available by default. But to avoid crippling the server's memory with unnecessary functions, symfony will provide optional helpers only if their group is declared with a use_helper(). To finish with the comment partial, you just need to know that the distance_of_time_in_words() helper outputs an amount of time in words, so that the comments can be labelled "John Doe said 4 hours after," or "Foobar said 2 days after."

Back to photoSuccess.php. Let's focus on the first of the Javascript helpers used in the template: link_to_function. This shortcut triggers a JavaScript onClick event on a link. The second argument of the function is normally a JavaScript string; a good example for this function would be:

<?php echo link_to_function('click me', 'alert("you did it!");')) ?>      
// outputs in HTML      
<a href="#" onClick="alert('you did it!');return false;">click me</a>

In photoSuccess.php, the second argument is a call to visual_effect(). This is another JavaScript helper, which outputs JavaScript code to execute a visual effect based on the script.aculo.us library. In this case, clicking on the 'Add a comment' link will reveal or hide the addComment form with a cool blind-style effect.

This leads us to the heart of the Ajax aspect of this template: the form. The form_remote_tag() helper expects as the first parameter an array of Ajax options in which two options are compulsory: url is the internal URI of the action to be called in the background when the form is submitted, and update is the id of the HTML element that should be updated with the action result. As with most helpers, the second parameter is a way to set additional attributes to the generated HTML tag (in this case, a <form> tag). So the translation of this helper call into plain English reads something like: "Build a form that, when submitted, will call the public/addComment action and replace itself with the response to this request."

The following helper calls are all members of the Form helper group (and are always available), and are shortcuts to usual input tags. Their names are fairly explicit, and the HTML code they output in the response source suffices to exemplify their use:

// HTML generated by the Form helper calls in photoSuccess.php      
<input type="hidden" name="photo_id" id="photo_id" value="2" />            
<label for="author">Your name</label>            
<input type="text" name="author" id="author" value="" /><br />      
<label for="body">Your comment</label>      
<textarea name="body" id="body"></textarea><br />      
<input type="submit" name="commit" value="submit" />

An Ajax interaction contains a server script that handles the request. In this case, the form_remote_tag() calls the photo/addComment action. You already know what this action should do: create a new Comment object, populate it with the request parameters, and save it in the database. Here's how to do it with symfony:

// in apps/frontend/modules/public/actions/actions.class.php      
public function executeAddComment()      
{      
 $this->forward404unless($photo = PhotoPeer::retrieveByPk($this->getRequestParameter('photo_id')));      
 $comment = new Comment();      
 $comment->setPhoto($photo);      
 $comment->setAuthor($this->getRequestParameter('author'));      
 $comment->setBody($this->getRequestParameter('body'));      
 $comment->save();      
       
 $this->comment = $comment;      
}

And what should the template of this action show? A comment, of course, because that's just what the _comment partial does. The addCommentSuccess.php template simply shows an include_partial(), which is what most Ajax templates do:

// in apps/frontend/modules/public/templates/addCommentSuccess.php      
<?php include_partial('comment', array('comment' => $comment)) ?>

The Ajax Interaction is ready to be tested. Display a photo detail page, click on the "Add a comment" link to reveal the hidden form, fill in the form with a test comment, post it, and voila! Without a whole page refresh, the new comment replaces the form.

1566_fig17

Ajax is so easy to use with symfony that many developers include Ajax interactions throughout their web applications. But be aware that sometimes, a cool effect can be counterproductive and not usable at all, so use Ajax sparingly and wisely...

Conclusion

You can download the final code of this tutorial here. Building the few pages we put up together in this tutorial took us a few dozen lines. Imagine how much coding and testing would have been involved without a framework!

We've barely scratched the surface of the possibilities of symfony, and yet this tutorial should give you a good idea of the processes involved in web application development with symfony: it's fast, fun, and effective. Symfony implements the best design patterns and pushes you to write maintainable and clean code. To me, symfony's strengths are the quality of its code, its impressive amount of documentation, and a vibrant community. If you were uncertain about which framework to choose, it's time to jump in!

If you liked this article, share the love:
Print-Friendly Version Suggest an Article

Sponsored Links

Rate This Article

  • 1
    Poor
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
    Great

Comment on This Article

Have something to say?

Post A Comment

You need to be a member of the SitePoint Forums to comment on this post. Sign Up

Already a member? Post using your SitePoint Forums account: