In my previous tutorial Nested comment system in PHP, AJAX I have shown how to create nested or threaded comment system using PHP, MySQl and AJAX. Here I am going to show you how to do the same thing but using Codeigniter 3 framework.

The final output on the browser would be similar to the following attached screen-shot

nested comment using codeigniter, ajax

Now we will look into the following steps to create such comment system.

Prerequisites

Netbeans 8.1
XAMPP in Windows
Codeigniter 3.0.6

Configure XAMPP and Netbeans

From Netbeans IDE go to Tools->Options. Click on PHP. Now on tab “General” browse the file for “PHP 5 Interpreter”. The php interpreter file generally placed inside the <physical drive in Windows OS>:xamppphpphp.exe

Configure Codeigniter and Netbeans

Create a new PHP project in Netbeans. Then remove the index.php file from the newly created project. Now copy the extracted files from Codeigniter 3.0.6 to the newly created project directory.

Directory Structure

A typical directory structure for the project would be as shown below. Here assests directory will contain static resources like css, js, images.

nested categories codeigniter

Step 1. Now modify <root directory>/application/config/autoload.php file for auto-loading html, url, file, form and session

$autoload['helper'] = array('html', 'url', 'file', 'form', 'app');

$autoload['libraries'] = array('database');

Create ci3/application/helpers/app_helper.php file with the following source code

<?php

if (!defined('BASEPATH'))
    exit('No direct script access allowed');

if (!function_exists('mysql_to_php_date')) {

    function mysql_to_php_date($mysql_date) {
        $datetime = strtotime($mysql_date);
        $format = date("F j, Y, g:i a", $datetime);
        return $format;
    }

}

Step 2. Go to location ci3/application/config/database.php file and change database parameter values for below parameters

$db['default']['username'] = 'root'; //your database username
$db['default']['password'] = ''; //your database password
$db['default']['database'] = 'roytuts'; //your MySQL database name

Step 3. Create a MySQL table in “roytuts” database

USE `roytuts`;

/*Table structure for table `blog` */

DROP TABLE IF EXISTS `blog`;

CREATE TABLE `blog` (
  `blog_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `blog_title` text NOT NULL,
  `blog_slug` text NOT NULL,
  `short_content` varchar(255) NOT NULL,
  `long_content` longtext NOT NULL,
  `related_tags` text,
  `meta_author` varchar(45) DEFAULT NULL,
  `meta_keywords` text,
  `meta_description` text,
  `is_approved` tinyint(1) NOT NULL DEFAULT '0',
  `is_deleted` tinyint(1) NOT NULL DEFAULT '0',
  `is_comment_enabled` tinyint(1) NOT NULL DEFAULT '1',
  `no_of_comments` smallint(4) DEFAULT '50',
  `image_path` text,
  `thumb_path` text,
  `source_code_path` text,
  `created_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `created_by` varchar(45) NOT NULL,
  `last_updated` datetime DEFAULT NULL,
  `updated_by` varchar(45) DEFAULT NULL,
  PRIMARY KEY (`blog_id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
USE `roytuts`;

/*Table structure for table `blog_comment` */

DROP TABLE IF EXISTS `blog_comment`;

CREATE TABLE `blog_comment` (
  `comment_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `comment_text` text NOT NULL,
  `parent_id` int(10) unsigned NOT NULL,
  `ip_address` varchar(20) NOT NULL,
  `email_address` varchar(100) NOT NULL,
  `web_address` varchar(255) DEFAULT NULL,
  `created_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `created_by` varchar(45) NOT NULL,
  `blog_blog_id` int(10) unsigned NOT NULL,
  PRIMARY KEY (`comment_id`,`blog_blog_id`),
  KEY `fk_blog_comment_blog1_idx` (`blog_blog_id`),
  CONSTRAINT `fk_blog_comment_blog1` FOREIGN KEY (`blog_blog_id`) REFERENCES `blog` (`blog_id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8;

Step 4. Dump some data into database table – blog and blog_comment

insert  into `blog`(`blog_id`,`blog_title`,`blog_slug`,`short_content`,`long_content`,`related_tags`,`meta_author`,`meta_keywords`,`meta_description`,`is_approved`,`is_deleted`,`is_comment_enabled`,`no_of_comments`,`image_path`,`thumb_path`,`source_code_path`,`created_date`,`created_by`,`last_updated`,`updated_by`) values 
(2,'test blog','test-blog','jkllklk,mm,,m','The topic of blogging seems to come up a lot in our social media training workshops. The benefits of a quality blog are obvious – fresh content is good for your readers and your rankings. Blogs are easy to set up and even easier to update. We often tell people that if they can use Microsoft Word… they can update a blog.rnrn                        As easy as they are to set up, they can be difficult to maintain. A good blog is filled with relevant, timely content that is updated on a regular basis. New bloggers often start out with a bang but then fizzle out when they realize that creating content can be challenging.','fdfddfkjfdkfd,dfjhfdjhfdjhjdfdjhgfd?nmmnmnnm$fdfdfd$gfkhgfjkghkjg','www.roytuts.com','fidhfdjkhfdkjhd$dffdmfd','hjjhm, ,m,m,',1,0,1,100,'','','','2015-05-14 18:55:58','-1',NULL,'-1'),
(3,'another blog','another-blog','content','content','tags$tag2','www.roytuts.com','key1$key2','description',1,0,1,50,'C:/xampp/htdocs/blog/siteimages/blog/6361666246e392f2ba337100957487e9.jpg','siteimages/blog/thumb/6361666246e392f2ba337100957487e9.jpg','','2015-10-28 05:30:45','-1',NULL,NULL);
insert  into `blog_comment`(`comment_id`,`comment_text`,`parent_id`,`ip_address`,`email_address`,`web_address`,`created_date`,`created_by`,`blog_blog_id`) values 
(1,'Test comment',0,'::1','abc@test.com','','2013-10-05 07:07:31','Anonymous',3),
(2,'test reply',1,'::1','test@localhost.com','http://www.sblog.in','2013-10-05 07:13:06','Rahul',3),
(3,'test reply 2',1,'::1','test@localhost.com','','2013-10-05 07:18:11','Naru',3),
(4,'test reply 3',2,'::1','test@localhost.com','','2013-10-05 07:20:33','Shibu',3),
(5,'Test comment',0,'::1','test@localhost.com','','2013-10-05 10:17:20','Debabrata',2),
(6,'reply',5,'::1','abc@test.com','','2013-10-05 10:19:28','Liton',2),
(7,'erre',5,'::1','abc@test.com','','2013-10-05 10:20:57','Souvik',2),
(8,'trt',6,'::1','test@localhost.com','','2013-10-05 10:21:39','Debina',2),
(9,'ghgh',8,'::1','test@localhost.com','','2013-10-05 10:24:55','Suman',2),
(10,'Test Comment',0,'127.0.0.1','contact@roytuts.com','https://roytuts.com','0000-00-00 00:00:00','Roy',2),
(11,'Test Reply',10,'127.0.0.1','contact@roytuts.com','https://roytuts.com','0000-00-00 00:00:00','Roy',2),
(12,'Hello',0,'127.0.0.1','contact@roytuts.com','https://roytuts.com','2016-08-03 05:07:14','Roy',2),
(13,'Hi',12,'127.0.0.1','contact@roytuts.com','https://roytuts.com','2016-08-03 05:07:32','Roy',2);

Step 5. Create a model file blogmodel.php under ci3/application/models directory with the below source code

<?php

if (!defined('BASEPATH'))
    exit('No direct script access allowed');

/**
 * Description of blogmodel
 *
 * @author https://roytuts.com
 */
class BlogModel extends CI_Model {

    private $blog = 'blog';
    private $blog_comment = 'blog_comment';

    function __construct() {
        
    }

    //get blog details
    function get_blog_details($blog_slug) {
        $query = $this->db->get_where($this->blog, array('blog_slug=>' . $this->db->escape($blog_slug)));
        return $query->row();
    }

    //get blog comments for blog slug
    function get_blog_comments($blog_slug) {
        $query = $this->db->query('SELECT bc.comment_id, bc.blog_blog_id, bc.parent_id, bc.comment_text, 
                    bc.created_date, bc.created_by, bc.email_address, bc.web_address
                    FROM ' . $this->blog_comment . ' bc, ' . $this->blog . ' b
                    WHERE bc.blog_blog_id=b.blog_id AND 
                        b.blog_slug=' . $this->db->escape($blog_slug) .
                ' ORDER BY bc.created_date DESC');
        if ($query->num_rows() > 0) {
            $items = array();
            foreach ($query->result() as $row) {
                $items[] = $row;
            }
            //return $items;
            $comments = $this->format_comments($items);
            return $comments;
        }
        return '<ul class="comment"></ul>';
    }

    //add blog comment
    function add_blog_comment($data) {
        $this->db->insert($this->blog_comment, $data);
        $inserted_id = $this->db->insert_id();
        if ($inserted_id > 0) {
            $query = $this->db->query('SELECT bc.comment_id, bc.blog_blog_id, 
                    bc.parent_id, bc.comment_text, bc.created_date, bc.created_by,
                    bc.email_address, bc.web_address
                    FROM ' . $this->blog_comment . ' bc
                    WHERE bc.comment_id=' . $inserted_id);
            return $query->result();
        }
        return NULL;
    }

    //format comments for display on blog and article
    private function format_comments($comments) {
        $html = array();
        $root_id = 0;
        foreach ($comments as $comment)
            $children[$comment->parent_id][] = $comment;

        // loop will be false if the root has no children (i.e., an empty comment!)
        $loop = !empty($children[$root_id]);

        // initializing $parent as the root
        $parent = $root_id;
        $parent_stack = array();

        // HTML wrapper for the menu (open)
        $html[] = '<ul class="comment">';

        while ($loop && ( ( $option = each($children[$parent]) ) || ( $parent > $root_id ) )) {
            if ($option === false) {
                $parent = array_pop($parent_stack);

                // HTML for comment item containing childrens (close)
                $html[] = str_repeat("t", ( count($parent_stack) + 1 ) * 2) . '</ul>';
                $html[] = str_repeat("t", ( count($parent_stack) + 1 ) * 2 - 1) . '</li>';
            } elseif (!empty($children[$option['value']->comment_id])) {
                $tab = str_repeat("t", ( count($parent_stack) + 1 ) * 2 - 1);

                $name = $option['value']->created_by;

                // HTML for comment item containing childrens (open)
                $html[] = sprintf(
                        '%1$s<li id="li_comment_%2$s">' .
                        '%1$s%1$s<div><span class="commenter">%3$s</span>&nbsp;<span class="comment_date">%5$s</span></div>' .
                        '%1$s%1$s<div style="margin-top:4px;">%4$s</div>' .
                        '%1$s%1$s<a href="#" class="reply_button" id="%2$s">reply</a>', $tab, // %1$s = tabulation
                        $option['value']->comment_id, //%2$s id
                        $name, // %3$s = commenter 
                        $option['value']->comment_text, // %4$s = comment
                        mysql_to_php_date($option['value']->created_date) // %5$s = comment created_date
                );
                //$check_status = "";
                $html[] = $tab . "t" . '<ul class="comment">';

                array_push($parent_stack, $option['value']->parent_id);
                $parent = $option['value']->comment_id;
            } else {
                $name = $option['value']->created_by;

                // HTML for comment item with no children (aka "leaf") 
                $html[] = sprintf(
                        '%1$s<li id="li_comment_%2$s">' .
                        '%1$s%1$s<div><span class="commenter">%3$s</span>&nbsp;<span class="comment_date">%5$s</span></div>' .
                        '%1$s%1$s<div style="margin-top:4px;">%4$s</div>' .
                        '%1$s%1$s<a href="#" class="reply_button" id="%2$s">reply</a>' .
                        '%1$s</li>', str_repeat("t", ( count($parent_stack) + 1 ) * 2 - 1), // %1$s = tabulation
                        $option['value']->comment_id, //%2$s id
                        $name, // %3$s = commenter 
                        $option['value']->comment_text, // %4$s = comment
                        mysql_to_php_date($option['value']->created_date) // %5$s = comment created_date
                );
            }
        }

        // HTML wrapper for the comment (close)
        $html[] = '</ul>';
        return implode("rn", $html);
    }

}

/* End of file blogmodel.php */
/* Location: ./application/models/blogmodel.php */

Step 6. Create a controller file blogcontroller.php under ci3/application/controllers with the following source code

<?php

if (!defined('BASEPATH'))
    exit('No direct script access allowed');

/**
 * Description of blogcontroller
 *
 * @author https://roytuts.com
 */
class BlogController extends CI_Controller {

    function __construct() {
        parent::__construct();
        $this->load->model('categorymodel', 'cat');
    }

    function blog_details() {
        $data['blog_details'] = $this->blog->get_blog_details('test-blog'); //blog slug should not be hardcoded
        $data['blog_comments'] = $this->blog->get_blog_comments('test-blog'); //blog slug should not be hardcoded
        $this->load->view('blog_details', $data);
    }

    function add_blog_comment() {
        if (isset($_POST) && isset($_POST['comment_text'])) {
            $name = $_POST['comment_name'];
            $blog_id = $_POST['content_id'];
            $parent_id = $_POST['reply_id'];
            $comment_text = $_POST['comment_text'];
            $email = $_POST['comment_email'];
            $web = $_POST['comment_web'];
            $data = array(
                'comment_text' => $comment_text,
                'parent_id' => $parent_id,
                'ip_address' => $this->input->ip_address(),
                'email_address' => $email,
                'web_address' => $web,
                'created_date' => date('Y-m-d h:i:sa'),
                'created_by' => $name,
                'blog_blog_id' => $blog_id
            );
            $resp = $this->blog->add_blog_comment($data);
            if ($resp != NULL) {
                foreach ($resp as $row) {
                    $date = mysql_to_php_date($row->created_date);
                    echo "<li id="li_comment_{$row->comment_id}">" .
                    "<div><span class="commenter">{$row->created_by}</span>&nbsp;<span class="comment_date">{$date}</span></div>" .
                    "<div style="margin-top:4px;">{$row->comment_text}</div>" .
                    "<a href="#" class="reply_button" id="{$row->comment_id}">reply</a>" .
                    "</li>";
                }
            } else {
                echo 'Error in adding comment';
            }
        } else {
            echo 'Error: Please enter your comment';
        }
    }

}

/* End of file category.php */
/* Location: ./application/controllers/category.php */

Step 7. Create a view file blog_details.php under ci3/application/views

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8"/>
        <title>Nested Comment using Codeigniter, MySQL, AJAX</title>
        <!--[if IE]> <script> (function() { var html5 = ("abbr,article,aside,audio,canvas,datalist,details," + "figure,footer,header,hgroup,mark,menu,meter,nav,output," + "progress,section,time,video").split(','); for (var i = 0; i < html5.length; i++) { document.createElement(html5[i]); } try { document.execCommand('BackgroundImageCache', false, true); } catch(e) {} })(); </script> <![endif]-->
        <link type="text/css" rel="stylesheet" href="<?php echo base_url(); ?>assets/css/comments.css"/>
        <script type= 'text/javascript' src="<?php echo base_url(); ?>assets/js/jquery-1.9.1.min.js"></script>
        <script type= 'text/javascript' src="<?php echo base_url(); ?>assets/js/jquery-ui-1.10.3-custom.min.js"></script>
        <script type= 'text/javascript' src="<?php echo base_url(); ?>assets/js/jquery-migrate-1.2.1.js"></script>
        <script type= 'text/javascript' src="<?php echo base_url(); ?>assets/js/jquery.blockUI.js"></script>
        <script type= 'text/javascript' src="<?php echo base_url(); ?>assets/js/comments_blog.js"></script>
    </head>
    <body>
        <div class='singlepost'>
            <div class='fullpost clearfix'>
                <div class='entry'>
                    <h1 class='post-title'>
                        <?php echo $blog_details->blog_title; ?>
                    </h1>
                    <p>
                        Posted By:&nbsp;<?php echo $blog_details->meta_author; ?>,&nbsp;&nbsp;
                        Posted On:&nbsp;<?php echo $blog_details->created_date; ?>
                    </p>
                    <div>&nbsp;</div>
                    <div class="entry">
                        <p><?php echo $blog_details->long_content; ?></p>
                    </div>
                    <div>&nbsp;</div>
                    <div style="width: 600px;">
                        <div id="comment_wrapper">
                            <div id="comment_form_wrapper">
                                <div id="comment_resp"></div>
                                <h4>Please Leave a Reply<a href="javascript:void(0);" id="cancel-comment-reply-link">Cancel Reply</a></h4>
                                <form id="comment_form" name="comment_form" action="" method="post">
                                    <div>
                                        Name<input type="text" name="comment_name" id="comment_name" size="54"/>
                                    </div>
                                    <div>
                                        Email<input type="text" name="comment_email" id="comment_email" size="54"/>
                                    </div>
                                    <div>
                                        Website<input type="text" name="comment_web" id="comment_web" size="54"/>
                                    </div>
                                    <div>
                                        Comment<textarea name="comment_text" id="comment_text" rows="6"></textarea>
                                    </div>
                                    <div>
                                        <input type="hidden" name="content_id" id="content_id" value="<?php echo $blog_details->blog_id; ?>"/>
                                        <input type="hidden" name="reply_id" id="reply_id" value=""/>
                                        <input type="hidden" name="depth_level" id="depth_level" value=""/>
                                        <input type="submit" name="comment_submit" id="comment_submit" value="Post Comment" class="button"/>
                                    </div>
                                </form>
                            </div>
                            <?php
                            echo $blog_comments;
                            ?>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</body>
</html>

Step 8. Modify file ci3/application/config/routes.php file

$route['default_controller'] = 'blogcontroller';

Step 9. Create blog_comments.js file under ci3/assets/js directory with below content

$(function () {
    $("#cancel-comment-reply-link").hide();
    $(".reply_button").live('click', function (event) {
        event.preventDefault();
        var id = $(this).attr("id");
        if ($("#li_comment_" + id).find('ul').size() > 0) {
            $("#li_comment_" + id + " ul:first").prepend($("#comment_form_wrapper"));
        } else {
            $("#li_comment_" + id).append($("#comment_form_wrapper"));
        }
        $("#reply_id").attr("value", id);
        $("#cancel-comment-reply-link").show();
    });

    $("#cancel-comment-reply-link").bind("click", function (event) {
        event.preventDefault();
        $("#reply_id").attr("value", "");
        $("#comment_wrapper").prepend($("#comment_form_wrapper"));
        $(this).hide();
    });

    $("#comment_form").bind("submit", function (event) {
        event.preventDefault();
        if ($("#comment_text").val() == "")
        {
            alert("Please enter your comment");
            return false;
        }
        $.ajax({
            type: "POST",
            url: "http://localhost/ci3/index.php/blogcontroller/add_blog_comment",
            data: $('#comment_form').serialize(),
            dataType: "html",
            beforeSend: function () {
                $('#comment_wrapper').block({
                    message: 'Please wait....',
                    css: {
                        border: 'none',
                        padding: '15px',
                        backgroundColor: '#ccc',
                        '-webkit-border-radius': '10px',
                        '-moz-border-radius': '10px'
                    },
                    overlayCSS: {
                        backgroundColor: '#ffe'
                    }
                });
            },
            success: function (comment) {
                var reply_id = $("#reply_id").val();
                if (reply_id == "") {
                    $("#comment_wrapper ul:first").prepend(comment);
                    if (comment.toLowerCase().indexOf("error") >= 0) {
                        $("#comment_resp_err").attr("value", comment);
                    }
                }
                else {
                    if ($("#li_comment_" + reply_id).find('ul').size() > 0) {
                        $("#li_comment_" + reply_id + " ul:first").prepend(comment);
                    }
                    else {
                        $("#li_comment_" + reply_id).append('<ul class="comment">' + comment + '</ul>');
                    }
                }
                $("#comment_text").attr("value", "");
                $("#reply_id").attr("value", "");
                $("#cancel-comment-reply-link").hide();
                $("#comment_wrapper").prepend($("#comment_form_wrapper"));
                $('#comment_wrapper').unblock();
            }
        });
    });
});

Step 10. If everything is fine then run the application. You will get the output in the browser.

Download assets assets

That’s all. Thanks for reading.

I am a professional Web developer, Enterprise Application developer, Software Engineer and Blogger. Connect me on JEE Tutorials Twitter Facebook  Google Plus Linkedin Or Email Me

Leave a Reply

Your email address will not be published. Required fields are marked *