Web Statistics
  • Who am I??

    Heya!! I'm Vishal Sanjay the person responsible for all the content you see on your blog. Over the years I've devised many ways to make money on the internet, to know them all do stick around. Read More...
  • Advertise Here!

    Your currently seeing one of the most reputed blogs in the blogosphere.....Gain laser targeted traffic by advertising on this site...See What We Have To Offer
  • Custom Widget 3

    This widget is completely customizable and can be controlled via the Widgets Panel in Admin → Appearance → Widgets → Feature Box Column 3

Unobstructive AJAX Rating System

by bittuthegr8 on April 22, 2010

  • Sharebar

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.

Demo

Download

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 }

Nick April 23, 2010 at 7:22 pm

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 =-.

Reply

Vishal Sanjay April 23, 2010 at 8:31 pm

Glad you liked it Nick!! This happens to be from my co-author. Thanks for retweeting and commenting.

Reply

Aswani April 23, 2010 at 10:54 pm

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 =-.

Reply

Real Blogging Advice April 27, 2010 at 7:26 pm

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 =-.

Reply

markus April 28, 2010 at 7:41 pm

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.

Reply

MarkSpizer May 3, 2010 at 4:18 pm

great post as usual!

Reply

Franklin Manuel May 10, 2010 at 8:41 am

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 =-.

Reply

Jorge May 12, 2010 at 12:27 am

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?

Reply

Heavy Metal CD Reviews May 14, 2010 at 7:55 pm

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.

Reply

bittuthegr8 May 14, 2010 at 8:31 pm

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)

Reply

lalique glass May 17, 2010 at 12:20 pm

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.

Reply

Mike July 24, 2010 at 2:46 am

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.

Reply

Metal File Cabinet December 15, 2010 at 2:04 pm

I'm not finished read this yet, but it's so fabulous 'n I'll back again when I was finished my job :D

Reply

Leave a Comment

{ 9 trackbacks }