Simple Star Review Rating In PHP MySQL (Free Download)

Welcome to a quick tutorial on how to create a simple star rating system with PHP and MySQL. So you want to open a blog post, article, or maybe even products for users to rate? Well, let us walk through a simple and lightweight example in this guide – Read on!

 

 

TABLE OF CONTENTS

 

DOWNLOAD & NOTES

Here is the download link to the example code, so you don’t have to copy-paste everything.

 

EXAMPLE CODE DOWNLOAD

Source code on GitHub Gist

Just click on “download zip” or do a git clone. I have released it under the MIT license, so feel free to build on top of it or use it in your own project.

 

SORRY FOR THE ADS...

But someone has to pay the bills, and sponsors are paying for it. I insist on not turning Code Boxx into a "paid scripts" business, and I don't "block people with Adblock". Every little bit of support helps.

Buy Me A Coffee Code Boxx eBooks

 

PHP STAR RATING SYSTEM

All right, let us get into the details of building a simple PHP MYSQL star rating system.

 

 

STEP 1) STAR RATING DATABASE TABLE

1-database.sql
CREATE TABLE `star_rating` (
  `product_id` bigint(20) NOT NULL,
  `user_id` bigint(20) NOT NULL,
  `rating` smallint(6) NOT NULL DEFAULT '1'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

ALTER TABLE `star_rating`
  ADD PRIMARY KEY (`product_id`,`user_id`);
Field Description
product_id Partial primary key and foreign key. The product that is being rated, or you can change this to an article ID… Or whatever that you are rating.
user_id Partial primary key and foreign key. The user ID of the rating.
rating The number of stars given.

Yes, 3 fields are all we need. Feel free to add more fields as your project requires.

 

 

STEP 2) PHP STARS RATING LIBRARY

2-lib.php
<?php
class Stars {
  // (A) CONSTRUCTOR - CONNECT TO DATABASE
  private $pdo;
  private $stmt;
  public $error;
  function __construct () {
    $this->pdo = new PDO(
      "mysql:host=".DB_HOST.";dbname=".DB_NAME.";charset=".DB_CHARSET,
      DB_USER, DB_PASSWORD, [
      PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
      PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_NAMED
    ]);
  }

  // (B) DESTRUCTOR - CLOSE DATABASE CONNECTION
  function __destruct () {
    if ($this->stmt !== null) { $this->stmt = null; }
    if ($this->pdo !== null) { $this->pdo = null; }
  }
 
  // (C) HELPER - RUN SQL QUERY
  function query ($sql, $data=null) : void {
    $this->stmt = $this->pdo->prepare($sql);
    $this->stmt->execute($data);
  }
 
  // (D) SAVE/UPDATE USER STAR RATING
  function save ($pid, $uid, $stars) {
    $this->query(
      "REPLACE INTO `star_rating` (`product_id`, `user_id`, `rating`) VALUES (?,?,?)",
      [$pid, $uid, $stars]
    );
    return true;
  }

  // (E) GET USER STAR RATINGS
  function get ($uid) {
    $this->query("SELECT * FROM `star_rating` WHERE `user_id`=?", [$uid]);
    $star = [];
    while ($r = $this->stmt->fetch()) { $star[$r["product_id"]] = $r["rating"]; }
    return $star;
  }
 
  // (F) GET AVERAGE STAR RATING
  function avg () {
    $this->query(
      "SELECT `product_id`, ROUND(AVG(`rating`), 2) `avg`, COUNT(`user_id`) `num`
      FROM `star_rating` GROUP BY `product_id`"
    );
    $average = [];
    while ($r = $this->stmt->fetch()) {
      $average[$r["product_id"]] = [
        "avg" => $r["avg"], // average rating
        "num" => $r["num"] // number of reviews
      ];
    }
    return $average;
  }
}

// (G) DATABASE SETTINGS - CHANGE TO YOUR OWN !
define("DB_HOST", "localhost");
define("DB_NAME", "test");
define("DB_CHARSET", "utf8mb4");
define("DB_USER", "root");
define("DB_PASSWORD", "");

// (H) NEW STARS OBJECT
$_STARS = new Stars();

This one looks complicated at first, but keep calm and look carefully.

  • (A, B, H) The constructor automatically connects to the database when new Stars() is created, the destructor closes the connection.
  • (C) query() A helper function to run an SQL query.
  • (D To F) There are only 3 functions here!
    • save() Adds/updates a star rating entry
    • get() Fetches all the ratings set by the specified user
    • avg() Gets the average ratings for all products.
  • (G) Self-explanatory, remember to change the database settings to your own.

 

 

STEP 3) PRODUCTS PAGE

3A) THE HTML

3a-page.php
<?php
// (A) CORE LIBRARY + USER ID
require "2-lib.php";
$uid = 901; // fixed for demo, use your own session user id
 
// (B) UPDATE STAR RATINGS
if (isset($_POST["pid"]) && isset($_POST["stars"])) {
  echo $_STARS->save($_POST["pid"], $uid, $_POST["stars"])
    ? "<div class='note'>Rating Saved</div>" : "<div class='note'>Error</div>" ;
}
?>

<div id="demo"><?php
// (C) GET RATINGS
$average = $_STARS->avg(); // average ratings
$ratings = $_STARS->get($uid); // ratings by current user
 
// (D) OUTPUT DUMMY PRODUCTS
$products = [1 => "Hamburger", 2 => "Humborgar"];
foreach ($products as $pid=>$pdt) { ?>
<div class="pCell">
  <img class="pImg" src="burger.png">
  <div class="pName"><?=$pdt?></div>
  <div class="pReview">Customer Review</div>
  <div class="pStar" data-pid="<?=$pid?>"><?php
    $rate = isset($ratings[$pid]) ? $ratings[$pid] : 0 ;
    for ($i=1; $i<=5; $i++) {
      $css = $i<=$rate ? "star" : "star blank" ;
      echo "<div class='$css' data-i='$i'></div>";
    }
  ?></div>
  <div class="pStat">
    <?=$average[$pid]["avg"]?> out of 5 stars.
  </div>
  <div class="pStat">
    <?=$average[$pid]["num"]?> customer ratings.
  </div>
</div>
<?php }
?></div>
 
<!-- (E) HIDDEN FORM TO UPDATE STAR RATING -->
<form id="ninForm" method="post" target="_self">
  <input id="ninPdt" type="hidden" name="pid">
  <input id="ninStar" type="hidden" name="stars">
</form>

Oh no, this looks like trouble, but let’s walk through it part by part again.

  1. The PHP library shouldn’t need any further introductions, but take note of $uid = 901. We use a fixed dummy user for this demo, use the session user ID in your own system.
  2. This part updates the star rating for a product, but it is only processed when the user clicks on a star and updates the rating.
  3. Gets the average star ratings for all products, and also for the current user.
  4. Generates the HTML for the dummy products, and also the rating stars. The “highlight number of stars on hover” and “update stars rating” parts will be done with Javascript.
  5. When the user clicks on a star, the hidden product ID and rating fields will be set. The form will be submitted and part B will handle the rating update.

 

 

3B) THE JAVASCRIPT

3b-products.js
var stars = {
  // (A) INIT - ATTACH HOVER & CLICK EVENTS
  init : () => {
    for (let d of document.getElementsByClassName("pStar")) {
      let all = d.getElementsByClassName("star");
      for (let s of all) {
        s.onmouseover = () => { stars.hover(all, s.dataset.i); };
        s.onclick = () => { stars.click(d.dataset.pid, s.dataset.i); };
      }
    }
  },

  // (B) HOVER - UPDATE NUMBER OF YELLOW STARS
  hover : (all, rating) => {
    let now = 1;
    for (let s of all) {
      if (now<=rating) { s.classList.remove("blank"); }
      else { s.classList.add("blank"); }
      now++;
    }
  },
  
  // (C) CLICK - SUBMIT FORM
  click : (pid, rating) => {
    document.getElementById("ninPdt").value = pid;
    document.getElementById("ninStar").value = rating;
    document.getElementById("ninForm").submit();
  }
};
window.addEventListener("load", stars.init);

The final piece of the puzzle.

  • Remember the “hover to update the number of stars”? stars.hover() deals with that.
  • On clicking a star, stars.click() will set the selected product ID, rating, and submit the form.

 

 

EXTRAS

That’s it for the simple project and example. Here are a few small extras that may be useful to you.

 

MUST USERS BE LOGGED IN?

Yes, it is best to keep the rating system to registered users only. It is otherwise difficult or impossible to identify who is who, it will be total chaos if we allow anyone to rate; It will become a spam magnet quickly. If you really have to open to the public, consider using the PHP session ID as the “unique user key”.

  • Change database user_id to VARCHAR.
  • session_start();
  • Use session_id() as the user_id.

 

 

STARS RATING FOR SEO

<script type="application/ld+json">
{
  "@context": "http://schema.org/",
  "@type": "Product",
  "name": "PRODUCT NAME",
  "image": "https://site.com/image.jpg",
  "description": "PRODUCT IMAGE",
  "sku": "IF ANY",
  "mpn": "IF ANY",
  "brand": {
    "@type": "Thing",
    "name": "BRAND NAME"
  },
  "aggregateRating": {
    "@type": "AggregateRating",
    "ratingValue": "4.5",
    "reviewCount": "2"
  }
}
</script>

Yes, we can actually add a small JSON-LD product snippet and include the average rating for SEO purposes. As this is kind of out-of-point for this tutorial, I will just point to:

 

THE END

Thank you for reading, and we have come to the end of this guide. I hope that it has helped you with your project. If you have anything to add to this project, please feel free to comment below. Good luck and happy coding!

14 thoughts on “Simple Star Review Rating In PHP MySQL (Free Download)”

  1. Thanks for all your efforts, I ran the codes step by step, and the star icons did not show, it did show the rating but the stars did not appear or in weight color.

  2. Hello,
    thanks for this great code.
    I use the ranking to set the destination of the next bike tour with my friends. There are 5 tours to choose from.
    Since the bike tours page is not public, I don’t want to use a (user_id) identification, but the “session_id” instead.
    Unfortunately I don’t know where and how to use the “session_start()” command. Can you help me on this.
    Thank you very much and good luck
    Helmut

Comments are closed.