One of the best ways to improve a site is to let users interact with it. This can be through comments , a forum or small things like letting your visitors rate your content. A rating script may sound simple but requires a good deal of coding to be done.
First of all major credit goes to komodomedia.com and boedesign.com for their versions of a rating script. They both gave a good base to start on.
Experience required: Intermediate PHP, Javascript and CSS
Features
- Light on the server and the database
- The images can easily be substituted
- Doesn’t fail if javascript is disabled – Unobtrusive
- Protection against SQL injection
- Stores rating upto 3 decimal places
- Can globally disable all rating or disable only for individual “id”s
- Display number of rates or the current rating in text
Step one
The config file
This is a pretty straightforward step. The config.php file just contains the database connection code and a record of the settings the rating script uses.
[php]<?php
$server = ‘localhost’; //Database settings
$username = ‘root’;
$password = ” ;
$database = ”; //The database where the tables are present
$con = mysql_connect("$server","$username","$password"); //The connection code
if (!$con)
{
die(‘Could not connect : ‘ . mysql_error());
}
mysql_select_db($database, $con);
//Settings array
//Edit this
$settings = array(
"table" => ”, //Name of the table where the three fields are stored
"stars" => 5, //Number of stars/objects to show
"id" => ‘id’, //The field name in the content table that uniquely identifies each row
"expire_time" => 99999999, //Time after which the user cookie that decides if a user should vote expires
"disable" => false //Globally disable all rating
);
?>[/php]
There is not a lot to explain, but you should keep in mind that if you change the images you should change the image height and width the css and js files.
Step two
Creating the tables
The script needs two different tables, one dedicated to recording the user’s rates and the other is the table of the content for which the rating script is intended.
There is a file named install_table.php that will create the first table.
[php]<?php
include("./includes/config.php");
$sql = ‘CREATE TABLE `ratings` (
`id` int(11) NOT NULL auto_increment,
`rating_id` VARCHAR(80) NOT NULL,
`rating` int(11) NOT NULL,
`ip` varchar(25) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;’;
if(mysql_query($sql)){
echo ‘Table created!’;
} else {
echo ‘Table installation unsuccesful :’.mysql_error();
}
?>[/php]
The second table needs three fields added to it rating[decimal(3.3)],total_rating[int(11)],total_votes[int(11)]
total_votes will record the number of ratings,
rating will record the current rating and
total_rating will record the sum of all ratings
Step three
Getting the css and html right
The base code will be an unordered list with list items of different widths an z-indexs.
First a div to display information like the number of rates or the current rating. Next is the unordered list with the first list-item to display the current rating in stars, next is a bunch of list-items with <a> tags to display the stars according to increasing width to represent the ratings that a user can give.
Finally there is a div to display any messages to the user after rating like a error message.
[html]<div class="rated_text">Rated <span id="ratio_2" class="ratio_class">4.3</span>/5 (<span id="votes_2" class="votes_class">9 Ratings</span>)</div>
<ul class="star-rating" id="rater_2">
<li class="current-rating" style="width:86%;" id="ul_2"></li>
<li><a style="width:20%;z-index:6;" onclick="rate(’1′,’2′,1,1); return false;" href="includes/rating_process.php?id=2&rating=1" title="1 star out of 5">1</a></li>
<li><a style="width:40%;z-index:5;" onclick="rate(’2′,’2′,1,1); return false;" href="includes/rating_process.php?id=2&rating=2" title="2 star out of 5">2</a></li>
<li><a style="width:60%;z-index:4;" onclick="rate(’3′,’2′,1,1); return false;" href="includes/rating_process.php?id=2&rating=3" title="3 star out of 5">3</a></li>
<li><a style="width:80%;z-index:3;" onclick="rate(’4′,’2′,1,1); return false;" href="includes/rating_process.php?id=2&rating=4" title="4 star out of 5">4</a></li>
<li><a style="width:100%;z-index:2;" onclick="rate(’5′,’2′,1,1); return false;" href="includes/rating_process.php?id=2&rating=5" title="5 star out of 5">5</a></li>
</ul>
<div id="message__2"></div>[/html]
The css to make this design work was based on code found at http://www.komodomedia.com/blog/2007/01/css-star-rating-redux/
The modified version looks like this
[css].star-rating,
.star-rating a:hover,
.star-rating a:active,
.star-rating .current-rating{
background: url(../images/rating_star.gif) left -1000px repeat-x;
}
.star-rating{
position:relative;
width:125px; /* (Width of each individual star)*(Number of stars) */
height:25px; /* Height of each individual star */
overflow:hidden;
list-style:none;
margin:0;
padding:0;
background-position: left top;
}
.star-rating li{
display: inline;
}
.star-rating a,
.star-rating .current-rating{
position:absolute;
top:0;
left:0;
text-indent:-1000em;
height:25px; /* Height of each individual star */
line-height:25px; /* Height of each individual star */
outline:none;
overflow:hidden;
border: none;
}
.star-rating a:hover{
background-position: left bottom;
}
.star-rating .current-rating{
z-index:1;
background-position: left center;
}
/* SECOND STAR (ALREADY VOTED) */
.star-rating_disabled,
.star-rating_disabled a:active,
.star-rating_disabled .current-rating{
background: url(../images/rating_star_2.gif) left -1000px repeat-x;
}
.star-rating_disabled{
position:relative;
width:125px; /* (Width of each individual star)*(Number of stars) */
height:25px; /* Height of each individual star */
overflow:hidden;
list-style:none;
margin:0;
padding:0;
background-position: left top;
}
.star-rating_disabled li{
display: inline;
}
.star-rating_disabled a,
.star-rating_disabled .current-rating {
position:absolute;
top:0;
left:0;
text-indent:-1000em;
height:25px; /* Height of each individual star */
line-height:25px; /* Height of each individual star */
outline:none;
overflow:hidden;
border: none;
cursor:default; /* So that when the cursor goes over the link it remains the same*/
}
.star-rating_disabled .current-rating{
z-index:1;
background-position: left center;
}
/* END SECOND STAR */
.voted {
padding:5px 5px 5px 16px;
text-align:center;
font-family:Verdana, Arial, Helvetica, sans-serif;
color:#333;
width:130px;
font-size:11px;
}
/* The text that displays the rating information */
.rated_text {
font-family:Verdana, Arial, Helvetica, sans-serif;
font-size:11px;
margin-bottom:5px;
color:#666;
}
/* Text that shows the rating */
.ratio_class {
color:#00CC00;
font-weight:bold;
}
/* Text that shows the number of rates */
.votes_class {
}
/* Text that tells the users if they have already voted*/
.rated_twice{
color:#EE0000;
padding:5px 5px 5px 16px;
text-align:center;
font-family:Verdana, Arial, Helvetica, sans-serif;
width:130px;
font-size:11px;
}[/css]
Step four
Php functions to interact with the database
[php]<?php
function get_rating($id){
global $settings;
sanitize($id);
$sql = mysql_query(‘SELECT rating FROM ‘.$settings['table'].’ WHERE ‘.$settings['id']." = ‘$id’ ");
if(mysql_num_rows($sql) == 1){
$data = mysql_fetch_assoc($sql);
return $data['rating'];
} else {
return ’0′;
}
}
function get_rating_rounded($id){
global $settings;
sanitize($id);
$sql = mysql_query(‘SELECT rating FROM ‘.$settings['table'].’ WHERE ‘.$settings['id']." = ‘$id’ ");
if(mysql_num_rows($sql) == 1){
$data = mysql_fetch_assoc($sql);
return round($data['rating'],2);
} else {
return ’0′;
}
}
function get_num_votes($id){
global $settings;
sanitize($id);
$sql = mysql_query("SELECT total_votes FROM $settings[table] WHERE $settings[id] = ‘$id’ ");
$data = mysql_fetch_assoc($sql);
if($data['total_votes'] == 0){
$votes = ’0 Ratings’;
}else if($data['total_votes'] == 1){
$votes = ’1 Rates’;
} else {
$votes = $data['total_votes'].’ Ratings’;
}
return $votes;
}
?>[/php]
The first function as its name suggests, retrieves the rating from the database, which corresponds to a unique “id”.
The second function is similar to the first one, except it returns the rating rounded (without the decimal part).
The third function returns the number of votes/rates for a specific “id” value.
The global $settings; line is important as php treats scope of a variable differently than most languages like c or java.
It requires explicit specification that a function can access a non local variable.
Note: The sanitize() function will be explained under Security, at the end.
Step five
Php function to generate the rating html
First we need to check if the user has voted or not by looking up his IP in our database or check to see if certain cookies are set. Even if the user has not voted rating could be disabled so we need to check for that. Also rating could be disabled for that rater only.
Don’t take a breath yet, now we need to check if we have to show the number of rates and the rating ratio.
Next we need to decide based upon the above variables if we need to show the disabled rater or the working one.
Once done the code to actually display the rater begins, we loop through the number of stars and accordingly decide the z-index and width for each list item.
[php]function show_rating($id,$show_ratio = false, $show_votes = false, $disable = false){
<pre>
global $settings;
$html = ”;
$show = false;
$sql = mysql_query("SELECT id FROM ratings WHERE ip = ‘".$_SERVER['REMOTE_ADDR']."’ AND rating_id = ‘$id’");
if(mysql_num_rows($sql) > 0 || $disable == true || isset($_COOKIE['voted_'.$id]) || $settings['disable'] == true){
//The user has already voted or voting has been disabled.
$show = false;
} else {
//The user has not voted.
$show = true;
}
$html = ”;
if($show_ratio || $show_votes){
$html = ‘<div class="rated_text">’;
if($show_ratio){
$html .= ‘Rated <span id="ratio_’.$id.’" class="ratio_class">’.get_rating($id).’</span>/5′;
}
if($show_votes){
$html .= ‘ (<span id="votes_’.$id.’" class="votes_class">’.get_num_votes($id).’</span>)’;
}
$html .= ‘</div>
‘;
}
$html .= ‘<ul class="star-rating’.(($show == false)?’_disabled’:”).’" id="rater_’.$id.’">
<li class="current-rating" style="width:’.(get_rating($id)*100/$settings['stars']).’%;" id="ul_’.$id.’"></li>
‘;
if($show == true){
for($i=1;$i<=$settings['stars'];$i++){
$html .= ‘<li><a style="width:’.($i*100/$settings['stars']).’%;z-index:’.($settings['stars']+2-$i).’;" onclick="rate(\”.$i.’\',\”.$id.’\',’.(int)$show_ratio.’,’.(int)$show_votes.’); return false;" href="includes/rating_process.php?id=’.$id.’&rating=’.$i.’" title="’.$i.’ star out of ‘.$settings['stars'].’">’.$i.’</a></li>
‘;
}
return $html .= ‘
</ul>
<div id="message_’.$id.’"></div>’;
}else{
for($i=1;$i<=$settings['stars'];$i++){
$html .= ‘<li><a style="width:’.($i*100/$settings['stars']).’;z-index:’.($settings['stars']+2-$i).’;" onclick="return false;" href="#" title="’.$i.’ star out of ‘.$settings['stars'].’">’.$i.’</a></li>
‘;
}
return $html.’</ul>’;
}
}[/php]
Step six
The javascript
Im not very familiar with javascript so most of it was based on some found at http://boedesign.com/blog/2007/02/18/ajax-star-rating/
[javascript]// AJAX
var xmlHttp
function GetXmlHttpObject(){
var xmlHttp = null;
try {
// Firefox, Opera 8.0+, Safari
xmlHttp = new XMLHttpRequest();
}
catch (e) {
// Internet Explorer
try {
xmlHttp = new ActiveXObject("Msxml2.XMLHTTP");
}
catch (e){
xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
}
}
return xmlHttp;
}
//Register the rating with the database
function rate(rating,id,show_ratio,show_votes){
xmlHttp = GetXmlHttpObject()
if(xmlHttp == null){
alert ("Your browser does not support AJAX!");
return;
}
xmlHttp.onreadystatechange = function(){
var message = document.getElementById(‘message_’+id);
var uldiv = document.getElementById(‘ul_’+id);
if (xmlHttp.readyState == 4){
//message.style.display = ‘none’;
var res = xmlHttp.responseText;
if(res == ‘You have already rated this!’){
message.style.display = ‘block’;
message.innerHTML = ‘<div class="rated_twice">You already rated this!</div>’;
} else {
message.style.display = ‘block’;
message.innerHTML = ‘<div class="voted">Thanks for voting!</div>’;
if(show_ratio == true){
var ratio_div = document.getElementById(‘ratio_’+id);
ratio_div.innerHTML = res;
}
if(show_votes == true){
var votes_div = document.getElementById(‘votes_’+id).innerHTML;
var new_value = parseInt(votes_div.split(‘ ‘,1)) + 1;
if(new_value == 1){
document.getElementById(‘votes_’+id).innerHTML = new_value+’ Rate’;
} else {
document.getElementById(‘votes_’+id).innerHTML = new_value+’ Ratings’;
}
}
var ul_rater = document.getElementById(‘rater_’+id);
ul_rater.className = ‘star-rating_disabled’;
var all_li = ul_rater.getElementsByTagName(‘li’);
for(var i=1;i<all_li.length;i++){
all_li[i].getElementsByTagName(‘a’)[0].onclick = ‘return false;’;
all_li[i].getElementsByTagName(‘a’)[0].setAttribute(‘href’,'#’);
}
if(navigator.appName == ‘Microsoft Internet Explorer’){ // IE
uldiv.style.setAttribute(‘width’,res*100/5+’%'); //The formula is res*100/(Number of stars)
} else { // Everyone else
uldiv.setAttribute(‘style’,'width:’+res*100/5+’%'); //The formula is res*100/(Number of stars)
}
}
} else {
message.innerHTML = ‘loading….’;
}
}
var url = "includes/rating_process.php";
var params = "id="+id+"&rating="+rating;
xmlHttp.open("POST",url,true);
xmlHttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xmlHttp.setRequestHeader("Content-length", params.length);
xmlHttp.setRequestHeader("Connection", "close");
xmlHttp.send(params);
}[/javascript]
Step seven
Php functions to record the users choice in the database
This part stores the ratings from the users into both tables. It should be able to handle both GET and POST requests. It first sanitizes the input and then checks if the input is valid. Then it should checks if the user has already voted or not, if not it sets the “voted” cookie and registers the IP in the database. Then we have to calculate the new ratio and store it in the database and give back the appropriate output. A redirect in case of a GET request or the modified rating in case of a POST request.
[php] $id = sanitize($_REQUEST['id']);
$rating = (int) $_REQUEST['rating'];
if($rating <= $settings['stars'] && $rating >= 1){ //Check if it is a valid rating value
if(mysql_fetch_assoc(mysql_query("SELECT id FROM ratings WHERE ip = ‘".$_SERVER['REMOTE_ADDR']."’ AND rating_id = ‘$id’")) || isset($_COOKIE['voted_'.$id])){
echo ‘You have already rated this!’; //Message to display if the user has already voted.
} else {
setcookie(‘voted_’.$id,$id,time() + $settings['expire_time'],’/',$domain,false);
mysql_query("INSERT INTO ratings (rating_id,rating,IP) VALUES (‘$id’,'$rating’,'".$_SERVER['REMOTE_ADDR']."’)") or die(mysql_error());
$data = mysql_fetch_assoc(mysql_query(‘SELECT * FROM ‘.$settings['table'].’ WHERE ‘.$settings['id']." = ‘$id’ ")) or die(mysql_error());
$total_votes = $data['total_votes'] + 1;
$total_rating = $data['total_rating'] + $rating;
$dbrating = $total_rating/$total_votes;
mysql_query(‘UPDATE ‘.$settings['table'].’ SET rating = \”.$dbrating.’\',total_votes = \”.$total_votes.’\',total_rating = \”.$total_rating.’\'WHERE ‘.$settings['id']." = ‘$id’ ") or die(mysql_error());
if($_GET){echo round($dbrating,2);}
}
if($_GET){header("Location:".$_SERVER['HTTP_REFERER']);
die;
}
}
else {
echo ‘You cannot rate this more than 5 or less than 1 <a href="’.$_SERVER['HTTP_REFERER'].’">Go back</a>’;
}[/php]
Step eight
Security
The function below is to prevent SQL injection by escaping all SQL statments.
[php]function sanitize($input){ //sanitize user input
$input = trim($input);
if(get_magic_quotes_gpc()) {
$input = stripslashes($input);
}
return mysql_real_escape_string($input);
}[/php]
To our readers from Vishal Sanjay
Its always a great idea to allow your readers rate your product, this will not only let you know that there is someone reading your content, but that there is someone who agrees with you. Many bloggers have told me that using such a system will help your visitors judge your content without wasting time.
Another thing I’d like to announce is that the author of this post, Bittu (I know an unusual name
) is taking over as the co-owner of this blog and will start to write a few articles from now on. Bittu is a web developer(unlike me), so you’ll be seeing web design posts along with our normal blogging tips and internet marketing ones.





{ 13 comments… read them below or add one }
I do not use it on my blog, however I’ve noticed that rating system integrated on the blogs is very popular.
It Could be useful for my readers, so I’ve retweeted it. Thanks Vishal!
.-= Nick´s last blog ..6 Basic Money Rules For A Bad Economy =-.
Glad you liked it Nick!! This happens to be from my co-author. Thanks for retweeting and commenting.
Hi Vishal…I cannot use it this on my site. But anyways, looks..very useful information. Thanks for sharing it here.
.-= Aswani´s last blog ..IPL Scam – The Inside Story =-.
I think it won’t work in blogger platform.
But anyway, It really helps for new to this. It is very useful for the author to gather information from their reader’s opinion.
.-= Real Blogging Advice´s last blog ..10 Steps to Successful Selling on eBay =-.
PLEASE STOP POSTING YOUR BLOG LINK ON EVERY SINGLE ARTICLE ON TECH CRUNCH. It is not helping your SEO efforts and just makes you look ignorant and stupid. All of your comments on TechCrunch look like you just said anything just to get a link on that page. It’s pathetic.
great post as usual!
Its better to use Outbrain for rating system.
@Real Blogging Advice – I will come up with a rating system for blogger in some days. I hope it helps you.
.-= Franklin Manuel´s last blog ..How To Setup Custom Sub Domain For Your Tumblr Blog =-.
Hi, i need some help to use this on my website, it is not registering the data.
Could someone post an example of the config and sql table names?
what an amazing post that possess actually come via. This gives the information that i truly was searching for the previous 7 days and I’m really satisfied along with this publish. Require more like this. Many thanks.
hi Jorge
the config file for the demo is
$settings = array(
“table” => ‘(sorry cant reveal this for security reasons)’,
“stars” => 5,
“id” => ‘id’,
“expire_time” => 99999999,
“disable” => false
);
if this dosent solve the problem read this
both tables(the content and the ratings table) should be in the same database
check if your mysql username and password are correct
also if the mysql user has permissions to insert a record
it might help if you had php show all errors (E_ALL)
exactly what an amazing publish which possess ever arrive via. This the actual information which i was really searching for the previous week and I’m truly satisfied along with this post. Require more like this. Thank you.
As a tutorial, there really are a lot of quite important steps missing from this. Where does $id come from? What do you do with any of the code in index.php? This is a bit of a mess, no offence.
Anyway, I am getting a 'message is null' message from firebug when clicking on a rating, regarding message.innerhtml
Also the script is telling me that I have already rated this but there is nothing on the database.
I'm not finished read this yet, but it's so fabulous 'n I'll back again when I was finished my job
{ 9 trackbacks }