How To Design and Create a PHP Powered Poll

Published by Chris Coyier

Polls are fun! They can help engage the readers of your site and give both you and the poll-taker valuable information. Let's walk through building a poll from scratch. All the way from the Photoshop design to the PHP / MySQL that powers it. Here is what we will build:

View Demo   Download Files

1. Design the Background in Photoshop

Create a brand new Photoshop document. In mine, I have filled the background with a dark blue (#233743) and have it sized at 700x700px.

Then create a brand new layer (press the little page icon in the layers palette) on top of your background layer. Select the gradient tool (sub tool of the paint bucket tool). Make sure you have the gradient tool set to Foreground to Transparent, radial, and full opacity like this:

In mine, I used a slightly lighter blue color (#364c5a) and drug out a gradient from near the top middle. The gradient can go "off" the top, but make sure it doesn't go off either side or the bottom. The idea here is that we are going to center this over a background of a matching blue, so we don't want the gradient to end abruptly. The reason we are making this on it's own layer is so we can nudge it around without having to re-do it.

Now let's add the fun "POLL!" text at the top. Here I used the font Agenda Black (one of my new favs) in an even lighter shade of blue (#e3f1fa). I free-transformed it (Command-T) in order to rotate it a bit and then gave it a slight drop shadow in the layer styles.

Now the graphic is ready to be saved out. You can "Save for Web & Devices" from the file menu. Use a high quality JPG setting (best for larger images with gradients). Name the file "page-bg.jpg" and save it into an "images" folder inside a directory you will use for this project.

 

2. Building the Page Structure

A poll, in our case, is just a really simple form. Basically a series of radio button inputs and a submit button. Here is what the entire HTML markup looks like:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Poll</title>
<link href="style.css" rel="stylesheet" type="text/css" />
</head>
<body>
<fieldset>
	<legend>What is your JavaScript library of choice?</legend>
	<form action="<?php echo $editFormAction; ?>" id="form1" name="form1" method="POST">
		<label>
			<input type="radio" name="Poll" value="mootools" id="Poll_0" />
			Mootools
		 </label>
		<label>
			<input type="radio" name="Poll" value="prototype" id="Poll_1" />
			Prototype
		</label>
		<label>
			<input type="radio" name="Poll" value="jquery" id="Poll_2" />
			jQuery
		</label>
		<label>
			<input type="radio" name="Poll" value="spry" id="Poll_3" />
			Spry
		</label>
		<label>
			<input type="radio" name="Poll" value="other" id="Poll_4" />
			Other
		</label>
		<input type="submit" name="submit" id="submit" value="Vote" />
		<input type="hidden" name="id" value="form1" />
		<input type="hidden" name="MM_insert" value="form1" />
	</form>
</fieldset>
</body>
</html>

Few things to note in the markup here. I put my form inside a fieldset. Nothing functionality related here, I just like how the fieldset/legend combination looks and gives us a hook for some CSS styling. Also notice how the inputs are inside the label elements. This allows users to click on the words as well as the radio button to select it, which is nice. Also notice the bit of PHP in the action for the form, we'll get to that later.

Here is the CSS:

* { 
	margin: 0; 
	padding: 0; 
}
body { 
	font-size: 62.5%; 
	font-family: Georgia, serif;
	background: url(images/page-bg.jpg) top center no-repeat #233743; 
}
h6 {
	font-size: 1.4em;
	margin-bottom: 15px;
}
a { color: white; }
label, li {
	display: block;
	padding: 5px;
	font-size: 1.4em;
	color: #e3f1fa;
}
fieldset {
	margin: 115px auto;
	width: 400px;
	padding: 8px 15px 15px 15px;
	border: 1px solid white;
	display: block; /* IE 7 Requires This */
	}
	legend {
		padding: 4px 6px 4px 6px;
		border: 1px solid white;
		font-size: 2.0em;
		color: #e3f1fa;
		font-style: italic;
	}
ul { list-style: none; margin-bottom: 15px;}
.results-bar {
	padding: 10px;
	color: white;
	background: url(images/result-bar-bg.png) left center;
	white-space: nowrap;
}
span.total-votes {
	font-size: 2.6em;
}

Notice the styles there at the bottom for things that aren't in our markup yet, those are for the Results page which we'll get to later.

 

3. Create a Database to Store Results

Most hosting packages allow you to create databases on your server. If you don't already know how, you may need to contact them or search around their help areas to find out how to add a new one.

CSS-Tricks is on Media Temple, so there is a handy little tool right in the domain admin area for creating new databases:

Make sure this is a MySQL database. The four things you will need to know are the hostname, the database username, the database "password", and the name of the database.

Now you will need to create a new file in the directory you have started for this project called "conn_vote.php". I have put mine inside a subfolder called "Connections". Here is the PHP:

<?php
# FileName="Connection_php_mysql.htm"
# Type="MYSQL"
# HTTP="true"
$hostname_conn_vote = "localhost";
$database_conn_vote = "your-database-name";
$username_conn_vote = "your-database-username";
$password_conn_vote = "your-database-password";
//$conn_vote = mysql_pconnect($hostname_conn_vote, $username_conn_vote, $password_conn_vote) or trigger_error(mysql_error(),E_USER_ERROR);
$conn_vote = mysql_connect($hostname_conn_vote, $username_conn_vote, $password_conn_vote) or die('Can\'t create connection: '.mysql_error());
mysql_select_db($database_conn_vote, $conn_vote) or die('Can\'t access specified db: '.mysql_error());
?>

Notice the four lines in bold above, those are the four variables for the four things I said you would need to know. Chances are good that your host is going to be localhost, but it isn't always. In the case of Media Temple like me, it's something like this: internal-db.s12345.gridserver.com

Your brand new empty database is going to need a table structure and some fake data to get started. Here is some SQL you can run to get that done:

-- 
-- Table structure for table `poll`
-- 
CREATE TABLE `poll` (
  `id` int(3) NOT NULL auto_increment,
  `question` varchar(200) default NULL,
  PRIMARY KEY  (`id`)
) TYPE=MyISAM AUTO_INCREMENT=43 ;
-- 
-- Dumping fake data for table `poll`
-- 
INSERT INTO `poll` VALUES (42, 'jquery');
INSERT INTO `poll` VALUES (41, 'mootools');
INSERT INTO `poll` VALUES (40, 'other');
INSERT INTO `poll` VALUES (39, 'mootools');
INSERT INTO `poll` VALUES (38, 'jquery');
INSERT INTO `poll` VALUES (37, 'mootools');
INSERT INTO `poll` VALUES (36, 'spry');
INSERT INTO `poll` VALUES (35, 'jquery');
INSERT INTO `poll` VALUES (21, 'mootools');
INSERT INTO `poll` VALUES (22, 'other');
INSERT INTO `poll` VALUES (23, 'mootools');
INSERT INTO `poll` VALUES (24, 'mootools');
INSERT INTO `poll` VALUES (25, 'prototype');
INSERT INTO `poll` VALUES (26, 'other');
INSERT INTO `poll` VALUES (27, 'mootools');
INSERT INTO `poll` VALUES (28, 'spry');
INSERT INTO `poll` VALUES (29, 'jquery');
INSERT INTO `poll` VALUES (30, 'mootools');
INSERT INTO `poll` VALUES (31, 'prototype');
INSERT INTO `poll` VALUES (32, 'mootools');
INSERT INTO `poll` VALUES (33, 'mootools');
INSERT INTO `poll` VALUES (34, 'mootools');

Most hosts also give you access to phpMyAdmin for running stuff like this. Make sure you are on your new database and then you can paste in that SQL and run it.

 

4. Make it Work with PHP

Now we are ready for some PHP coding magic to make this all work. I can't pretend to understand all this, but the gist of it is that it will take your selected option, turn it into a nice readable and safe string value, and save it as a new entry in that table in our DB.

Huge thanks to Jonathan Feanfor the PHP that powers this thing and David Walsh for helping me work out some kinks and get it up and running on my server.

Insert this PHP code at the top (even before the DOCTYPE) of your poll.php file:

<?php require_once('Connections/conn_vote.php'); ?>
<?php
if (!function_exists("GetSQLValueString")) {
function GetSQLValueString($theValue, $theType, $theDefinedValue = "", $theNotDefinedValue = "") 
{
  $theValue = get_magic_quotes_gpc() ? stripslashes($theValue) : $theValue;

  $theValue = function_exists("mysql_real_escape_string") ? mysql_real_escape_string($theValue) : mysql_escape_string($theValue);

  switch ($theType) {
    case "text":
      $theValue = ($theValue != "") ? "'" . $theValue . "'" : "NULL";
      break;    
    case "long":
    case "int":
      $theValue = ($theValue != "") ? intval($theValue) : "NULL";
      break;
    case "double":
      $theValue = ($theValue != "") ? "'" . doubleval($theValue) . "'" : "NULL";
      break;
    case "date":
      $theValue = ($theValue != "") ? "'" . $theValue . "'" : "NULL";
      break;
    case "defined":
      $theValue = ($theValue != "") ? $theDefinedValue : $theNotDefinedValue;
      break;
  }
  return $theValue;
}
}

$editFormAction = $_SERVER['PHP_SELF'];
if (isset($_SERVER['QUERY_STRING'])) {
  $editFormAction .= "?" . htmlentities($_SERVER['QUERY_STRING']);
}

if ((isset($_POST["MM_insert"])) && ($_POST["MM_insert"] == "form1")) {
  $insertSQL = sprintf("INSERT INTO poll (id, question) VALUES (%s, %s)",
                       GetSQLValueString($_POST['id'], "int"),
                       GetSQLValueString($_POST['Poll'], "text"));

  mysql_select_db($database_conn_vote, $conn_vote);
  $Result1 = mysql_query($insertSQL, $conn_vote) or die(mysql_error());

  $insertGoTo = "results.php";
  if (isset($_SERVER['QUERY_STRING'])) {
    $insertGoTo .= (strpos($insertGoTo, '?')) ? "&" : "?";
    $insertGoTo .= $_SERVER['QUERY_STRING'];
  }
  header(sprintf("Location: %s", $insertGoTo));
}

$colname_rs_vote = "-1";
if (isset($_GET['recordID'])) {
  $colname_rs_vote = $_GET['recordID'];
}
mysql_select_db($database_conn_vote, $conn_vote);
$query_rs_vote = sprintf("SELECT * FROM poll WHERE id = %s", GetSQLValueString($colname_rs_vote, "int"));
$rs_vote = mysql_query($query_rs_vote, $conn_vote) or die(mysql_error());
$row_rs_vote = mysql_fetch_assoc($rs_vote);
$totalRows_rs_vote = mysql_num_rows($rs_vote);
?>

And this PHP at the very end of the poll.php file (even after the <html>):

<?php
mysql_free_result($rs_vote);
?>

5. Create a Results Page

So our polls is up and working and successfully gathering votes, but the results page is the true payoff! Not only is this the fun part, but our PHP function auto-re-directs to a "results.php" page after you press the vote button, so we kinda have to build one =)

This time I will give you all the markup and the PHP together in one big lump:

<?php require_once('Connections/conn_vote.php'); ?>
<?php
if (!function_exists("GetSQLValueString")) {
function GetSQLValueString($theValue, $theType, $theDefinedValue = "", $theNotDefinedValue = "") 
{
  $theValue = get_magic_quotes_gpc() ? stripslashes($theValue) : $theValue;

  $theValue = function_exists("mysql_real_escape_string") ? mysql_real_escape_string($theValue) : mysql_escape_string($theValue);

  switch ($theType) {
    case "text":
      $theValue = ($theValue != "") ? "'" . $theValue . "'" : "NULL";
      break;    
    case "long":
    case "int":
      $theValue = ($theValue != "") ? intval($theValue) : "NULL";
      break;
    case "double":
      $theValue = ($theValue != "") ? "'" . doubleval($theValue) . "'" : "NULL";
      break;
    case "date":
      $theValue = ($theValue != "") ? "'" . $theValue . "'" : "NULL";
      break;
    case "defined":
      $theValue = ($theValue != "") ? $theDefinedValue : $theNotDefinedValue;
      break;
  }
  return $theValue;
}
}

mysql_select_db($database_conn_vote, $conn_vote);
$query_rs_vote = "SELECT * FROM poll";
$rs_vote = mysql_query($query_rs_vote, $conn_vote) or die(mysql_error());
$row_rs_vote = mysql_fetch_assoc($rs_vote);
$totalRows_rs_vote = mysql_num_rows($rs_vote);

$resultQuestion1 = mysql_query("SELECT * FROM poll WHERE question='mootools'");
$num_rowsQuestion1 = mysql_num_rows($resultQuestion1);

$resultQuestion2 = mysql_query("SELECT * FROM poll WHERE question='prototype'");
$num_rowsQuestion2 = mysql_num_rows($resultQuestion2);

$resultQuestion3 = mysql_query("SELECT * FROM poll WHERE question='jquery'");
$num_rowsQuestion3 = mysql_num_rows($resultQuestion3);

$resultQuestion4 = mysql_query("SELECT * FROM poll WHERE question='spry'");
$num_rowsQuestion4 = mysql_num_rows($resultQuestion4);

$resultQuestion5 = mysql_query("SELECT * FROM poll WHERE question='other'");
$num_rowsQuestion5 = mysql_num_rows($resultQuestion5);

$percentQuestion1 = ($num_rowsQuestion1 / $totalRows_rs_vote)*100;
$percentQuestion2 = ($num_rowsQuestion2 / $totalRows_rs_vote)*100;
$percentQuestion3 = ($num_rowsQuestion3 / $totalRows_rs_vote)*100;
$percentQuestion4 = ($num_rowsQuestion4 / $totalRows_rs_vote)*100;
$percentQuestion5 = ($num_rowsQuestion5 / $totalRows_rs_vote)*100;

?>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">

<head>
	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
	<title>Results</title>
	<link href="style.css" rel="stylesheet" type="text/css" />
</head>

<body>
	<fieldset>
	
		<legend>Results</legend>
		
		<ul>
			<li>
				<span class="total-votes"><?php echo $num_rowsQuestion1 ?></span> Mootools
				<br />
				<div class="results-bar" style="width: <?php echo round($percentQuestion1,2); ?>%;">
					 <?php echo round($percentQuestion1,2); ?>%
				</div>
			</li>
			
			<li>
				<span class="total-votes"><?php echo $num_rowsQuestion2 ?></span> Prototype
				<div class="results-bar" style="width: <?php echo round($percentQuestion2,2); ?>%;">
					 <?php echo round($percentQuestion2,2); ?>%
				</div>
			</li>
		
			<li>
				<span class="total-votes"><?php echo $num_rowsQuestion3 ?></span> jQuery
				<div class="results-bar" style="width: <?php echo round($percentQuestion3,2); ?>%;">
					 <?php echo round($percentQuestion3,2); ?>%
				</div>
			</li>
		
			<li>
				<span class="total-votes"><?php echo $num_rowsQuestion4 ?></span> Spry
				<div class="results-bar" style="width: <?php echo round($percentQuestion4,2); ?>%;">
					 <?php echo round($percentQuestion4,2); ?>%
				</div>
			</li>
		
			<li>
				<span class="total-votes"><?php echo $num_rowsQuestion5 ?></span> Other
				<div class="results-bar" style="width: <?php echo round($percentQuestion5,2); ?>%;">
					 <?php echo round($percentQuestion5,2); ?>%
				</div>
			</li>
		</ul>
	
		<h6>Total votes: <?php echo $totalRows_rs_vote ?></h6>
		
		<a href="poll.php">Back to Voting</a>
	
	</fieldset>
	
</body>
</html>

<?php
mysql_free_result($rs_vote);
?>

Notice there is a little math being done in the PHP which calculates the percentage of the total votes for each poll option. Not only is this good information, but we can use that percentage to set the width of a bar to add some visual flair to our results. Each poll option has it's own list item where the total votes for that item is displayed along with a "results-bar", who's width is determined by setting an inline width value to the percentage calculated. Back in our CSS, we already have that div styled up with a subtle patterned background image.

View Demo   Download Files

(Remember: this is requires a server running PHP and a MySQL database so you'll need to follow the steps above to have it work on your own server. Photoshop file included.)