4 Steps Simple Star Review Rating With PHP MySQL

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?

A barebones star rating system only involves a few key components.

  1. A database table to store the ratings.
  2. A PHP library to get and manage the star ratings.
  3. Finally, a page, post, or product to attach the ratings.

An actual example will better illustrate things, let us walk through a simple and lightweight one in this guide – Read on!

ⓘ I have included a zip file with all the source code at the start of this tutorial, so you don’t have to copy-paste everything… Or if you just want to dive straight in.

 

 

TABLE OF CONTENTS

Download & Note Star Rating Useful Bits & Links
The End

 

DOWNLOAD & NOTES

Firstly, here is the download link to the example code as promised.

 

EXAMPLE CODE DOWNLOAD

Click here to download the source code, I have released it under the MIT license, so feel free to build on top of it or use it in your own project.

 

QUICK NOTES

  • Create a database and import the 1-database.sql file.
  • Change the database settings in 2-core.php to your own.
  • Launch 3a-products.php in your browser.

If you spot a bug, please feel free to comment below. I try to answer questions too, but it is one person versus the entire world… If you need answers urgently, please check out my list of websites to get help with programming.

 

 

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` int(11) NOT NULL,
  `user_id` int(11) NOT NULL,
  `rating` smallint(6) NOT NULL DEFAULT '1'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

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-core.php
<?php
// (A) STARS CLASS
class Stars {
  // (A1) CONSTRUCTOR - CONNECT TO DATABASE
  private $pdo;
  private $stmt;
  public $error;
  function __construct () {
    try {
      $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
        ]
      );
    } catch (Exception $ex) { exit($ex->getMessage()); }
  }

  // (A2) DESTRUCTOR - CLOSE DATABASE CONNECTION
  function __destruct () {
    if ($this->stmt !== null) { $this->stmt = null; }
    if ($this->pdo !== null) { $this->pdo = null; }
  }

  // (A3) SAVE/UPDATE USER STAR RATING
  function save ($pid, $uid, $stars) {
    try {
      $this->stmt = $this->pdo->prepare(
        "REPLACE INTO `star_rating` (`product_id`, `user_id`, `rating`) VALUES (?,?,?)"
      );
      $this->stmt->execute([$pid, $uid, $stars]);
      return true;
    } catch (Exception $ex) {
      $this->error = $ex->getMessage();
      return false;
    }
  }

  // (A4) GET USER STAR RATINGS
  function get ($uid) {
    $this->stmt = $this->pdo->prepare(
      "SELECT * FROM `star_rating` WHERE `user_id`=?"
    );
    $this->stmt->execute([$uid]);
    $ratings = [];
    while ($row = $this->stmt->fetch()) {
      $ratings[$row['product_id']] = $row['rating'];
    }
    return $ratings;
  }

  // (A5) GET AVERAGE STAR RATING
  function avg () {
    $this->stmt = $this->pdo->prepare(
      "SELECT `product_id`, ROUND(AVG(`rating`), 2) `avg` FROM `star_rating` GROUP BY `product_id`"
    );
    $this->stmt->execute();
    $average = [];
    while ($row = $this->stmt->fetch()) {
      $average[$row['product_id']] = $row['avg'];
    }
    return $average;
  }
}

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

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

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

  • A – The class Stars literally contains all the mechanics to drive the stars rating.
    • The constructor will automatically connect to the database when a new Stars() object is created, the destructor will close the connection.
    • There are only 3 functions here – save() adds/updates a star rating entry, get() will fetch all the ratings set by a user, and avg() gets the average ratings for all products.
  • B & C – Self-explanatory, the database settings, and creating a stars object.

 

 

STEP 3) PRODUCTS LANDING PAGE

3a-page.php
<div id="demo"><?php
// (A) CORE LIBRARY + USER ID
require "2-core.php";
// FIXED TO 901 FOR DEMO, SHOULD BE SESSION USER ID IN YOUR PROJECT
$uid = 901;
 
// (B) UPDATE STAR RATINGS
if (isset($_POST['pid'])) {
  if (!$_STARS->save($_POST['pid'], $uid, $_POST['stars'])) {
    echo "<div>$_STARS->error</div>";
  }
}
 
// (C) GET RATINGS
$average = $_STARS->avg(); // AVERAGE RATINGS
$ratings = $_STARS->get($uid); // RATINGS BY CURRENT USER
 
// (D) OUTPUT DUMMY PRODUCTS
$products = [
  "1" => ["name" => "Foo Bar", "price" => 123.45],
  "2" => ["name" => "Fork Bar", "price" => 24.56],
  "3" => ["name" => "Goo Bar", "price" => 8.76],
  "4" => ["name" => "Joo Bar", "price" => 2.57]
]; 
foreach ($products as $pid=>$pdt) { ?>
<div class="pdt">
  <div class="pname">Name: <?=$pdt['name']?></div>
  <div class="pprice">Price: $<?=$pdt['price']?></div>
  <div class="prate">Rating: <?=$average[$pid]?></div>
  <div class="pstar" data-pid="<?=$pid?>">
    Your rating: <?php
    $rate = isset($ratings[$pid]) ? $ratings[$pid] : 0 ;
    for ($i=1; $i<=5; $i++) {
      $img = $i<=$rate ? "star" : "star-blank" ;
      echo "<img src='$img.png' data-set='$i'>";
    }
  ?></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 core library shouldn’t need any further introductions, but take note of $uid = 901. We will use a fixed dummy user for the demo, but this should be the 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” part 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.

 

 

STEP 4) THE JAVASCRIPT

3b-products.js
var stars = {
  // (A) INIT - ATTACH CLICK & HOVER EVENTS FOR STARS
  init : function () {
    for (let docket of document.getElementsByClassName("pstar")) {
      for (let star of docket.getElementsByTagName("img")) {
        star.addEventListener("mouseover", stars.hover);
        star.addEventListener("click", stars.click);
      }
    }
  },

  // (B) HOVER - UPDATE NUMBER OF YELLOW STARS
  hover : function () {
    let all = this.parentElement.getElementsByTagName("img"),
        set = this.dataset.set,
        i = 1;
    for (let star of all) {
      star.src = i<=set ? "star.png" : "star-blank.png";
      i++;
    }
  },
  
  // (C) CLICK - SUBMIT FORM
  click : function () {
    document.getElementById("ninPdt").value = this.parentElement.dataset.pid;
    document.getElementById("ninStar").value = this.dataset.set;
    document.getElementById("ninForm").submit();
  }
};
window.addEventListener("DOMContentLoaded", stars.init);

The final piece of the puzzle.

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

 

 

USEFUL BITS & LINKS

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!

7 thoughts on “4 Steps Simple Star Review Rating With PHP MySQL”

  1. Muy buen trabajo de explicación, pero si me permites ( desde españa) deberia verse en el producto las estrellas mas votadas , si 5 estrellas tienes 30 votos y 1 estrella un voto , no debe verse al entrar el ultimo voto efectuado sino el numero de estrella que tenga mas votos.

    1. Sorry, I don’t speak Spanish, and Google translate is not making a lot of sense… I am guessing you want to show the past ratings on products, sorted by the number of stars –

      SELECT * FROM `star_rating` WHERE `product_id`=? ORDER BY `rating` DESC

Leave a Comment

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