In this tutorial, we'll develop a secure poll and voting system using PHP and MySQL. This system will allow you to interact with your audience and display a collection of polls. You'll learn to create polls, implement a voting system, delete polls, and display the list of published polls.

A poll and voting system lets people share their opinions on a question by choosing from several answer options. Users select their choice, and the system counts all the votes to show the overall results. This is often used in surveys, market research, and online platforms to gather feedback and understand what people think about various topics.

During poll creation, you can specify multiple answers, as they will be stored in a separate database table. One table will store poll-related data (title, description, etc.), while the other will store the answers, linking both to display the poll list.

The Advanced package includes additional features and a download link to the source code.

1. Getting Started

Before we jump into programming our poll and voting system, there are a few requirements that need to be met. We need to install the development tools and set up the file structure for our app.

1.1. What You Will Learn in this Tutorial

  • Form Design — Design a Poll and Voting app with HTML5 and CSS3.
  • Poll System — Create a working poll system with PHP & MySQL (create polls, delete polls, and view polls).
  • Voting System — Each poll will consist of answers that the user can select to cast a vote and subsequently view the result.
  • MySQL Database Interaction — Interact with a MySQL database using the PHP PDO interface. All data entered during the creation phase will be stored in the MySQL database.
  • Basic Template System — We'll create a basic template system for our app, which will consist of header and footer functions. Those functions can then be implemented on all the pages we create. It's so we don't have to write the same code over and over.

1.2. Requirements

  • Download and install XAMPP — XAMPP is a web server that includes the essential software for web developers (PHP, MySQL, Apache, etc). Skip this step if you already have a development server installed.

1.3. File Structure & Setup

Navigate to your XAMPP htdocs directory (usually located at C:\xampp\htdocs) and create the following files and directories:

File Structure

\-- phppoll
    |-- functions.php
    |-- index.php
    |-- create.php
    |-- vote.php
    |-- result.php
    |-- delete.php
    |-- style.css

Each file will contain the following:

  • functions.php — The functions file will contain the template and database connection functions.
  • index.php — The index page will contain the list of published polls and the navigation buttons.
  • create.php — The create page will contain form input fields, which we can use to create new polls.
  • vote.php — The vote page will consist of poll answers with the option to cast a vote.
  • result.php — The result page will show the results for the specified poll, while each answer will show the number of votes and the percentage bar.
  • style.css — The stylesheet (CSS3) for our poll and voting system.

2. Creating the Database and setting-up Tables

If you have installed XAMPP, you can create the MySQL database with phpMyAdmin. Although, you need to make sure you start your web server: open the XAMPP control panel and click the Start button for both Apache and MySQL.

  • Navigate to http://localhost/phpmyadmin/ in your browser.
  • Click the SQL tab at the top and execute the following SQL statement:
CREATE DATABASE IF NOT EXISTS `phppoll` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
USE `phppoll`;

CREATE TABLE IF NOT EXISTS `polls` (
	`id` int(11) NOT NULL AUTO_INCREMENT,
	`title` text NOT NULL,
	`description` text NOT NULL,
	PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

INSERT INTO `polls` (`id`, `title`, `description`) VALUES (1, 'What''s your favorite programming language?', '');

CREATE TABLE IF NOT EXISTS `poll_answers` (
	`id` int(11) NOT NULL AUTO_INCREMENT,
	`poll_id` int(11) NOT NULL,
	`title` text NOT NULL,
	`votes` int(11) NOT NULL DEFAULT '0',
	PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;

INSERT INTO `poll_answers` (`id`, `poll_id`, `title`, `votes`) VALUES (1, 1, 'PHP', 0), (2, 1, 'Python', 0), (3, 1, 'C#', 0), (4, 1, 'Java', 0);

In phpMyAdmin, our database should resemble the following:

MySQL Poll Database

A summary of each table and the columns associated with them:

  • polls table — This table will contain information related to the polls we create (title and description).
    • id — The unique ID for the poll, which will be auto-incremented, meaning the number will increase as more rows are created.
    • title — The title of the poll, which could be a question, etc.
    • description — The description of the poll, which is optional during the creation phase.
  • poll_answers table — This table will contain all the answers for our created polls.
    • id — Unique ID, which will be auto incremented.
    • poll_id — The poll ID, which will be associated with the id column in the polls table. It's how we can relate both tables.
    • title — The title of the poll answer.
    • votes — The number of votes the answer has.

To make sure the database has been successfully imported, you can check on phpMyAdmin — click the database name in the left side navigation panel and you should see the following:

phpMyAdmin Poll Database

3. Creating the Stylesheet (CSS3)

The stylesheet will format the structure of our poll and voting system and make it look more appealing. Add the following CSS code to the style.css file:

* {
      box-sizing: border-box;
      font-family: -apple-system, BlinkMacSystemFont, "segoe ui", roboto, oxygen, ubuntu, cantarell, "fira sans", "droid sans", "helvetica neue", Arial, sans-serif;
      font-size: 16px;
}
body {
      background-color: #FFFFFF;
      margin: 0;
}
.navtop {
      background-color: #3f69a8;
      height: 60px;
      width: 100%;
      border: 0;
}
.navtop div {
      display: flex;
      margin: 0 auto;
      width: 1000px;
      height: 100%;
}
.navtop div h1, .navtop div a {
      display: inline-flex;
      align-items: center;
}
.navtop div h1 {
      flex: 1;
      font-size: 24px;
      padding: 0;
      margin: 0;
      color: #ecf0f6;
      font-weight: normal;
}
.navtop div a {
      padding: 0 20px;
      text-decoration: none;
      color: #c5d2e5;
      font-weight: bold;
}
.navtop div a i {
      padding: 2px 8px 0 0;
}
.navtop div a:hover {
      color: #ecf0f6;
}
.content {
      width: 1000px;
      margin: 0 auto;
}
.content h2 {
      margin: 0;
      padding: 25px 0;
      font-size: 22px;
      border-bottom: 1px solid #ebebeb;
      color: #666666;
}
.home .create-poll {
      display: inline-block;
      text-decoration: none;
      background-color: #38b673;
      font-weight: bold;
      font-size: 14px;
      border-radius: 5px;
      color: #FFFFFF;
      padding: 10px 15px;
      margin: 15px 0;
}
.home .create-poll:hover {
      background-color: #32a367;
}
.home table {
      width: 100%;
      padding-top: 30px;
      border-collapse: collapse;
}
.home table thead {
      background-color: #ebeef1;
      border-bottom: 1px solid #d3dae0;
}
.home table thead td {
      padding: 10px;
      font-weight: bold;
      color: #767779;
      font-size: 14px;
}
.home table tbody tr {
      border-bottom: 1px solid #d3dae0;
}
.home table tbody tr:nth-child(even) {
      background-color: #fbfcfc;
}
.home table tbody tr:hover {
      background-color: #376ab7;
}
.home table tbody tr:hover td {
      color: #FFFFFF;
}
.home table tbody tr:hover td:nth-child(1) {
      color: #FFFFFF;
}
.home table tbody tr td {
      padding: 10px;
}
.home table tbody tr td:nth-child(1) {
      color: #a5a7a9;
}
.home table tbody tr td.actions {
      padding: 8px;
      text-align: right;
}
.home table tbody tr td.actions .view, .home table tbody tr td.actions .edit, .home table tbody tr td.actions .trash {
      display: inline-flex;
      text-align: right;
      text-decoration: none;
      color: #FFFFFF;
      padding: 10px 12px;
      border-radius: 5px;
}
.home table tbody tr td.actions .trash {
      background-color: #b73737;
}
.home table tbody tr td.actions .trash:hover {
      background-color: #a33131;
}
.home table tbody tr td.actions .edit {
      background-color: #37afb7;
}
.home table tbody tr td.actions .edit:hover {
      background-color: #319ca3;
}
.home table tbody tr td.actions .view {
      background-color: #37b770;
}
.home table tbody tr td.actions .view:hover {
      background-color: #31a364;
}
.update form {
      padding: 15px 0;
      display: flex;
      flex-flow: column;
      width: 400px;
}
.update form label {
      display: inline-flex;
      width: 100%;
      padding: 10px 0;
      margin-right: 25px;
}
.update form input, .update form textarea {
      padding: 10px;
      width: 100%;
      margin-right: 25px;
      margin-bottom: 15px;
      border: 1px solid #cccccc;
}
.update form textarea {
      height: 200px;
}
.update form input[type="submit"] {
      display: block;
      background-color: #38b673;
      border: 0;
      font-weight: bold;
      font-size: 14px;
      color: #FFFFFF;
      cursor: pointer;
      width: 200px;
      margin-top: 15px;
      border-radius: 5px;
}
.update form input[type="submit"]:hover {
      background-color: #32a367;
}
.delete .yesno {
      display: flex;
}
.delete .yesno a {
      display: inline-block;
      text-decoration: none;
      background-color: #38b673;
      font-weight: bold;
      color: #FFFFFF;
      padding: 10px 15px;
      margin: 15px 10px 15px 0;
      border-radius: 5px;
}
.delete .yesno a:hover {
      background-color: #32a367;
}
.poll-vote form {
      display: flex;
      flex-flow: column;
}
.poll-vote form label {
      padding-bottom: 10px;
}
.poll-vote form input[type="radio"] {
      transform: scale(1.1);
}
.poll-vote form input[type="submit"], .poll-vote form a {
      display: inline-block;
      padding: 8px;
      border-radius: 5px;
      background-color: #38b673;
      border: 0;
      font-weight: bold;
      font-size: 14px;
      color: #FFFFFF;
      cursor: pointer;
      width: 150px;
      margin-top: 15px;
}
.poll-vote form input[type="submit"]:hover, .poll-vote form a:hover {
      background-color: #32a367;
}
.poll-vote form a {
      text-align: center;
      text-decoration: none;
      background-color: #37afb7;
      margin-left: 5px;
}
.poll-vote form a:hover {
      background-color: #319ca3;
}
.poll-result .wrapper {
      display: flex;
      flex-flow: column;
}
.poll-result .wrapper .poll-question {
      width: 50%;
      padding-bottom: 5px;
}
.poll-result .wrapper .poll-question p {
      margin: 0;
      padding: 5px 0;
}
.poll-result .wrapper .poll-question p span {
      font-size: 14px;
}
.poll-result .wrapper .poll-question .result-bar {
      display: flex;
      height: 25px;
      min-width: 5%;
      background-color: #38b673;
      border-radius: 5px;
      font-size: 14px;
      color: #FFFFFF;
      justify-content: center;
      align-items: center;
}

Feel free to customize it or use your own stylesheet.

4. Creating the Poll and Voting System with PHP

We can finally start programming our poll and voting system with PHP.

4.1. Functions

The functions.php file will contain the template and database connection functions, which we can implement in all the pages that we create.

Edit the functions.php file and add:

<?php
function pdo_connect_mysql() {
    // Update the details below with your MySQL details
    $DATABASE_HOST = 'localhost';
    $DATABASE_USER = 'root';
    $DATABASE_PASS = '';
    $DATABASE_NAME = 'phppoll';
    try {
    	return new PDO('mysql:host=' . $DATABASE_HOST . ';dbname=' . $DATABASE_NAME . ';charset=utf8', $DATABASE_USER, $DATABASE_PASS);
    } catch (PDOException $exception) {
    	// If there is an error with the connection, stop the script and display the error.
    	exit('Failed to connect to database!');
    }
}

Every time we want to connect to our MySQL database, all we have to do is execute the above function, like the following:

pdo_connect_mysql();

If you encounter a connection issue with MySQL, you will most likely have to update the database variables to reflect your MySQL credentials and database name. You shouldn't have to change the variables if you're using XAMPP.

Add after:

// Template header, feel free to customize it, but DO NOT INDENT THE PHP CODE
function template_header($title) {
// DO NOT INDENT THE BELOW PHP CODE OR YOU WILL ENCOUNTER ISSUES
echo <<<EOT
<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>$title</title>
		<link href="style.css" rel="stylesheet" type="text/css">
		<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.1/css/all.css">
	</head>
	<body>
    <nav class="navtop">
    	<div>
    		<h1>Voting & Poll System</h1>
            <a href="index.php"><i class="fas fa-poll-h"></i>Polls</a>
    	</div>
    </nav>
EOT;
}

The header function for our templates, which includes the head section of the document and the top navigation bar that will appear on every page. We also include the Font Awesome library, which is a free icon library (icons we'll be using in our app).

Add after:

// Template footer
function template_footer() {
// DO NOT INDENT THE PHP CODE
echo <<<EOT
    </body>
</html>
EOT;
}

The template footer function, which is basically the end of the document (closing the HTML tags, etc).

And now, if we wanted to create a new page, we could implement code like so:

<?php
// examplepage.php
include 'functions.php';
$pdo = pdo_connect_mysql();
?>
<?=template_header('Example Page')?>

<p>Hello World! Welcome to my custom page!</p>

<?=template_footer()?>

Awesome, right? Now, we don't have to include the same template function code and connection function code in all our PHP files, as all we have to do is execute the function.

Take note the following code:

<?=template_footer()?>

Is the short version of:

<?php echo template_footer(); ?>

That's all the functions created that we're going to be using.

4.2. Index Page

The index page will consist of all the populated lists of polls, along with buttons that we can utilize to view and delete polls.

Edit the index.php file and add:

<?php
// Include the function file
include 'functions.php';
// Connect to MySQL
$pdo = pdo_connect_mysql();
// MySQL query that retrieves all the polls and poll answers
$stmt = $pdo->query('SELECT p.*, GROUP_CONCAT(pa.title ORDER BY pa.id) AS answers FROM polls p LEFT JOIN poll_answers pa ON pa.poll_id = p.id GROUP BY p.id');
$polls = $stmt->fetchAll(PDO::FETCH_ASSOC);
?>

The above code will retrieve all the polls and poll answers from our database tables, which we can then subsequently populate a list in HTML table format.

Add after:

<?=template_header('Polls')?>

<div class="content home">
	<h2>Polls</h2>
	<p>Welcome to the home page! You can view the list of polls below.</p>
	<a href="create.php" class="create-poll">Create Poll</a>
	<table>
        <thead>
            <tr>
                <td>#</td>
                <td>Title</td>
				<td>Answers</td>
                <td></td>
            </tr>
        </thead>
        <tbody>
            <?php foreach($polls as $poll): ?>
            <tr>
                <td><?=$poll['id']?></td>
                <td><?=$poll['title']?></td>
				<td><?=$poll['answers']?></td>
                <td class="actions">
					<a href="vote.php?id=<?=$poll['id']?>" class="view" title="View Poll"><i class="fas fa-eye fa-xs"></i></a>
                    <a href="delete.php?id=<?=$poll['id']?>" class="trash" title="Delete Poll"><i class="fas fa-trash fa-xs"></i></a>
                </td>
            </tr>
            <?php endforeach; ?>
        </tbody>
    </table>
</div>

<?=template_footer()?>

First and foremost, we start the above code by executing the template header function (template_header()), and subsequently iterate the polls using a foreach loop and populate them in an HTML table, and lastly, we end the template with the template footer function (template_footer()).

With the HTML anchor links in the table, you can see we're going to pass the ID parameter (using a GET request) to the vote.php and delete.php pages. It's how the PHP code will know which poll the user has clicked in the table.

Tip: to create a custom GET request in PHP, you can append parameters to the PHP file in the URL, for example: contact.php?name=david&email=david@codeshack.io.

If we navigate to http://localhost/phppoll/index.php, we should see the following:

http://localhost/phppoll/index.php
PHP Poll Table List

We can now view the list of polls created on the index page. Do not be concerned about the buttons not working, as we have yet to create the pages associated with them.

4.3. Create Page

The create page we can use to create new polls using an HTML form and input fields, and on the server side, we can use PHP and MySQL to insert new records into our database tables. However, for this to happen, we first need to execute a POST request with PHP.

Edit the create.php file and add:

<?php
include 'functions.php';
$pdo = pdo_connect_mysql();
$msg = '';

We're going to need to use MySQL and use the template functions that we created previously. Therefore, we must include the functions.php file. The $msg variable will be the output message to the user.

Add after:

// Check if POST data is not empty
if (!empty($_POST)) {
    // Post data not empty insert a new record
    // Check if POST variable "title" exists, if not default the value to blank, basically the same for all variables
    $title = isset($_POST['title']) ? $_POST['title'] : '';
    $description = isset($_POST['description']) ? $_POST['description'] : '';
    // Insert new record into the "polls" table
    $stmt = $pdo->prepare('INSERT INTO polls (title, description) VALUES (?, ?)');
    $stmt->execute([ $title, $description ]);
    // Below will get the last insert ID, this will be the poll id
    $poll_id = $pdo->lastInsertId();
    // Get the answers and convert the multiline string to an array, so we can add each answer to the "poll_answers" table
    $answers = isset($_POST['answers']) ? explode(PHP_EOL, $_POST['answers']) : '';
    foreach($answers as $answer) {
        // If the answer is empty there is no need to insert
        if (empty($answer)) continue;
        // Add answer to the "poll_answers" table
        $stmt = $pdo->prepare('INSERT INTO poll_answers (poll_id, title) VALUES (?, ?)');
        $stmt->execute([ $poll_id, $answer ]);
    }
    // Output message
    $msg = 'Created Successfully!';
}
?>

The code above will only execute if the user has clicked the submit button in the HTML form, as it's a POST request. If the POST variable is not empty, insert a new record into both the polls and poll_answers tables — the number of records inserted into the poll_answers table depends on how many answers the user specified.

Not only can we insert new records, but we're also securing the user input as prepared statements will prevent SQL injection. We don't need to escape user input if we're using prepared statements.

Add After:

<?=template_header('Create Poll')?>

<div class="content update">
	<h2>Create Poll</h2>
    <form action="create.php" method="post">
        <label for="title">Title</label>
        <input type="text" name="title" id="title" placeholder="Title" required>
        <label for="description">Description</label>
        <input type="text" name="description" id="description" placeholder="Description">
        <label for="answers">Answers (per line)</label>
        <textarea name="answers" id="answers" placeholder="Description" required></textarea>
        <input type="submit" value="Create">
    </form>
    <?php if ($msg): ?>
    <p><?=$msg?></p>
    <?php endif; ?>
</div>

<?=template_footer()?>

Remember the index template we created earlier? We're technically using the same header and footer functions to structure our HTML template.

The form that we have created above we can use to insert new records into our database tables. The PHP POST variable names reflect the names of the elements in the HTML form. The forms method is set to post because we need to make a POST request.

And now, if we click the Create Poll button on the index page, we'll see the following:

http://localhost/phppoll/create.php
PHP Create Poll Form

That's all we need to do to insert new records into our database tables.

4.4. Vote Page

On the voting page, users will be able to see the populated list of answers for the specified poll and have the option to vote. They can also see the result without voting.

Edit the vote.php file and add:

<?php
include 'functions.php';
// Connect to MySQL
$pdo = pdo_connect_mysql();
// If the GET request "id" exists (poll id)...
if (isset($_GET['id'])) {
    // MySQL query that selects the poll records by the GET request "id"
    $stmt = $pdo->prepare('SELECT * FROM polls WHERE id = ?');
    $stmt->execute([ $_GET['id'] ]);
    // Fetch the record
    $poll = $stmt->fetch(PDO::FETCH_ASSOC);
    // Check if the poll record exists with the id specified
    if ($poll) {
        // MySQL query that selects all the poll answers
        $stmt = $pdo->prepare('SELECT * FROM poll_answers WHERE poll_id = ?');
        $stmt->execute([ $_GET['id'] ]);
        // Fetch all the poll anwsers
        $poll_answers = $stmt->fetchAll(PDO::FETCH_ASSOC);
        // If the user clicked the "Vote" button...
        if (isset($_POST['poll_answer'])) {
            // Update and increase the vote for the answer the user voted for
            $stmt = $pdo->prepare('UPDATE poll_answers SET votes = votes + 1 WHERE id = ?');
            $stmt->execute([ $_POST['poll_answer'] ]);
            // Redirect user to the result page
            header('Location: result.php?id=' . $_GET['id']);
            exit;
        }
    } else {
        exit('Poll with that ID does not exist.');
    }
} else {
    exit('No poll ID specified.');
}
?>

For the voting page to work correctly, the id parameter must be specified in the URL (vote.php?id=2, etc). If the id parameter exists, we can retrieve the record from our MySQL database by the id column (poll table) and the poll_id column (poll_answers table).

Not only do we make a GET request, but we also make a POST request — only if the user selects an answer and clicks the Vote button, which will subsequently update the votes for that particular answer (MySQL UPDATE query). Upon a successful POST request, the user is redirected to the result page (result.php).

Add after:

<?=template_header('Poll Vote')?>

<div class="content poll-vote">
	<h2><?=$poll['title']?></h2>
	<p><?=$poll['description']?></p>
    <form action="vote.php?id=<?=$_GET['id']?>" method="post">
        <?php for ($i = 0; $i < count($poll_answers); $i++): ?>
        <label>
            <input type="radio" name="poll_answer" value="<?=$poll_answers[$i]['id']?>"<?=$i == 0 ? ' checked' : ''?>>
            <?=$poll_answers[$i]['title']?>
        </label>
        <?php endfor; ?>
        <div>
            <input type="submit" value="Vote">
            <a href="result.php?id=<?=$poll['id']?>">View Result</a>
        </div>
    </form>
</div>

<?=template_footer()?>

The template above will iterate each answer and populate the input radio fields we need for our HTML form.

If we click the eye icon next to the test poll on the index page, we'll see the following:

http://localhost/phppoll/vote.php?id=1
PHP Poll Vote Form

We can now vote on the polls we have created.

4.5. Result Page

On the result page, the user can view the populated list of answers along with the number of votes.

Edit the result.php file and add:

<?php
include 'functions.php';
// Connect to MySQL
$pdo = pdo_connect_mysql();
// If the GET request "id" exists (poll id)...
if (isset($_GET['id'])) {
    // MySQL query that selects the poll records by the GET request "id"
    $stmt = $pdo->prepare('SELECT * FROM polls WHERE id = ?');
    $stmt->execute([ $_GET['id'] ]);
    // Fetch the record
    $poll = $stmt->fetch(PDO::FETCH_ASSOC);
    // Check if the poll record exists with the id specified
    if ($poll) {
        // MySQL Query that will get all the answers from the "poll_answers" table ordered by the number of votes (descending)
        $stmt = $pdo->prepare('SELECT * FROM poll_answers WHERE poll_id = ? ORDER BY votes DESC');
        $stmt->execute([ $_GET['id'] ]);
        // Fetch all poll answers
        $poll_answers = $stmt->fetchAll(PDO::FETCH_ASSOC);
        // Total number of votes, will be used to calculate the percentage
        $total_votes = 0;
        foreach($poll_answers as $poll_answer) {
            // Every poll answers votes will be added to total votes
            $total_votes += $poll_answer['votes'];
        }
    } else {
        exit('Poll with that ID does not exist.');
    }
} else {
    exit('No poll ID specified.');
}
?>

Similar to the voting page, we need to retrieve the ID GET parameter (result.php?id=2, etc.), and with that, we can retrieve the poll results from our database — ordered by the number of votes (descending). The answers are iterated using a foreach loop, and the total votes are counted accordingly.

Add after:

<?=template_header('Poll Results')?>

<div class="content poll-result">
	<h2><?=$poll['title']?></h2>
	<p><?=$poll['description']?></p>
    <div class="wrapper">
        <?php foreach ($poll_answers as $poll_answer): ?>
        <div class="poll-question">
            <p><?=$poll_answer['title']?> <span>(<?=$poll_answer['votes']?> Votes)</span></p>
            <div class="result-bar" style= "width:<?=@round(($poll_answer['votes']/$total_votes)*100)?>%">
                <?=@round(($poll_answer['votes']/$total_votes)*100)?>%
            </div>
        </div>
        <?php endforeach; ?>
    </div>
</div>

<?=template_footer()?>

The above template will iterate the answers and populate them in HTML format along with the number of votes and a percentage bar.

Navigate to the test poll, and you can either click Vote or click the View Results button, which you should subsequently see something like the following:

http://localhost/
PHP Poll Results

That's basically how you populate the poll results and using a bit of CSS magic to create the percentage bar.

4.6. Delete Page

On the delete page, we'll be able to delete polls — we'll include a confirmation so the user doesn't accidentally delete the wrong poll.

Edit the delete.php file and add:

<?php
include 'functions.php';
$pdo = pdo_connect_mysql();
$msg = '';
// Check that the poll ID exists
if (isset($_GET['id'])) {
    // Select the record that is going to be deleted
    $stmt = $pdo->prepare('SELECT * FROM polls WHERE id = ?');
    $stmt->execute([ $_GET['id'] ]);
    $poll = $stmt->fetch(PDO::FETCH_ASSOC);
    if (!$poll) {
        exit('Poll doesn\'t exist with that ID!');
    }
    // Make sure the user confirms beore deletion
    if (isset($_GET['confirm'])) {
        if ($_GET['confirm'] == 'yes') {
            // User clicked the "Yes" button, delete record
            $stmt = $pdo->prepare('DELETE FROM polls WHERE id = ?');
            $stmt->execute([ $_GET['id'] ]);
            // We also need to delete the answers for that poll
            $stmt = $pdo->prepare('DELETE FROM poll_answers WHERE poll_id = ?');
            $stmt->execute([ $_GET['id'] ]);
            // Output msg
            $msg = 'You have deleted the poll!';
        } else {
            // User clicked the "No" button, redirect them back to the home/index page
            header('Location: index.php');
            exit;
        }
    }
} else {
    exit('No ID specified!');
}
?>

If the poll ID is specified and exists in our polls table, we can prompt the user whether they would like to delete the poll or not. If they choose Yes, the poll will be permanently deleted along with the poll answers. The data will be deleted from both the polls and poll_answers database tables using the DELETE statement.

Add after:

<?=template_header('Delete')?>

<div class="content delete">
	<h2>Delete Poll #<?=$poll['id']?></h2>
    <?php if ($msg): ?>
    <p><?=$msg?></p>
    <?php else: ?>
	<p>Are you sure you want to delete poll #<?=$poll['id']?>?</p>
    <div class="yesno">
        <a href="delete.php?id=<?=$poll['id']?>&confirm=yes">Yes</a>
        <a href="delete.php?id=<?=$poll['id']?>&confirm=no">No</a>
    </div>
    <?php endif; ?>
</div>

<?=template_footer()?>

The above code is the template for the delete page. If we navigate to the index page and click the trash icon next to one of our polls, we'll see something like the following:

http://localhost/phppoll/delete.php?id=1
PHP Poll Delete

Conclusion

Congratulations! You have now successfully created a poll and voting system with PHP and MySQL.

What next? Consider implementing an authentication functionality that restricts certain users from creating and deleting polls, or implement your own features to the poll and voting system.

If you've enjoyed this article, consider sharing it on social media websites, as the more visitors we receive, the more quality content we can create.

Enjoy coding, and thanks for reading!