Creating a Simple Comment Section using PHP and SQLite Tutorial

In this tutorial, you will learn how to create a Simple Comment Section in your Web Application Projects using PHP and SQLite Database. Comment Section is one of the most common feature in a web application such as Blog Sites. This application feature allows users to leave a comment, ask questions, or feedback for a certain post/content. This kind of web application feature also allows other users to reply-to-comment which means other users can leave a comment under a specific comment too.

Here, I will be providing a very simple comment section web application in which app users can submit, read, reply, edit, and delete comments. The demo application that we will be creating don not require users to register and log in, this only requires entering the user name for each comment and reply.

Getting Started

In this tutorial source code, I used Bootstrap v5 for the design of application and XAMPP v3.3.0. Download and Install the said library and local web machine server to run the following script on your end. After Installing the XAMPP, open the XAMPP's php.ini file (i.e "C:\xampp\php\php.ini") in a text-editor and uncomment the ;extension=sqlite3. To do that, remove the (;) [semicolon]. Lastly, open your XAMPP's Control Panel and start the Apache

Creating the Database Connection

The follwing PHP scripts is the database connection script of our project. This also contains a script that creates the database and tables if not yet existed. Save this file as Connection.php

  1. <?php
  2. if(!is_dir(__DIR__.'/db'))
  3. mkdir(__DIR__.'/db');
  4. if(!defined('db_file')) define('db_file',__DIR__.'./db/comment_db.db');
  5. function my_udf_md5($string) {
  6.     return md5($string);
  7. }
  8. Class DBConnection extends SQLite3{
  9.     protected $db;
  10.     function __construct(){
  11.         $this->open(db_file);
  12.         $this->createFunction('md5', 'my_udf_md5');
  13.         $this->exec("PRAGMA foreign_keys = ON;");
  14.  
  15.         $this->exec("CREATE TABLE IF NOT EXISTS `comments` (
  16.            `comment_id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
  17.            `sender` TEXT NOT NULL,
  18.            `comment` TEXT NOT NULL,
  19.            `date_created` TIMESTAMP DEFAULT (datetime('now','localtime'))
  20.        )");
  21.        
  22.        $this->exec("CREATE TABLE IF NOT EXISTS `replies` (
  23.            `reply_id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
  24.            `comment_id` INTEGER NOT NULL,
  25.            `sender` TEXT NOT NULL,
  26.            `reply` TEXT NOT NULL,
  27.            `date_created` TIMESTAMP DEFAULT (datetime('now','localtime')),
  28.            FOREIGN KEY (`comment_id`) References `comments`(comment_id) ON DELETE CASCADE
  29.        )");
  30.  
  31.     }
  32.     function __destruct(){
  33.          $this->close();
  34.     }
  35. }
  36.  
  37. $conn = new DBConnection();

Creating the Page Interfaces

The below source code files is a PHP files/scripts that contains the codes of each pages interfaces. In contains a combined HTML and PHP scripts. Save the following files according to the given filename above each file scripts.

index.php

This file contains the interface script of the application's Main Page. This contains also the query that list all the comments and replies submitted.

  1. <?php require_once('Connection.php') ?>
  2. <!DOCTYPE html>
  3. <html lang="en">
  4.  
  5. <head>
  6.     <meta charset="UTF-8">
  7.     <meta http-equiv="X-UA-Compatible" content="IE=edge">
  8.     <meta name="viewport" content="width=device-width, initial-scale=1.0">
  9.     <title>Simple Comment Section</title>
  10.     <link rel="stylesheet" href="./css/bootstrap.min.css">
  11.     <script src="./js/jquery-3.6.0.min.js"></script>
  12.     <script src="./js/bootstrap.min.js"></script>
  13.     <style>
  14.          :root {
  15.             --bs-success-rgb: 71, 222, 152 !important;
  16.         }
  17.        
  18.         html,
  19.         body {
  20.             height: 100%;
  21.             width: 100%;
  22.             font-family: Apple Chancery, cursive;
  23.         }
  24.     </style>
  25. </head>
  26.  
  27. <body class="bg-light">
  28.     <nav class="navbar navbar-expand-lg navbar-dark bg-primary bg-gradient" id="topNavBar">
  29.         <div class="container">
  30.             <a class="navbar-brand" href="https://sourcecodester.com">
  31.             Sourcecodester
  32.             </a>
  33.         </div>
  34.     </nav>
  35.     <div class="container py-3" id="page-container">
  36.         <h3>Simple Comment Section</h3>
  37.         <?php if(isset($_SESSION['response_msg'])): ?>
  38.             <div class="alert alert-<?php echo $_SESSION['response_type']  ?>">
  39.                 <p class="m-0"><?php echo $_SESSION['response_msg']  ?></p>
  40.             </div>
  41.         <?php
  42.             unset($_SESSION['response_msg']);
  43.             unset($_SESSION['response_type']);
  44.             endif;
  45.             if(isset($_SESSION['comment-post']))
  46.             unset($_SESSION['comment-post']);
  47.             if(isset($_SESSION['reply-post']))
  48.             unset($_SESSION['reply-post']);
  49.         ?>
  50.         <hr>
  51.         <?php
  52.         $comment_qry = $conn->query("SELECT * FROM `comments` order by strftime(date_created) asc ");
  53.         while($row = $comment_qry->fetchArray()):
  54.         ?>
  55.         <div class="card mb-2">
  56.             <div class="card-body">
  57.                 <div class="d-flex align-items-end">
  58.                     <div class="fw-bold flex-grow-1"><?php echo ucwords($row['sender']) ?></div>
  59.                     <span><small class="text-muted"><?php echo date("m d,Y h:i A",strtotime($row['date_created'])) ?></small></span>
  60.                 </div>
  61.                 <hr>
  62.                 <div class="lh-1">
  63.                     <p class=""><span class="mx-3"></span><?php echo str_replace('\n','<br/>',$row['comment']) ?></p>
  64.                 </div>
  65.                 <hr>
  66.                 <div class="w-100 d-flex justify-content-end">
  67.                     <a href="reply.php?comment_id=<?php echo $row['comment_id'] ?>" class="btn btn-primary btn-sm rounded-0 me-2">Reply</a>
  68.                     <a href="edit_comment.php?comment_id=<?php echo $row['comment_id'] ?>" class="btn btn-primary btn-sm rounded-0 me-2">Edit</a>
  69.                     <a href="delete_comment.php?comment_id=<?php echo $row['comment_id'] ?>" class="btn btn-danger btn-sm rounded-0">Delete</a>
  70.  
  71.                 </div>
  72.             </div>
  73.         </div>
  74.         <?php
  75.             $reply_qry = $conn->query("SELECT * FROM `replies` where comment_id ='{$row['comment_id']}' order by strftime(date_created) asc ");
  76.             while($rrow = $reply_qry->fetchArray()):
  77.         ?>
  78.         <div class="card mb-2" style="margin-left:15%">
  79.             <div class="card-body">
  80.                 <div class="d-flex align-items-end">
  81.                     <div class="fw-bold flex-grow-1"><?php echo ucwords($rrow['sender']) ?></div>
  82.                     <span><small class="text-muted"><?php echo date("m d,Y h:i A",strtotime($rrow['date_created'])) ?></small></span>
  83.                 </div>
  84.                 <hr>
  85.                 <div class="lh-1">
  86.                     <p class=""><span class="mx-3"></span><?php echo str_replace('\n','<br/>',$rrow['reply']) ?></p>
  87.                 </div>
  88.                 <hr>
  89.                 <div class="w-100 d-flex justify-content-end">
  90.                     <a href="reply.php?reply_id=<?php echo $rrow['reply_id'] ?>&comment_id=<?php echo $row['comment_id'] ?>" class="btn btn-primary btn-sm rounded-0 me-2">Edit</a>
  91.                     <a href="delete_reply.php?reply_id=<?php echo $rrow['reply_id'] ?>" class="btn btn-danger btn-sm rounded-0">Delete</a>
  92.                 </div>
  93.             </div>
  94.         </div>
  95.         <?php endwhile; ?>
  96.         <?php endwhile; ?>
  97.         <hr>
  98.         <form id="comment-form" action="save_comment.php" method="POST">
  99.             <div class="form-group">
  100.                 <label for="sender" class="control-label">Sender</label>
  101.                 <input type="text" name="sender" id="sender" class="form-control form-control-sm rounded-0" value="<?php echo isset($_SESSION['comment-post']) ? $_SESSION['comment-post']['sender'] : "" ?>" required/>
  102.             </div>
  103.             <div class="form-group">
  104.                 <label for="comment" class="control-label">Comment</label>
  105.                 <textarea name="comment" id="comment" cols="30" rows="2" class="form-control" style="resize:none" placeholder="Write your comment here" required><?php echo isset($_SESSION['comment-post']) ? $_SESSION['comment-post']['comment'] : "" ?></textarea>
  106.             </div>
  107.             <div class="form-group">
  108.                 <div class="col-12 mt-4">
  109.                     <div class="d-flex justify-content-end align-items-end">
  110.                             <button class="btn btn-sm btn-sm btn-primary rounded-0 me-2" type="submit">Save</button>
  111.                             <button class="btn btn-sm btn-sm btn-secondary rounded-0" type="reset">Cancel</button>
  112.                     </div>
  113.                 </div>
  114.             </div>
  115.         </form>
  116.     </div>
  117. </body>
  118.  
  119. </html>
edit_comment.php

This file contains the script for the page where the users can edit the comment.

  1. <?php require_once('Connection.php') ?>
  2. <?php
  3. if(!isset($_GET['comment_id'])){
  4.     echo "<script>alert('Comment ID is required.'); location.replace('./');</script>";
  5.     exit;
  6. }
  7. ?>
  8. <!DOCTYPE html>
  9. <html lang="en">
  10.  
  11. <head>
  12.     <meta charset="UTF-8">
  13.     <meta http-equiv="X-UA-Compatible" content="IE=edge">
  14.     <meta name="viewport" content="width=device-width, initial-scale=1.0">
  15.     <title>Simple Comment Section</title>
  16.     <link rel="stylesheet" href="./css/bootstrap.min.css">
  17.     <script src="./js/jquery-3.6.0.min.js"></script>
  18.     <script src="./js/bootstrap.min.js"></script>
  19.     <style>
  20.          :root {
  21.             --bs-success-rgb: 71, 222, 152 !important;
  22.         }
  23.        
  24.         html,
  25.         body {
  26.             height: 100%;
  27.             width: 100%;
  28.             font-family: Apple Chancery, cursive;
  29.         }
  30.     </style>
  31. </head>
  32.  
  33. <body class="bg-light">
  34.     <nav class="navbar navbar-expand-lg navbar-dark bg-primary bg-gradient" id="topNavBar">
  35.         <div class="container">
  36.             <a class="navbar-brand" href="https://sourcecodester.com">
  37.             Sourcecodester
  38.             </a>
  39.         </div>
  40.     </nav>
  41.     <div class="container py-3" id="page-container">
  42.         <h3>Update Comment</h3>
  43.         <?php if(isset($_SESSION['response_msg'])): ?>
  44.             <div class="alert alert-<?php echo $_SESSION['response_type']  ?>">
  45.                 <p class="m-0"><?php echo $_SESSION['response_msg']  ?></p>
  46.             </div>
  47.         <?php
  48.             unset($_SESSION['response_msg']);
  49.             unset($_SESSION['response_type']);
  50.             endif;
  51.         ?>
  52.         <hr>
  53.         <?php
  54.         $comment_qry = $conn->query("SELECT * FROM `comments` where comment_id = '{$_GET['comment_id']}' ");
  55.         $result = $comment_qry->fetchArray();
  56.         if(!$result){
  57.             echo "<script>alert('Unkown Comment ID.'); location.replace('./');</script>";
  58.             exit;
  59.         }else{
  60.         ?>
  61.         <form id="comment-form" action="save_comment.php" method="POST">
  62.                 <input type="hidden" name="comment_id" value="<?php echo $_GET['comment_id'] ?>" >
  63.                 <div class="form-group">
  64.                     <label for="sender" class="control-label">Sender</label>
  65.                     <input type="text" name="sender" id="sender" class="form-control form-control-sm rounded-0" value="<?php echo isset($_SESSION['comment-post']) ? $_SESSION['comment-post']['sender'] : (isset($result['sender']) ? $result['sender'] : "") ?>" required/>
  66.                 </div>
  67.                 <div class="form-group">
  68.                     <label for="comment" class="control-label">Comment</label>
  69.                     <textarea name="comment" id="comment" cols="30" rows="2" class="form-control" style="resize:none" placeholder="Write your comment here" required><?php echo isset($_SESSION['comment-post']) ? $_SESSION['comment-post']['comment'] : (isset($result['comment']) ? $result['comment'] : "") ?></textarea>
  70.                 </div>
  71.                 <div class="form-group">
  72.                     <div class="col-12 mt-4">
  73.                         <div class="d-flex justify-content-end align-items-end">
  74.                                 <button class="btn btn-sm btn-sm btn-primary rounded-0 me-2" type="submit">Save</button>
  75.                                 <a class="btn btn-sm btn-sm btn-secondary rounded-0" href="./">Cancel</a>
  76.                         </div>
  77.                     </div>
  78.                 </div>
  79.             </form>
  80.         <?php } ?>
  81.         <hr>
  82.     </div>
  83.  
  84. </body>
  85.  
  86. </html>
reply.php

This file contains the script for the page where the users can submit and edit the reply-to-comment.

  1. <?php require_once('Connection.php') ?>
  2. <?php
  3. if(!isset($_GET['comment_id'])){
  4.     echo "<script>alert('Comment ID is required.'); location.replace('./');</script>";
  5.     exit;
  6. }
  7. ?>
  8. <!DOCTYPE html>
  9. <html lang="en">
  10.  
  11. <head>
  12.     <meta charset="UTF-8">
  13.     <meta http-equiv="X-UA-Compatible" content="IE=edge">
  14.     <meta name="viewport" content="width=device-width, initial-scale=1.0">
  15.     <title>Simple Comment Section</title>
  16.     <link rel="stylesheet" href="./css/bootstrap.min.css">
  17.     <script src="./js/jquery-3.6.0.min.js"></script>
  18.     <script src="./js/bootstrap.min.js"></script>
  19.     <style>
  20.          :root {
  21.             --bs-success-rgb: 71, 222, 152 !important;
  22.         }
  23.        
  24.         html,
  25.         body {
  26.             height: 100%;
  27.             width: 100%;
  28.             font-family: Apple Chancery, cursive;
  29.         }
  30.     </style>
  31. </head>
  32.  
  33. <body class="bg-light">
  34.     <nav class="navbar navbar-expand-lg navbar-dark bg-primary bg-gradient" id="topNavBar">
  35.         <div class="container">
  36.             <a class="navbar-brand" href="https://sourcecodester.com">
  37.             Sourcecodester
  38.             </a>
  39.         </div>
  40.     </nav>
  41.     <div class="container py-3" id="page-container">
  42.         <h3>Reply to</h3>
  43.         <?php if(isset($_SESSION['response_msg'])): ?>
  44.             <div class="alert alert-<?php echo $_SESSION['response_type']  ?>">
  45.                 <p class="m-0"><?php echo $_SESSION['response_msg']  ?></p>
  46.             </div>
  47.         <?php
  48.             unset($_SESSION['response_msg']);
  49.             unset($_SESSION['response_type']);
  50.             endif;
  51.         ?>
  52.         <hr>
  53.         <?php
  54.         $comment_qry = $conn->query("SELECT * FROM `comments` where comment_id = '{$_GET['comment_id']}' ");
  55.         $result = $comment_qry->fetchArray();
  56.         if(!$result){
  57.             echo "<script>alert('Unkown Comment ID.'); location.replace('./');</script>";
  58.             exit;
  59.         }else{
  60.         ?>
  61.         <div class="card mb-2">
  62.             <div class="card-body">
  63.                 <div class="d-flex align-items-end">
  64.                     <div class="fw-bold flex-grow-1"><?php echo ucwords($result['sender']) ?></div>
  65.                     <span><small class="text-muted"><?php echo date("m d,Y h:i A",strtotime($result['date_created'])) ?></small></span>
  66.                 </div>
  67.                 <hr>
  68.                 <div class="lh-1">
  69.                     <p class=""><span class="mx-3"></span><?php echo str_replace('\n','<br/>',$result['comment']) ?></p>
  70.                 </div>
  71.             </div>
  72.         </div>
  73.         <?php } ?>
  74.         <hr>
  75.         <?php
  76.         if(isset($_GET['reply_id'])){
  77.             $reply_qry = $conn->query("SELECT * FROM `replies` where reply_id = '{$_GET['reply_id']}' ");
  78.             $result2 = $reply_qry->fetchArray();
  79.             if(!$result2){
  80.                 echo "<script>alert('Unkown Reply ID.'); location.replace('./');</script>";
  81.                 exit;
  82.             }
  83.         }
  84.         ?>
  85.         <form id="comment-form" action="save_reply.php" method="POST">
  86.             <input type="hidden" name="reply_id" value="<?php echo isset($_GET['reply_id']) ? $_GET['reply_id'] : '' ?>" >
  87.             <input type="hidden" name="comment_id" value="<?php echo $_GET['comment_id'] ?>" >
  88.             <div class="form-group">
  89.                 <label for="sender" class="control-label">Sender</label>
  90.                 <input type="text" name="sender" id="sender" class="form-control form-control-sm rounded-0" value="<?php echo isset($_SESSION['reply-post']) ? $_SESSION['reply-post']['sender'] : (isset($result2['sender']) ? $result2['sender'] : "") ?>" required/>
  91.             </div>
  92.             <div class="form-group">
  93.                 <label for="reply" class="control-label">Reply</label>
  94.                 <textarea name="reply" id="reply" cols="30" rows="2" class="form-control" style="resize:none" placeholder="Write your reply here" required><?php echo isset($_SESSION['reply-post']) ? $_SESSION['reply-post']['reply'] : (isset($result2['reply']) ? $result2['reply'] : "") ?></textarea>
  95.             </div>
  96.             <div class="form-group">
  97.                 <div class="col-12 mt-4">
  98.                     <div class="d-flex justify-content-end align-items-end">
  99.                             <button class="btn btn-sm btn-sm btn-primary rounded-0 me-2" type="submit">Save</button>
  100.                             <a class="btn btn-sm btn-sm btn-secondary rounded-0" href="./">Cancel</a>
  101.                     </div>
  102.                 </div>
  103.             </div>
  104.         </form>
  105.     </div>
  106.  
  107. </body>
  108.  
  109. </html>

Creating the PHP Action Scripts

The following PHP Scripts are the files that manages the queries on the database. Theese are the saving,inserting, and deleting scripts for comments and replies. Save the following files according to the given filename above each file scripts.

save_comment.php

The PHP Code that insert new comment or update old comment in the database.

  1. <?php
  2. require_once('Connection.php');
  3. if(isset($_SESSION['comment-post'])){
  4.     unset($_SESSION['comment-post']);
  5. }
  6. $comment_id = isset($_POST['comment_id']) ? $_POST['comment_id'] : '';
  7. $sender = $_POST['sender'];
  8. $comment = $conn->escapeString($_POST['comment']);
  9.  
  10. if(empty($comment_id))
  11. $sql = "INSERT INTO `comments` (`sender`,`comment`) VALUES ('{$sender}','{$comment}')";
  12. else
  13. $sql = "UPDATE `comments` set `sender` = '{$sender}' ,`comment` = '{$comment}' where comment_id = '$comment_id'";
  14.  
  15. $save = $conn->query($sql);
  16. if($save){
  17.     $_SESSION['response_type'] = 'success';
  18.     $_SESSION['response_msg'] = 'Comment Successfully saved.';
  19.     if(empty($comment_id))
  20.         header('location:'.$_SERVER['HTTP_REFERER']);
  21.     else
  22.         header('location:./');
  23. }else{
  24.     $_SESSION['response_type'] = 'danger';
  25.     $_SESSION['response_msg'] = 'Saving comment failed. Error: '. $conn->lastErrorMsg();
  26.     $_SESSION['comment-post'] = $_POST;
  27. header('location:'.$_SERVER['HTTP_REFERER']);
  28. }
save_reply.php

The PHP Code that insert new reply or update old reply in the database.

  1. <?php
  2. require_once('Connection.php');
  3. if(isset($_SESSION['reply-post'])){
  4.     unset($_SESSION['reply-post']);
  5. }
  6. $reply_id = isset($_POST['reply_id']) ? $_POST['reply_id'] : '';
  7. $comment_id = $_POST['comment_id'];
  8. $sender = $_POST['sender'];
  9. $reply = $conn->escapeString($_POST['reply']);
  10.  
  11. if(empty($reply_id))
  12. $sql = "INSERT INTO `replies` (`comment_id`,`sender`,`reply`) VALUES ('{$comment_id}','{$sender}','{$reply}')";
  13. else
  14. $sql = "UPDATE `replies` set `sender` = '{$sender}' ,`reply` = '{$reply}' where reply_id = '$reply_id'";
  15.  
  16. $save = $conn->query($sql);
  17. if($save){
  18.     $_SESSION['response_type'] = 'success';
  19.     $_SESSION['response_msg'] = 'Reply Successfully saved.';
  20.     header('location:./');
  21. }else{  
  22.     $_SESSION['response_type'] = 'danger';
  23.     $_SESSION['response_msg'] = 'Saving reply failed. Error: '. $conn->lastErrorMsg();
  24.     $_SESSION['reply-post'] = $_POST;
  25.     header('location:'.$_SERVER['HTTP_REFERER']);
  26. }
delete_comment.php

The PHP code for deleting comment.

  1. <?php
  2. require_once('Connection.php');
  3. $sql = "DELETE FROM `comments` where comment_id = '{$_GET['comment_id']}'";
  4. $delete = $conn->query($sql);
  5. if($delete){
  6.     $_SESSION['response_type'] = 'success';
  7.     $_SESSION['response_msg'] = 'Comment Successfully deleted.';
  8.     if(empty($_GET['comment_id']))
  9.         header('location:'.$_SERVER['HTTP_REFERER']);
  10.     else
  11.         header('location:./');
  12. }else{
  13.     $_SESSION['response_type'] = 'danger';
  14.     $_SESSION['response_msg'] = 'Deleting comment failed. Error: '. $conn->lastErrorMsg();
  15.     $_SESSION['comment-post'] = $_POST;
  16. header('location:'.$_SERVER['HTTP_REFERER']);
  17. }
  18. ?>
delete_reply.php

The PHP code for deleting reply.

  1. <?php
  2. require_once('Connection.php');
  3. $sql = "DELETE FROM `replies` where reply_id = '{$_GET['reply_id']}'";
  4. $delete = $conn->query($sql);
  5. if($delete){
  6.     $_SESSION['response_type'] = 'success';
  7.     $_SESSION['response_msg'] = 'Reply Successfully deleted.';
  8.     if(empty($_GET['reply_id']))
  9.         header('location:'.$_SERVER['HTTP_REFERER']);
  10.     else
  11.         header('location:./');
  12. }else{
  13.     $_SESSION['response_type'] = 'danger';
  14.     $_SESSION['response_msg'] = 'Deleting reply failed. Error: '. $conn->lastErrorMsg();
  15.     $_SESSION['reply-post'] = $_POST;
  16. header('location:'.$_SERVER['HTTP_REFERER']);
  17. }
  18. ?>

There you go. You can now test the source code on your end. If you have encountered any errors, kindly review your source code by differentiating it from the codes I provided above. You can also download the working source code I created for this tutorial. The download button is located below this article.

DEMO VIDEO

That's the end of this tutorial. I hope this Comment Section tutorial in PHP and SQLite will help you and useful for your PHP Projects.

Explore more on this website for more Tutorials and Free Source Codes.

Happy Coding :)

Comments

This tutorial on creating a simple comment section with PHP and SQLite is an excellent resource for beginners. The step-by-step guide and clear explanations make it easy to follow along. I appreciate the practical examples, which help in understanding the concepts better. The inclusion of code snippets is particularly useful for hands-on learning. Overall, a great tutorial for anyone looking to implement a basic comment section on their website!

Add new comment