How to add comments with AJAX in CakePHP

Published on Mar 9, 2009 by Jamie Munro

In today's article we are going to create a very basic blog that allows people to create a post and posts comments on that post via AJAX. We are going to keep it extremely basic and just focus on that actual AJAX functionality.

Ready? Let's begin. We are going to start by creating two database tables: posts and posts_comments. Below is a sample create statement for the posts table:



CREATE TABLE `posts` (
 `id` int(10) unsigned NOT NULL auto_increment,
 `title` varchar(255) NOT NULL,
 `summary` text NOT NULL,
 `body` text NOT NULL,
 `created` datetime default NULL,
 `modified` datetime default NULL,
 PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;


And now for the posts_comments table:

CREATE TABLE `posts_comments` (
 `id` int(10) unsigned NOT NULL auto_increment,
 `post_id` int(10) unsigned NOT NULL,
 `name` varchar(100) NOT NULL,
 `comment` text NOT NULL,
 `created` datetime default NULL,
 `modified` datetime default NULL,
 PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;


To save myself at least three to ten hours of coding, I have used the bakery to bake my two models, two controllers, and eight view pages. I won't go into detail of using the bakery here as I recently covered it in detail during "How to create a CMS in CakePHP".

Once you're done with the bakery, you should have the following files created for you:

  • app/models/post.php

  • app/models/posts_comment.php

  • app/controllers/posts.php

  • app/controllers/posts_comments.php

  • app/views/posts/add.ctp

  • app/views/posts/edit.ctp

  • app/views/posts/index.ctp

  • app/views/posts/view.ctp

  • app/views/posts_comments/add.ctp

  • app/views/posts_comments/edit.ctp

  • app/views/posts_comments/index.ctp

  • app/views/posts_comments/view.ctp


Got all of those files? Excellent, let's move on. The first thing we are going to want to do is update our posts_comments controller. We want to update all of our functions to expect a post_id to be passed into it. We also want to update our files to be prepared to perform our AJAX functions. Below is our updated app/controllers/posts_comments.php:

<?php
class PostsCommentsController extends AppController {

var $name = 'PostsComments';
var $helpers = array('Html', 'Form', 'Ajax');
var $components = array('RequestHandler');

function index($post_id = null) {
if (!$post_id) {
$this->Session->setFlash(__('Invalid PostsComment', true));
$this->redirect(array('controller' => 'posts', 'action'=>'index'));
}
$this->set('post_id', $post_id);
$this->PostsComment->recursive = 0;
$this->set('postsComments', $this->paginate('PostsComment', array('post_id' => intval($post_id))));
}

function add($post_id = null) {
if (!$post_id && empty($this->data)) {
$this->Session->setFlash(__('Invalid PostsComment', true));
}
if (!empty($this->data)) {
$this->PostsComment->create();
if ($this->PostsComment->save($this->data)) {
$this->Session->setFlash(__('The PostsComment has been saved', true));
} else {
$this->Session->setFlash(__('The PostsComment could not be saved. Please, try again.', true));
}
} else {
$this->data['PostsComment']['post_id'] = $post_id;
}
}

function delete($id = null, $post_id = null) {
if (!$id || !$post_id) {
$this->Session->setFlash(__('Invalid id for PostsComment', true));
}
if ($this->PostsComment->del($id)) {
$this->Session->setFlash(__('PostsComment deleted', true));
}
}

}


The key things that we've done in here is:

  • Updated all functions to accept $post_id

  • Updated the functions to check that $post_id is passed in, if not display an error message

  • Update our paginate function in the index to add the condition of checking our post_id

  • In our add, update our $this->data to set the post_id automatically

  • None of our functions (except the index) do any redirects, they simply set our flash message

  • Added the RequestHandler component (allow AJAX pagination)

  • Added the Ajax helper


Next up, we've updated our app/views/posts_comments/index.ctp:

<?php
echo $javascript->link('prototype');
echo $javascript->link('scriptaculous');

echo $javascript->codeBlock('function updateCommentList(post_id) {
new Ajax.Updater("content","/CMS/posts_comments/index/1", {asynchronous:true, evalScripts:true, requestHeaders:["X-Update", "content"]});
}');
?>
<div id="comments">
<h2><?php __('PostsComments');?></h2>
<p>
<?php
$paginator->options(array('update' => 'content', 'indicator' => 'spinner'));
echo $paginator->counter(array(
'format' => __('Page %page% of %pages%, showing %current% records out of %count% total, starting on record %start%, ending on %end%', true)
));
?></p>
<?php
foreach ($postsComments as $postsComment):
?>
<?php echo $postsComment['PostsComment']['name']; ?>
<?php echo $postsComment['PostsComment']['comment']; ?>
<?php echo $postsComment['PostsComment']['created']; ?>
<?php echo $ajax->link(__('Delete', true), array('action'=>'delete', $postsComment['PostsComment']['id'],

$postsComment['PostsComment']['post_id']), array('complete' => 'updateCommentList(' . $post_id . ')'), sprintf(__('Are you sure you want to delete # %s?',

true), $postsComment['PostsComment']['id'])); ?>
<?php endforeach; ?>
<div id="newcomment"></div>
<div class="paging">
<?php echo $paginator->prev('<< '.__('previous', true), array(), null, array('class'=>'disabled'));?>
 | <?php echo $paginator->numbers();?>
<?php echo $paginator->next(__('next', true).' >>', array(), null, array('class'=>'disabled'));?>
</div>
<div class="actions">
<ul>
<li><?php echo $ajax->link(__('New PostsComment', true), 'add/' . $post_id, array('update' => 'newcomment')); ?></li>
</ul>
</div>
</div>


The first thing we did is stripped out our table listing and have just left straight echos of our content and delete link. I'll let you stylize it and make it look "pretty". At the top of our file, we have included two Javascript files. Download Prototype (http://www.prototypejs.org/download) and save it in app/webroot/js/prototype.js and download scriptaculous http://script.aculo.us/downloads and save it in app/webroot/js/*.

We've only made three other important modifications:

  • Added $paginator->options() making our pagination done through AJAX

  • Updated our $html->link() for deleting to $ajax->link(), we've done the same for our New PostsComment link as well

  • For our delete link, we've told AJAX to call our Javascript function "updateCommentList()" when it's done and we've told our new comment link to display the form in the <div> "newcomment"


Finally our add.ctp:

<div class="postsComments form">
<?php echo $form->create('PostsComment');?>
<fieldset>
<legend><?php __('Add PostsComment');?></legend>
<?php
echo $form->input('post_id', array('type' => 'hidden'));
echo $form->input('name');
echo $form->input('comment');
?>
</fieldset>
<?php
echo $ajax->submit('Add', array('url' => 'add', 'complete' => 'updateCommentList(' . $this->data['PostsComment']['post_id'] . ')'));
echo $form->end();
?>
</div>


We've done two things here:

  • Updated our "post_id" to be hidden because we set it automatically for the user

  • We've changed our form->end to not be a submit button and added an ajax->submit. Like the delete link, when we are done we tell it to call our "updateCommentList" Javascript function.


To test the following functionality, start by creating a new post. With a new post created, we will assume the id is 1, go to <url>/posts_comments/index/1. This will display a list of comments for this post. Clicking the New PostsComment will AJAX our form on the page. Enter in some data and click submit. Your data will be submitted via AJAX and when it's done our comment will automatically be displayed at the end of the list. Clicking delete will remove the comment and redraw the list through AJAX.

That's all for today's article, I hope you enjoyed it. If you have any suggestions, please let me know by adding a comment below.

Tags: AJAX | CakePHP | php | JavaScript | jQuery

Related Posts

blog comments powered by Disqus