Dec
18
2009

Simple Voting for WordPress with PHP and jQuery

by   |  Posted in Tutorials  |  41 comments
This tutorial is no longer up-to-date. Please check out A Better Voting System for WordPress for a more secure and efficient approach.

I needed to create a simple way for site members to vote on a post and the few scripts I found didn’t really work how I needed them to. So I decided to whip something together myself. I developed a pretty basic script using PHP and jQuery to allow members who are signed in to click on a simple link to add their vote to a post.

First you need to add this to your header.php file between the <head> tags. If you notice that you already have the <?php wp_head(); ?> call somewhere else in your header.php file, just delete it.

<?php wp_enqueue_script( 'jquery' ) ?>
<?php wp_head(); ?>
<script type="text/javascript">
jQuery(document).ready(function(){
jQuery(".vote a").click(
function() {
var some = jQuery(this);
var thepost = jQuery(this).attr("post");
var theuser = jQuery(this).attr("user");
jQuery.post("<?php bloginfo('template_url'); ?>/vote.php", {user: theuser, post: thepost}, 
function(data) {
var votebox = ".vote"+thepost+" span";
jQuery(votebox).text(data);
jQuery(some).replaceWith('<span class="voted">Voted</span>');
});
});
});	
</script>

When a member clicks on the vote link, the above code will get the post ID and the member’s user ID and send it to a file called vote.php using a post method. The vote.php file will perform everything we need to add the vote.

So let’s create a file called vote.php and add it to your theme’s directory.

<?php
$file = dirname(__FILE__);
$file = substr($file, 0, stripos($file, "wp-content") );
 
require( $file . "/wp-load.php");
 
$currentvotes = get_post_meta($_POST['post'], 'votes', true);
$currentvotes = $currentvotes + 1;
 
$voters = get_post_meta($_POST['post'], 'thevoters', true);
if(!$voters) $voters = $_POST['user']; else $voters = $voters.",".$_POST['user'];
 
update_post_meta($_POST['post'], 'votes', $currentvotes);
update_post_meta($_POST['post'], 'thevoters', $voters);
 
echo $currentvotes;
?>

Once the information is posted to vote.php two custom fields are created. One to count the vote and one to add the voter to a list so that they can’t vote again.

Add the code below to your functions.php file, or create a functions.php file if your theme does not have one.

// voting function
function voting($id) {
global $user_ID;
$currentvotes = get_post_meta($id, 'votes', true);
$voters = get_post_meta($id, 'thevoters', true);
$voters = explode(",", $voters);
foreach($voters as $voter) {
	if($voter == $user_ID) $alreadyVoted = true;
}
 
if(!$currentvotes) $currentvotes = 0;
echo '<div class="vote vote'.$id.'"><span>'.$currentvotes.'</span>';
if($user_ID && !$alreadyVoted) echo '<br /><a post="'.$id.'" user="'.$user_ID.'">'.__("Vote").'</a>';
if($user_ID && $alreadyVoted) echo '<br /><span class="voted">'.__("Voted").'</span>';
echo '</div>';
if(!$user_ID) echo '<div class="signup"><p><a href="'.get_bloginfo('url').'/wp-login.php?action=register">'.__('Register').'</a> '.__('to vote').'.</p></div>';
}

Let’s add some CSS to style our voting box.

.vote {
	padding: 5px 0;
	background: #eee;
	border: 1px solid #ddd;
	width :50px;
	text-align: center;
	margin: 10px auto;
	}
 
	.vote a, .vote span.voted {
		cursor: pointer;
		margin-top: 5px;
		padding-top: 5px;
		border-top: 1px solid #ddd;
		display: block;
		}
 
	.vote span.voted {
		cursor: default;
		}

Now all you need to do is add the following within the WordPress loop to have the voting box appear.

<?php voting($post->ID); ?>

Test it out below:

0
Vote

About the author:

A freelance web developer living in Montreal who spends most of his time writing for this site and building Premium themes for WordPress. You can find him on Twitter @bavotasan.

Site5 Affiliate Link
Share the love...

Tags: , , , , , ,

Short URL: http://bit.ly/aLhLbd

Discussion 41 Comments

  1. anthony on December 18, 2009 at 5:17 pm

    terrific ideal. what would be an equally great is a way to query the “top” or “recetn” votes =)

  2. Jauhari on December 21, 2009 at 10:48 am

    Wonderful trick…

  3. Twitter Icon Guy on December 27, 2009 at 10:29 am

    I will be “borrowing” this for sure, thanks!

    I implemented the most popular star/rating plugin the other day for a WP site, but it just seems to have TOO MANY features and overhead.

    Also a jQuery fan, so again, many thanks!

  4. Andrew on January 13, 2010 at 4:12 pm

    When already logged in as wordpress admin, I am getting a Call to Undefined function error for $alreadyvoted. This can be worked around by adding $alreadyvoted=false; to the beginning of the function, however this throws off the if($user_ID && !$alreadyVoted) condition.

    Is there any way to allow guest voting without registration? Furthermore, do you have a script for the registration process? As of now it is an empty link.

    • c.bavota on January 13, 2010 at 11:45 pm

      You are correct about that register link. There is now a fix above. Be sure you have registration turned on or else the link will take you nowhere.

      You gotta remember that PHP is case sensitive so $alreadyvoted and $alreadyVoted are two different variables.

    • Andrew on February 3, 2010 at 1:06 am

      Is there any way to allow guest voting without registration? I don’t think that it’s necessary for my purposes?

  5. Karar A. on January 26, 2010 at 5:46 am

    very cool man :)

    do you know anyway to make it rating ? like the 5 stars rating ?
    really would be great.

    Thanks
    Karar A.

  6. Genevi?®ve on January 28, 2010 at 10:30 am

    Hey!
    Your code has been very useful.
    I would like to make it work without the registration process.
    Is that possible?

  7. c.bavota on February 3, 2010 at 11:46 am

    @Genevi?®ve & @Andrew,

    This specific code would only work with registered users. It is possible to do if for non-registered users but it would take a lot of modification. Instead of storing if the person voted in their user account, you would have to work with cookies. That changes quite a bit.

    • Andrew on February 3, 2010 at 5:13 pm

      Yeah that’s what I figured. Thanks for the help and keep up the good work.

    • MikCronin on October 20, 2010 at 1:10 am

      @Andrew,

      Did you manage to figure out the cookie code? I have tried but not a cookie pro! :) Let me know if you have any modified code you want to share for allowing unregistered user voting.

  8. SEO Birmingham on February 20, 2010 at 1:36 pm

    I am a big big fan of Jquery, with the library there are so many cool interactive things you can do. This example is very good, effective and a little extra to you blog. As part of my work I often get asked to market sites that use flash, I find my self turning to the likes of jquery and motools more and more as a viable alternative to flash for on page interactivity.

    Thanks for the example, ill be using this on my blog.

  9. SEO Birmingham on April 6, 2010 at 7:49 am

    To be honest I am not a big user of PHP or Jquery, I have an .asp site. I am still getting familiar with PHP and Jquery but the examples you have shown seem to be very helpful, I will try it out and let you know how I have got on.

  10. Flights to Paphos on April 12, 2010 at 2:11 pm

    @SEO Birmingham Get using PHP and Jquery, let us know how you get on

  11. Kids Days Out on April 20, 2010 at 7:06 am

    Very good coding

  12. seo birmingham on April 26, 2010 at 5:46 pm

    Not a big fan of php to be honest I have a classic asp, I have tried php and jquery recently for my clients and I am very impressed indeed.

  13. yadi on May 10, 2010 at 4:09 am

    Hi, thank’s . its work for my own social bookmarking. take a look at nestdev.com.

  14. eJobsViet on July 18, 2010 at 10:37 am

    very cool

    Thanks a lot

  15. Justin Broglie on September 1, 2010 at 11:39 am

    Thank you so much for sharing this code! This is a simple and very good way to do voting. I was trying to use the Vote-It-Up plugin, but it uses a separate table, and my site is a network of sites using WP3 and BuddyPress. With this, the voting stats are simply in the meta data, genius!

    Here are two additions I made in order it to best work on a network hub:

    - added underscores before the custom field key names (i.e. __votes) so that users will not be able to just change the votes as they want :-)

    - added some lines that check to see if the blog of the post is different than the current blog, thus switch_to_blog() needs to be called.

    Here is my changed code:

    id) {
    	switch_to_blog($_POST['blog']);
    	$need_to_switch_back = true;
    }
     
    $currentvotes = get_post_meta($_POST['post'], '_votes', true);
    $currentvotes = $currentvotes + 1;
     
    $voters = get_post_meta($_POST['post'], '_thevoters', true);
    if(!$voters) $voters = $_POST['user']; else $voters = $voters.",".$_POST['user'];
     
    update_post_meta($_POST['post'], '_votes', $currentvotes);
    update_post_meta($_POST['post'], '_thevoters', $voters);
     
    if ($need_to_switch_back) restore_current_blog();
     
    echo $currentvotes;
    ?&gt;
     
    		jQuery(document).ready(function(){
    		jQuery(".vote a").click(
    		function() {
    		var some = jQuery(this);
    		var thepost = jQuery(this).attr("post");
    		var theuser = jQuery(this).attr("user");
    		var theblog = jQuery(this).attr("blog");
    		jQuery.post("/vote.php", {user: theuser, post: thepost, blog: theblog}, 
    		function(data) {
    		var votebox = ".vote"+thepost+" span";
    		jQuery(votebox).text(data);
    		jQuery(some).replaceWith('Voted');
    		});
    		});
    		});
    function voting($id, $blog) {
    	global $user_ID;
     
    	$need_to_switch_back = false;
    	$current_site = get_current_site();
    	if ($blog != $current_site-&gt;id) {
    		switch_to_blog($blog);
    		$need_to_switch_back = true;
    	}
     
    	delete_post_meta($id, 'votes');
    	delete_post_meta($id, 'thevoters');
    	$currentvotes = get_post_meta($id, '_votes', true);
    	$voters = get_post_meta($id, '_thevoters', true);
    	$voters = explode(",", $voters);
    	foreach($voters as $voter) {
    		if($voter == $user_ID) $alreadyVoted = true;
    	}
     
    	if(!$currentvotes) $currentvotes = 0;
    	echo ''.$currentvotes.'';
    	if($user_ID &amp;&amp; !$alreadyVoted) echo '<a>'.__("Vote").'</a>';
    	if($user_ID &amp;&amp; $alreadyVoted) echo ''.__("Voted").'';
    	echo '';
    	if(!$user_ID) echo '<a href="'.get_bloginfo('url').'/wp-login.php?action=register">'.__('Register').'</a> '.__('to vote').'.';
     
    	if ($need_to_switch_back) restore_current_blog();
    }
    • Justin Broglie on September 1, 2010 at 11:42 am

      Whoops, I think it may have cut off the first code blurb, I’ll try again:

      $file = dirname(__FILE__);
      $file = substr($file, 0, stripos($file, "wp-content") );
       
      require( $file . "/wp-load.php");
       
      $need_to_switch_back = false;
      $current_site = get_current_site();
      if ($_POST['blog'] != $current_site-&gt;id) {
      	switch_to_blog($_POST['blog']);
      	$need_to_switch_back = true;
      }
       
      $currentvotes = get_post_meta($_POST['post'], '_votes', true);
      $currentvotes = $currentvotes + 1;
       
      $voters = get_post_meta($_POST['post'], '_thevoters', true);
      if(!$voters) $voters = $_POST['user']; else $voters = $voters.",".$_POST['user'];
       
      update_post_meta($_POST['post'], '_votes', $currentvotes);
      update_post_meta($_POST['post'], '_thevoters', $voters);
       
      if ($need_to_switch_back) restore_current_blog();
       
      echo $currentvotes;
  16. Kev on October 2, 2010 at 7:37 pm

    Is there any way to display the most voted posts in the last 24 hours? Or simply display the most voted posts?

    Thanks for this great plugin!!

    • c.bavota on October 4, 2010 at 7:58 pm

      You would have to create another custom field value that would add up the votes, and then be reset every day at midnight.

  17. Saurabh Shukla on October 28, 2010 at 4:19 pm

    Hi,

    I was going to write some code to run a contest where 10 selected posts on the site could be put to a vote by the members and decide a winner.

    Unlike every other time I searched google, I stubled upon this post. By tweaking the code a bit (I just had to make sure that a user could vote only once, across the posts), I was able to achieve a day long task in half an hour.

    This was a great help! Thanks a lot man!

    • Carson on March 1, 2011 at 8:33 am

      That’s what I need to do! What did you change in the code to limit it to one vote across all posts?

  18. ndattai on November 29, 2010 at 4:14 am

    great tutorial. Added to my bookamrk

    Thanks a lot!

  19. Lynn on January 14, 2011 at 10:49 am

    Probably a stupid question.. but could anybody help me out with getting three separate options?

    • c.bavota on January 14, 2011 at 12:20 pm

      Explain yourself a bit more and I’ll see what I can do.

  20. Lynne on January 17, 2011 at 2:44 pm

    The website I’m working on needs a 3-way voting system which has been a real headache to find. I like the simplicity of this code, but to utilize it for my needs it would have to have three separate vote options. Like “Good” “Okay” and “Bad”.

    • Lynne on January 17, 2011 at 11:44 pm

      Hey don’t go trying to code this. I found a plugin

  21. Lisa on February 11, 2011 at 3:45 pm

    Terrific but simple solution and exactly what most of the people actually need. do you also have any advanced version of this voting function? some kind of reports etc?

  22. Carson on March 1, 2011 at 8:44 am

    Is there somewhere you can go to see the list of people that have voted? Even if it’s in the MySQL. I’m hoping I can view or export the list because people that vote will be entered into a contest.

    • Carson on March 1, 2011 at 9:26 am

      Found a way to find everything in the database. Use the following SQL query:

      SELECT * FROM 'your_tbl' WHERE meta_key='thevoters'

      That will get all of the voters user IDs. I changed the code a bit in the functions.php file so the username would show up instead – making it easier to track. To find the votes per post, change ‘thevoters’ to ‘votes’

  23. Daydream on March 20, 2011 at 10:24 pm

    Is there a way to prevent people from voting more than once across the site, to prevent vote rigging?

    Thanks for the script!

    • c.bavota on March 22, 2011 at 11:23 am

      You would have to set a cookie.

  24. Mike Schinkel on March 29, 2011 at 2:54 am

    I was pointed to this post by comments on my answer here: http://wordpress.stackexchange.com/questions/13260/is-it-a-bad-practice-to-go-directly-to-the-mysql-database-while-developing-a-plug/13266#13266

    Sorry for being the bearer of bad news, but…I wanted to point out several problems with the approach written about in this post.

    First, there is no concern for security. A neophyte hacker could easily write a script to up-vote any posts and/or to corrupt your user voting counts. They could also possibly even use it to corrupt your ntire database since you don’t escape any of the $_POST values. You really should be using nonces (http://codex.wordpress.org/WordPress_Nonces) and you should be escaping all values posted (http://markjaquith.wordpress.com/2009/06/12/escaping-api-updates-for-wordpress-2-8/).

    Also, you’ve be safer to use the built-in AJAX functionality from WordPress that has been security hardened (http://codex.wordpress.org/AJAX_in_Plugins) instead of rolling your own.

    Further your approach of storing a comma-separated list of user_IDs won’t scale for a high-traffic site as some posts might have 25,000 votes. You’d be better off putting post_IDs in user meta although there are still even better ways (see my updated answer on WordPress Answers for more.)

    Lastly, your approach of getting, incrementing, then updating post meta can significantly undercount votes on a high-traffic site. You really need to use a different technique to count votes. There are several approaches to solving the undercounting problem such as a scheduled task that collects votes from usermeta where you add new usermeta records for each vote; that way there would be only ever one process doing the updating. But there are other ways too.

    Whatever the case, you owe it to your readers to add a note to the top of this post saying your technique is unsecure, doesn’t scale, and can undercount until you’ve had a chance to update your post with a version that does not have these issues.

    • c.bavota on March 29, 2011 at 1:00 pm

      You are 100% right. I wrote this way back before I discovered the built-in Ajax functionality in WordPress. Expect a redux shortly. Thanks for taking the time to point out the deficiencies in the script.

    • Nick Budden on May 11, 2011 at 6:06 pm

      Thanks for the great tutorial. I was just about to start writing my own plugin because vote it up wasn’t able to easily sort posts by vote. I read that you are thinking about updating this tutorial, would you suggest that I wait before starting my own plugin (I’m sure your solution will be better than mine)? When do you think you may be updating your approach? Thanks again.

    • Nick Budden on May 25, 2011 at 5:35 pm

      Went ahead with modifying your code to come up with my own plugin, just wanted to thank you for sharing the code. Saved my site.

  25. Stanley warri on May 11, 2011 at 9:58 am

    When is the update of this plugin coming. I really need this as of yesterday. Please kindly bring a new update.

    Thanks