In this tutorial, we'll be creating a review system with PHP, MySQL, and AJAX (JavaScript). With the review system, your users can share their own opinion and experiences that relate to your services, products, etc.

We'll be able to implement the review system to any webpage, even a static HTML document. How is this possible? We're going to leverage AJAX to fetch results from our PHP server, which will be determined by the webpage the user is currently on.

You'll be able to implement this review system into your website hassle-free, as long as you have PHP and MySQL installed.

The Advanced package includes additional features and a download link to the source code.

1. Getting Started

If you want to install the review system on your local environment, you must install a webserver solution. Follow the below instructions.

  • Install a webserver solution package such as XAMPP.
  • If you have your own server (VPS, Dedicated, etc.), you'll need to install PHP, Apache, MySQL, and phpMyAdmin (Note: these are already included with XAMPP).
  • Install a code editor. You can use Notepad, but I don't recommend it. Install one of the following instead: Notepad++, Visual Studio Code, or Atom.

2. Creating the Database and setting-up Tables

Now we need to create the MySQL database and create the reviews table, which we can do with phpMyAdmin.

Navigate to phpMyAdmin (e.g. http://localhost/phpmyadmin/) in your browser and follow the below instructions:

  • Click the Databases tab at the top
  • Under Create database, type in phpreviews in the text box
  • Select utf8_general_ci as the collation (UTF-8 is the default encoding in HTML5)
  • Click Create

While the database is selected, click the SQL tab and execute the following SQL statement:

SQL
CREATE TABLE IF NOT EXISTS `reviews` (
	`id` int(11) NOT NULL AUTO_INCREMENT,
	`page_id` int(11) NOT NULL,
	`name` varchar(255) NOT NULL,
	`content` text NOT NULL,
	`rating` tinyint(1) NOT NULL,
	`submit_date` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
	PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8;

INSERT INTO `reviews` (`id`, `page_id`, `name`, `content`, `rating`, `submit_date`) VALUES
(1, 1, 'David Deacon', 'I use this website daily, the amount of content is brilliant.', 5, '2020-01-09 20:43:02'),
(2, 1, 'John Doe', 'Great website, great content, and great support!', 4, '2020-01-09 21:00:41'),
(3, 1, 'Robert Billings', 'Website needs more content, good website but content is lacking.', 3, '2020-01-09 21:10:16'),
(4, 1, 'Daniel Callaghan', 'Great!', 5, '2020-01-09 23:51:05'),
(5, 1, 'Bobby', 'Not much content.', 2, '2020-01-14 21:54:24'),
(6, 1, 'Joshua Kennedy', 'Fantasic website, has everything I need to know.', 5, '2020-01-16 17:34:27'),
(7, 1, 'Johannes Hansen', 'Really like this website, helps me out a lot!', 5, '2020-01-16 17:35:12'),
(8, 1, 'Wit Kwiatkowski', 'Please provide more quality content.', 5, '2020-01-16 17:36:03'),
(9, 1, 'Óli Þórðarson', 'Thanks', 5, '2020-01-16 17:36:34'),
(10, 1, 'Jaroslava Beránková', '', 5, '2020-01-16 17:37:48'),
(11, 1, 'Naomi Holt', 'Appreciate the amount of content you guys do.', 5, '2020-01-16 17:39:17'),
(12, 1, 'Isobel Whitehead', 'Thank you for providing a website that helps us out a lot!', 5, '2020-01-16 17:40:28'),
(13, 1, 'Warren Mills', 'Everything is awesome!', 5, '2020-01-16 19:34:08'),
(14, 1, 'Larry Johnson', 'Brilliant, thank you for providing quality content!', 5, '2020-01-29 18:40:36');

The data that we insert into the reviews table will be for testing purposes.

The above SQL statement will create the reviews table with the following columns:

  • id — The unique review ID.
  • page_id — This will determine which review is for which page. It will be the page ID that you can specify on any webpage.
  • name — The name of the user (e.g. Joe Bloggs).
  • content — The review content that the user will submit via the form.
  • rating — The review rating that will be from 1 to 5.
  • submit_date — The date the review was posted.

On phpMyAdmin this should look like:

http://localhost/phpmyadmin/
MySQL Reviews Table

3. Creating the Stylesheets (CSS3)

We'll be creating two stylesheets for our review system; one will be for our home page, which will be used as an example of how we'll implement the review system, and the other stylesheet will be used for the review system.

Stylesheets are used to format the layout of our review system.

Create the style.css file and add:

CSS
* {
  box-sizing: border-box;
  font-family: system-ui, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
  font-size: 16px;
}
body {
  background-color: #FFFFFF;
  margin: 0;
}
.navtop {
  background-color: #3f69a8;
  height: 60px;
  width: 100%;
  border: 0;
}
.navtop div {
  display: flex;
  margin: 0 auto;
  max-width: 1000px;
  padding: 0 10px;
  width: 100%;
  height: 100%;
}
.navtop div h1, .navtop div a {
  display: inline-flex;
  align-items: center;
}
.navtop div h1 {
  flex: 1;
  font-size: 24px;
  padding: 0;
  margin: 0;
  color: #ecf0f6;
  font-weight: normal;
}
.navtop div a {
  padding: 0 20px;
  text-decoration: none;
  color: #c5d2e5;
  font-weight: bold;
}
.navtop div a i {
  padding: 2px 8px 0 0;
}
.navtop div a:hover {
  color: #ecf0f6;
}
.content {
  max-width: 1000px;
  width: 100%;
  margin: 0 auto;
  padding: 0 10px;
}
.content h2 {
  margin: 0;
  padding: 25px 0;
  font-size: 22px;
  border-bottom: 1px solid #ebebeb;
  color: #666666;
}

Create the reviews.css file and add:

CSS
.reviews .overall_rating .num {
    font-size: 30px;
    font-weight: bold;
    color: #F5A624;
}
.reviews .overall_rating .stars {
    letter-spacing: 3px;
    font-size: 32px;
    color: #F5A624;
    padding: 0 5px 0 10px;
}
.reviews .overall_rating .total {
    color: #777777;
    font-size: 14px;
}
.reviews .write_review_btn, .reviews .write_review button {
    display: inline-block;
    background-color: #565656;
    color: #fff;
    text-decoration: none;
    margin: 10px 0 0 0;
    padding: 5px 10px;
    border-radius: 5px;
    font-size: 14px;
    font-weight: 600;
    border: 0;
}
.reviews .write_review_btn:hover, .reviews .write_review button:hover {
    background-color: #636363;
}
.reviews .write_review {
    display: none;
    padding: 20px 0 10px 0;
}
.reviews .write_review textarea {
    width: 100%;
    padding: 10px;
    border: 1px solid #ddd;
    border-radius: 5px;
    height: 150px;
    margin-top: 10px;
}
.reviews .write_review input {
    display: block;
    width: 250px;
    padding: 10px;
    border: 1px solid #ddd;
    border-radius: 5px;
    margin-top: 10px;
}
.reviews .write_review button {
    cursor: pointer;
}
.reviews .review {
    padding: 20px 0;
    border-bottom: 1px solid #eee;
}
.reviews .review .name {
    padding: 0 0 3px 0;
    margin: 0;
    font-size: 18px;
    color: #555555;
}
.reviews .review .rating {
    letter-spacing: 2px;
    font-size: 22px;
    color: #F5A624;
}
.reviews .review .date {
    color: #777777;
    font-size: 14px;
}
.reviews .review .content {
    padding: 5px 0;
}
.reviews .review:last-child {
    border-bottom: 0;
}

4. Creating the Review System with PHP

We can now start coding our review system with PHP. The script we're going to implement will retrieve results from our database and populate the output in HTML format.

Create the reviews.php file and add:

PHP
<?php
// Update the details below with your MySQL details
$DATABASE_HOST = 'localhost';
$DATABASE_USER = 'root';
$DATABASE_PASS = '';
$DATABASE_NAME = 'phpreviews';
try {
    $pdo = new PDO('mysql:host=' . $DATABASE_HOST . ';dbname=' . $DATABASE_NAME . ';charset=utf8', $DATABASE_USER, $DATABASE_PASS);
} catch (PDOException $exception) {
    // If there is an error with the connection, stop the script and display the error.
    exit('Failed to connect to database!');
}

The code above will connect to our MySQL database (phpreviews). The database variables must reflect your MySQL credentials, or else you will encounter an error.

Add after:

PHP
// Below function will convert datetime to time elapsed string.
function time_elapsed_string($datetime, $full = false) {
    $now = new DateTime;
    $ago = new DateTime($datetime);
    $diff = $now->diff($ago);
    $w = floor($diff->d / 7);
    $diff->d -= $w * 7;
    $string = ['y' => 'year','m' => 'month','w' => 'week','d' => 'day','h' => 'hour','i' => 'minute','s' => 'second'];
    foreach ($string as $k => &$v) {
        if ($k == 'w' && $w) {
            $v = $w . ' week' . ($w > 1 ? 's' : '');
        } else if (isset($diff->$k) && $diff->$k) {
            $v = $diff->$k . ' ' . $v . ($diff->$k > 1 ? 's' : '');
        } else {
            unset($string[$k]);
        }
    }
    if (!$full) $string = array_slice($string, 0, 1);
    return $string ? implode(', ', $string) . ' ago' : 'just now';
}

The above function will be used to format the review date. For example, the date "2020-01-09 20:43:02" will be converted to "3 weeks ago". It will help us identify how long ago the reviews were posted.

Add after:

PHP
// Page ID needs to exist, this is used to determine which reviews are for which page.
if (isset($_GET['page_id'])) {
    if (isset($_POST['name'], $_POST['rating'], $_POST['content'])) {
        // Insert a new review (user submitted form)
        $stmt = $pdo->prepare('INSERT INTO reviews (page_id, name, content, rating, submit_date) VALUES (?,?,?,?,NOW())');
        $stmt->execute([$_GET['page_id'], $_POST['name'], $_POST['content'], $_POST['rating']]);
        exit('Your review has been submitted!');
    }
    // Get all reviews by the Page ID ordered by the submit date
    $stmt = $pdo->prepare('SELECT * FROM reviews WHERE page_id = ? ORDER BY submit_date DESC');
    $stmt->execute([$_GET['page_id']]);
    $reviews = $stmt->fetchAll(PDO::FETCH_ASSOC);
    // Get the overall rating and total amount of reviews
    $stmt = $pdo->prepare('SELECT AVG(rating) AS overall_rating, COUNT(*) AS total_reviews FROM reviews WHERE page_id = ?');
    $stmt->execute([$_GET['page_id']]);
    $reviews_info = $stmt->fetch(PDO::FETCH_ASSOC);
} else {
    exit('Please provide the page ID.');
}
?>

With the above code, we use a GET request that will retrieve the page ID (e.g., reviews.php?page_id=1), and with that page ID, we can retrieve all the results from our database ordered by the review submit date (newest first).

In addition to retrieving the reviews from the database, we also need to calculate the average rating that is based on all the reviews retrieved. To do that, we can execute a separate query that will calculate the average rating using the MySQL AVG() function and retrieve the total number of reviews using the MySQL COUNT() function.

Also, take note prepared statements will prevent SQL injection, and therefore you don't need to be concerned about SQL injection or security issues.

After this line:

if (isset($_GET['page_id'])) {

Add:

PHP
if (isset($_POST['name'], $_POST['rating'], $_POST['content'])) {
    // Insert a new review (user submitted form)
    $stmt = $pdo->prepare('INSERT INTO reviews (page_id, name, content, rating, submit_date) VALUES (?,?,?,?,NOW())');
    $stmt->execute([$_GET['page_id'], $_POST['name'], $_POST['content'], $_POST['rating']]);
    exit('Your review has been submitted!');
}

What this will do is insert a new review into our reviews table in our database, but only if the user has submitted the "write review" form, as this form will contain fields for name, rating, and content, which can then subsequently be retrieved with the $_POST variable.

At the very end of the file, add (just after the PHP closing tag):

PHP
<div class="overall_rating">
    <span class="num"><?=number_format($reviews_info['overall_rating'], 1)?></span>
    <span class="stars"><?=str_repeat('&#9733;', round($reviews_info['overall_rating']))?></span>
    <span class="total"><?=$reviews_info['total_reviews']?> reviews</span>
</div>
<a href="#" class="write_review_btn">Write Review</a>
<div class="write_review">
    <form>
        <input name="name" type="text" placeholder="Your Name" required>
        <input name="rating" type="number" min="1" max="5" placeholder="Rating (1-5)" required>
        <textarea name="content" placeholder="Write your review here..." required></textarea>
        <button type="submit">Submit Review</button>
    </form>
</div>
<?php foreach ($reviews as $review): ?>
<div class="review">
    <h3 class="name"><?=htmlspecialchars($review['name'], ENT_QUOTES)?></h3>
    <div>
        <span class="rating"><?=str_repeat('&#9733;', $review['rating'])?></span>
        <span class="date"><?=time_elapsed_string($review['submit_date'])?></span>
    </div>
    <p class="content"><?=htmlspecialchars($review['content'], ENT_QUOTES)?></p>
</div>
<?php endforeach ?>

The above code is the template for our review system, which will iterate the reviews array and populate them accordingly. The form to write the review and the overall rating also appear in the code.

The average rating and the total number of reviews will be displayed at the top, ranging from 1 to 5 stars.

Did You Know? The htmlspecialchars() function will convert special characters to HTML entities and therefore prevent XSS injection.

That's everything you must do on the back end. Next, we'll be using AJAX to retrieve the results and populate them on our webpage.

5. Implementing the Review System into our Webpage with AJAX

Now that we have created the back-end code, we can proceed to create the front-end code with AJAX (JavaScript).

We can implement the reviews to any webpage as long as the page ID is specified. The template used below is just an example of how you can implement the review system.

Create the index.html file and add:

HTML
<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<meta name="viewport" content="width=device-width,minimum-scale=1">
		<title>Reviews System</title>
		<link href="style.css" rel="stylesheet" type="text/css">
		<link href="reviews.css" rel="stylesheet" type="text/css">
	</head>
	<body>
	    <nav class="navtop">
	    	<div>
	    		<h1>Reviews System</h1>
	    	</div>
	    </nav>
		<div class="content home">
			<h2>Reviews</h2>
			<p>Check out the below reviews for our website.</p>
		</div>
	</body>
</html>

The above code is the template we'll be using to test out the review system

After this line:

<p>Check out the below reviews for our website.</p>

Add:

HTML
<div class="reviews"></div>
<script>
const reviews_page_id = 1;
fetch("reviews.php?page_id=" + reviews_page_id).then(response => response.text()).then(data => {
	document.querySelector(".reviews").innerHTML = data;
	document.querySelector(".reviews .write_review_btn").onclick = event => {
		event.preventDefault();
		document.querySelector(".reviews .write_review").style.display = 'block';
		document.querySelector(".reviews .write_review input[name='name']").focus();
	};
	document.querySelector(".reviews .write_review form").onsubmit = event => {
		event.preventDefault();
		fetch("reviews.php?page_id=" + reviews_page_id, {
			method: 'POST',
			body: new FormData(document.querySelector(".reviews .write_review form"))
		}).then(response => response.text()).then(data => {
			document.querySelector(".reviews .write_review").innerHTML = data;
		});
	};
});
</script>

And now, if we navigate to http://localhost/phpreviews/index.html in our browser, it should appear as the following:

http://localhost/phpreviews/index.html
PHP and AJAX Review System

If we want to change the page ID, we can update the reviews_page_id variable. You can implement the above code on any webpage. The only code you need to update is the reviews_page_id variable; every page should have a unique page ID.

You can put the AJAX code in a separate JavaScript file if you prefer, just don't forget to specify the reviews_page_id variable.

That's everything you need to do to create a review system with PHP, MySQL, and AJAX.

Conclusion

Congratulations! You've successfully created a review system with PHP, MySQL, and AJAX! If you're struggling to comprehend some of the code, I'd suggest you go back and read the article again. In addition, all the PHP methods are available in the PHP manual.

If you've enjoyed this article, consider sharing it with the social links below and/or drop a comment.

Enjoy coding!