So with the magic of some Javascript, PHP, and MySQL we will be able to let the user cast a vote and show them the updated states without interrupting whatever they may be doing on the page. In this tutorial, we will be pretending that the user is casting votes on movies.
To begin, we will need to set up a MySQL database to hold the movies and their ratings from the users. Additionally, we will also be tracking the user's IP, so they can't vote more than once for the same movie. Note: If you are integrating this script into a user system, you may want to track it based on their User ID or Username, instead of IP.
First create a database, if you haven't done so already. (or use an existing one)
Now run this script, then delete the script.
<?php
mysql_connect ('localhost', 'USERNAME', 'PASSWORD');
mysql_select_db('DATABASE');
// holds the movie information
mysql_query("CREATE TABLE IF NOT EXISTS `movies` (
`id` int(11) NOT NULL auto_increment,
`title` varchar(50) NOT NULL,
`votes` int(11) NOT NULL,
`rating` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
");
// holds the voter information
mysql_query("CREATE TABLE IF NOT EXISTS `voters` (
`id` int(11) NOT NULL,
`ip` varchar(20) NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
");
?>
Note: Be sure to change the log in information to yours. (Username, password, and database name)
Now we will need to create [i]ajax.php[i], this script will not be seen viewed by the user. The job of this script is to actually execute the PHP code required to cast a vote. Such as updating the rating, checking if they have already voted, etc. It returns in an XML format which will become clear later on.
Save this file as ajax.php
<?php
mysql_connect ("localhost", "USERNAME", "PASSWORD");
mysql_select_db("DATABASE");
if(is_numeric($_GET['id']) && is_numeric($_GET['rating'])) {
$vote_cast = $_GET['rating'];
$id = $_GET['id'];
if(!($vote_cast >= 1 && $vote_cast <= 10)) {
echo $vote_cast;
die("Invalid vote");
}
if(mysql_num_rows(mysql_query("SELECT * FROM `movies` WHERE `id`='".$id."'")) == 0) {
die("Invalid movie");
}
$ip = $_SERVER['REMOTE_ADDR'];
if(mysql_num_rows(mysql_query("SELECT * FROM `voters` WHERE `id`='".$id."' && `ip`='".$ip."'")) == 0) {
mysql_query("INSERT INTO `voters`(`id`, `ip`) VALUES('".$id."', '".$ip."')");
mysql_query("UPDATE `movies` SET `votes`=votes+1, `rating`=rating+".$vote_cast." WHERE `id`='".$id."'") or die(mysql_error());
$data = mysql_fetch_array(mysql_query("SELECT * FROM `movies` WHERE `id`='".$id."'"));
$rating = round($data['rating']/$data['votes'], 1);
$votes = $data['votes'];
header('Content-Type: text/xml');
header('Pragma: no-cache');
echo '<?xml version="1.0" encoding="UTF-8"?>'."n";
echo "<result id='".$id."' rating='".$rating."' votes='".$votes."'>Vote cast and saved.</result>n";
}else{
header('Content-Type: text/xml');
header('Pragma: no-cache');
echo '<?xml version="1.0" encoding="UTF-8"?>'."n";
echo "<result id='".$id."' rating='-1' votes='-1'>You have already voted.</result>n";
}
}else{
echo "Invalid input";
}
?>
Note: Again, change your log in information to yours.
Code Breakdown:
if(is_numeric($_GET['id']) && is_numeric($_GET['rating'])) { - This will check that our input is set and that it is valid. (a number)
if(!($vote_cast >= 1 && $vote_cast <= 10)) { - Although the script will only call ajax.php within the limits, a person could attempt to cast it manually with a higher or lower vote.
if(mysql_num_rows(mysql_query("SELECT * FROM `movies` WHERE `id`='".$id."'")) == 0) { - This will check if the movie exists, based on its ID. It will return 0 or 1.
$ip = $_SERVER['REMOTE_ADDR']; - This variable will retrieve the user's IP and set it to $ip.
if(mysql_num_rows(mysql_query("SELECT * FROM `voters` WHERE `id`='".$id."' && `ip`='".$ip."'")) == 0) { - This will check if the user has already voted, it will return 0 if they have not.
mysql_query("INSERT INTO `voters`(`id`, `ip`) VALUES('".$id."', '".$ip."')"); - This will insert the information about the user, so they can't vote for the same movie again.
mysql_query("UPDATE `movies` SET `votes`=votes+1, `rating`=rating+".$vote_cast." WHERE `id`='".$id."'"); - This will add one more vote and add the rating to the total rating.
$data = mysql_fetch_array(mysql_query("SELECT * FROM `movies` WHERE `id`='".$id."'")); - This will retrieve the newly updated data about the movie.
$rating = round($data['rating']/$data['votes'], 1); - This will get the average rating and round it to one decimal place.
header('Content-Type: text/xml'); - This will change the output type to XML, instead of HTML.
echo '<?xml version="1.0" encoding="UTF-8"?>'."n"; - Required header in valid XML files
echo "<result id='".$id."' rating='".$rating."' votes='".$votes."'>Vote cast and saved.</result>n"; - This will output the movie id, new rating, new votes, and a message.
echo "<result id='".$id."' rating='-1' votes='-1'>You have already voted.</result>n"; - This message will be shown if they have already voted,
Now we have all the backend coding complete but we still have nothing to show for it, so we will need to create something for the user to interact with, which will list the movies and allow them to vote.
Save this file as movies.php
<script type="text/javascript">
function vote(id, rating) {
if (window.XMLHttpRequest) {
http = new XMLHttpRequest();
} else if (window.ActiveXObject) {
http = new ActiveXObject("Microsoft.XMLHTTP");
}
var url = 'ajax.php?';
var fullurl = url + 'id=' + id + '&rating=' + rating;
http.open("GET", fullurl, true);
http.send(null);
http.onreadystatechange = statechange_rate;
}
function statechange_rate() {
if (http.readyState == 4) {
var xmlObj = http.responseXML;
var html = xmlObj.getElementsByTagName('result').item(0).firstChild.data;
var id = xmlObj.getElementsByTagName('result').item(0).getAttribute("id");
var votes = xmlObj.getElementsByTagName('result').item(0).getAttribute("votes");
var rating = xmlObj.getElementsByTagName('result').item(0).getAttribute("rating");
if(votes != -1) {
document.getElementsByName('output_' + id).item(0).innerHTML = "<br />" + html;
window.setTimeout("document.getElementsByName('output_" + id + "').item(0).innerHTML = '';", 5000);
document.getElementsByName('rating_' + id).item(0).innerHTML = rating;
document.getElementsByName('votes_' + id).item(0).innerHTML = votes;
}else{
document.getElementsByName('output_' + id).item(0).innerHTML = "<br />" + html;
window.setTimeout("document.getElementsByName('output_" + id + "').item(0).innerHTML = '';", 5000);
}
}
}
</script>
<?php
mysql_connect ("localhost", "USERNAME", "PASSWORD");
mysql_select_db("DATABASE");
$result = mysql_query("SELECT * FROM `movies` ORDER BY `title`");
while($data = mysql_fetch_array($result)) {
if($data['votes'] > 0) {
echo $data['title'].' - <span name="rating_'.$data['id'].'">'.round($data['rating']/$data['votes'], 1).'</span> out of 10 (<span name="votes_'.$data['id'].'">'.$data['votes'].'</span> votes)<br />';
}else{
echo $data['title'].' - 0 out of 10<br />';
}
echo 'Vote: ';
for($i = 1;$i < 11;$i++) {
echo '<a href="javascript:vote('.$data['id'].', '.$i.')">'.$i.'</a> ';
}
echo ' <span name="output_'.$data['id'].'"></span>';
echo '<br /><br />';
}
?>
Note: Again, change your log in information to yours.
Code Breakdown:
http.open("GET", fullurl, true); - This will create the request to our ajax.php file, with the information about the vote.
if(votes != -1) { - Before, you may have noticed we set votes="-1" if they had already voted, this was just to provide an easy way to check the return of our ajax.php script.
document.getElementsByName('output_' + id).item(0).innerHTML = "<br />" + html; - This will inform the user about the vote they have cast.
window.setTimeout("document.getElementsByName('output_" + id + "').item(0).innerHTML = '';", 5000); - This will set a delay to make that message go away in 5000 miliseconds (5 seconds).
document.getElementsByName('rating_' + id).item(0).innerHTML = rating; - This will update the rating on the page to the new one.
$result = mysql_query("SELECT * FROM `movies` ORDER BY `title`"); - This will return every movie we have ordered by name.
while($data = mysql_fetch_array($result)) { - Loop through every movie we have and execute the code below.
if($data['votes'] > 0) { - This will make sure we have atleast 1 vote, to avoid a division by 0.
for($i = 1;$i < 11;$i++) { - This will list the numbers 1 through 10, allowing the users to click on it to vote.
My result with some random movies and ratings.
It may not be pretty but, it is easy to integrate. It should be very easy to change the numbers to images, such as stars or any image you have in mind. The same concept can be seen on the right (the 5 stars), feel free to try it out.
discuss this topic to forum
