CakePHP Ajax Comments

Recently, in a post regaurding site improvements, there was a comment request from Lecterror for the ajax comments I used in my blog system.

lecterror Said:

Hi there, I'd love to hear more about the AJAX-based comment system, looks really good.

Let me start by saying that using AJAX is not a beginners subject to tackle. This is not something to start with if you have just started writing your first CakePHP app yesterday. With that said, on to the tutorial.

First let's start with our Comments Controller.

<?php
class CommentsController extends AppController {
    var $components = array('RequestHandler');
    var $helpers = array('Ajax');
    var $name = 'Comments';

    function add() {
        if($this->RequestHandler->isAjax())
        {
            $this->Comment->recursive =-1;
            $commentInfos = $this->Comment->findAllByIp($_SERVER['REMOTE_ADDR']);
            $spam = FALSE;
            foreach($commentInfos as $commentInfo)
            {
                if ( time() - strtotime($commentInfo['Comment']['created']) < 180)
                {
                    $spam = TRUE;
                }
            }
           
            if ($spam === FALSE)
            {
           
                if (!empty($this->data)) {
                    $this->data['Comment']['ip'] = $_SERVER['REMOTE_ADDR'];
                    $this->Comment->create();
                    if ($this->Comment->save($this->data)) {
                        $this->Comment->Post->recursive = -1;
                        $postData = $this->Comment->Post->findById($this->data['Comment']['post_id'], array('comment_count', 'id'));
                        $postData['Post']['comment_count'] =$postData['Post']['comment_count'] + 1;
                        $this->Comment->Post->save($postData);
                        $this->Comment->recursive =-1;
                        $comments = $this->Comment->findAllByPostId($this->data['Comment']['post_id']);
                        $this->set(compact('comments'));
                        $this->viewPath = 'elements'.DS.'posts';
                        $this->render('comments');            
                    }
                }
            }
            else
            {
                        $this->Comment->recursive =-1;
                        $comments = $this->Comment->findAll(array('Comment.post_id' => $this->data['Comment']['post_id']));
                        $this->set(compact('comments'));
                        $this->viewPath = 'elements'.DS.'posts';
                        $this->render('spam');            
            }
        }
        else
        {
            $this->Session->setFlash(__('Invalid Action. Please view a post to add a comment.', true));
            $this->redirect(array('controller' => 'pages', 'action'=>'display', 'home'));
        }
    }
}
?>

 

In the add() function, we setup the action to require it being called via Ajax. If not then we display a nice message, and redirect to the home page.

The next section is my solution for spam protection. It simply detects the IP Address of the poster, and determines whether it has been 3 minutes since last post or not. The spam response is handled by app/views/elements/posts/spam.ctp and the !spam response app/views/elements/posts/comments.ctp

Next we look at the /app/views/posts/view.ctp file. This is where the Ajax call is made from.

<div class="post-footer" id="PostsComments">
<?php
    echo '<h3><a name="comments">Comments</a></h3>';
    if (! empty( $post['Comment'] ))
    {
        echo '';
        foreach ( $post['Comment'] as $comment )
        {
            echo '<div class="comments">';
            echo '<span class="date">On ' . $time->nice( $comment['created'] ) . '</span>' . " <h1>" . $comment['name'] . ' Said:</h1>';
            echo $comment['content'];
            echo '</div>';
        }
    }
?>
</div>
<div class="comments form">
<?php echo $ajax->form('/comments/add', 'post', array('url' => '/comments/add', 'update' => 'PostsComments', 'indicator' => 'commentSaved'));?>
    <fieldset>
        <legend><?php __('Add Comment');?></legend>
        <div id="commentSaved" style="display: none; float: right;">
            <?php echo $html->image('ajax-loader.gif'); ?>
        </div>
    <?php
        echo $form->hidden('Comment.post_id', array('value' => $post['Post']['id']));
        echo $form->input('Comment.name');
        echo $form->input('Comment.email');
        echo $form->input('Comment.web', array('value' => 'http://'));
        echo $form->input('Comment.content');
//
    ?>
    </fieldset>
   
<?php echo $form->end('Submit');?>
</div>

 

 

The top div PostsComments is what is refreshed from the ajax call. This div displays the files /app/views/elements/posts/comments.ctp and /app/views/elements/posts/spam.ctp as shown below.

<?php
if (! empty( $comments ))
{
    echo '<h3>Comments</h3>';
    foreach ( $comments as $comment )
    {
        echo '<div class="comments">';
        echo '<span class="date">On ' . $time->nice( $comment['Comment']['created'] ) . '</span>' . " <h1>" . $comment['Comment']['name'] . ' Said:</h1>';
        echo $comment['Comment']['content'];
        echo '</div>';
    }
}
echo '<h2 class="saved">Your Comment Has Been Saved</h2>';
?>
 

And then the spam.ctp file

<?php
    if (! empty( $comments ))
    {
        echo '<h3>Comments</h3>';
        foreach ( $comments as $comment )
        {
            echo '<div class="comments">';
            echo '<span class="date">On ' . $time->nice( $comment['Comment']['created'] ) . '</span>' . " <h1>" . $comment['Comment']['name'] . ' Said:</h1>';
            echo $comment['Comment']['content'];
            echo '</div>';
        }
    }
    ?>
    <h2 class="spam">You Must Wait 3 Minutes Between Each Comment</h2>
 

That's it. Any questions, just ask

 

 

Add Comment