Create A Christmas Wish List With PHP (For Beginners)

About The Author

Hello, I’m Daniel and I make things for the web. I’m the CTO at Kinsta and I write for a number of amazing publications like Smashing Magazine and … More about Daniel ↬

Email Newsletter

Weekly tips on front-end & UX.
Trusted by 200,000+ folks.

Please notice that this article doesn’t provide production code to use in your live websites. Make sure to read the full article and study more on PHP security before using the code listed in this article.

’Tis the season to be jolly, and how much jollier could we make it than with a helpful Christmas wish list crafted for your family to ensure that you get maximum presentage this holiday? In this article, we will focus on creating a very simple system that allows you to add gift ideas to a Web page, and for your family (or whoever) to view the list.

Further Reading on SmashingMag:

Please notice that this article was written for beginners who already grasp HTML and CSS, know a bit of PHP and have seen phpMyAdmin before. I will not go into best practices, safety and all the rest of it; let’s just have fun with this one!

Preparing Our Resources

Before creating the website, let’s see the finished product:

screenshot
The finished Christmas wish list.

The result will contain three pages: the list itself, a log-in page and an admin page. We will also have a style sheet, two scripts (for logging people in and saving items) and some images. You can view an online demo of the code to see the end result.

Below is a list of the resources I have used. Go ahead and grab them, but feel free to use your own! Please note the licence on each; some are free for commercial use, but some are free only for personal use.

  • I based the colors on the “’Tis the Season” color palette from COLOURlovers.
  • The Santa icon in the logo is from the “Santa” icon set, downloaded from Iconfinder.
  • The Christmas style font is called “Mountains of Christmas” and was obtained from Google Web Fonts
  • I created the background pattern by playing around with the background maker on BG Patterns.

File Structure

We know that we’ll need three pages (list, log-in and admin), but let’s look at the full list of files that we will need. Feel free to create these as empty files in advance.

  • index.php
  • login.php
  • admin.php
  • header.php
  • footer.php
  • config.php
  • script-login.php
  • script-additem.php
  • images
  • uploads

The first three files should be self-explanatory; they will handle the content of our three main pages. We will also use a separate header and footer file; the purpose of these files is to minimize duplicate code.

For all three of our pages, we’ll need to create the code for the header section (i.e. the Santa icon and the title, plus the actual HTML head section and so on). Instead of writing the code three times (which would make the code hard to update), we will write it out once and include the code in all three files.

We’ll also include the config.php file in each file. This sets up the database connection and handles a few other things that are needed for every page.

The next two files in the list are scripts that handle logging in and adding new items to the list.

Finally, we have two directories. The images directory will contain the images we need for our website’s framework. The uploads directory will contain all of the images for our uploaded items.

A Word Of Warning

Note that this is meant as a beginner’s exercise. The code you see here will give you the intended result, but a lot of it is not safe for production websites. It lacks a lot of safeguards, such as data validation, salts for passwords (for better security), htaccess rules and so on. The goal of this article is to let beginners forget about all of these things and just concentrate on building something nice.

Neither does this article promote best practices. You may find yourself adopting different methods later on, or I may write in another article that we shouldn’t do something you see here. The article is intended as a fun little example for beginners to spice up their boring theory sessions. I believe that the best way to learn is through increasingly difficult examples.

That said, I encourage you to try all of this out and play around with it at home or on your servers. If you put this on a live server, I recommend using an account that has only this website on it (or only test websites). I also recommend using passwords for user accounts that are not the same as your other passwords.

Creating Our Framework

Our mini-framework consists of the header and the basic structure of the page. Let’s see what this would look like in HTML:

<!DOCTYPE html>
<html>
   <head>
      <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

      <title>My Christmas List</title>

      <!-- Style sheets -->
      <link href='style.css' rel='stylesheet' type='text/css'>

   </head>
   <body>

      <div id="site_header">
         <img id="site_logo" src='images/santa_logo.png' alt='The Santa Logo' >
         <h1>Daniel Pataki's Christmas List</h1>
         <div class="clear"></div>
      </div>

      <div id="site_container">

      </div>

   </body>
</html>

The Head

As you know from your studies in HTML, the code above consists of two main sections. The code inside the <head> section is not visible to the user. The <head> contains meta information (such as keywords and a description of the website), the title, the style sheets used, the JavaScript files needed and so on.

Your Web page must have the <head> element and at the very least must contain a <title> element. The content of the <title> tag will be seen in the browser tab. In our case, we have also tied a style sheet to our website using a <link> element.

The Body

Your Web page must also contain a <body> element, even an empty one. Of course, nothing much would happen if we left it empty, so I’ve added the header of our website and the main content area.

The header of our website will be seen on all pages. This is the section with the Santa logo and the title “Daniel Pataki’s Christmas List.”

I’ve used a regular image tag to set the image, and a top-level heading to create the title. I’ve also used an empty <div> element, giving it a class of clear. The reason for this has to do with positioning (more on this when we get to the CSS).

Once we’re done with the header, we need a big white container to hold our list. This is done with a simple <div>, which we’ve given a class of site_container.

Finally, we close the <body> and <html> tags, and we make sure our code is valid by running it through the HTML Validator. Don’t worry yet if your code is invalid. Try to fix what you can, but don’t worry if something isn’t working. Your goal now is to get things working; you’ll have plenty of time later to worry about best practices.

Splitting Up

The code we’ve just written will be common to all of our pages. The only thing we’ll change is the content inside the site_container div (which is currently empty). To avoid having to add this same code three times, it is a good practice to separate it into two files: a header and footer file.

Everything common to the top part of each page should go in header.php, and everything common to the bottom part should go in footer.php.

Copy and paste everything up to and including <div id='site_container'> into the header.php file. Copy and paste everything else into the footer.php file.

If you load the website right now, you should see an empty page with no content. This is because index.php is being shown but is currently empty, so we need to pull in the header and footer code. Use the following code to make that happen.

<?php include('header.php'); ?>

<?php include('footer.php') ?>

If you load the Web page now, you should see the code in place. It will be awfully ugly, but we’ll change all that in a jiffy.

Adding Some Style

Now that our structure is in place, it’s time to make it look nice. Achieving what you see in the finished product is actually very easy, so let’s jump right in.

Resetting styles The first thing to do with many websites is to reset all browser-specific properties. Each browser handles headings, tables, inputs and other elements slightly differently, so let’s reset all of these styles, which will make the page ugly but at least uniform on every platform.

body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,form,fieldset,input,textarea,p,blockquote,th,td {
   margin:0;
   padding:0;
}

table {
   border-collapse:collapse;
   border-spacing:0;
}

fieldset,img {
   border:0;
}

address,caption,cite,code,dfn,em,strong,th,var {
   font-style:normal;
   font-weight:normal;
}

ol,ul {
   list-style:none;
}

caption,th {
   text-align:left;
}

h1,h2,h3,h4,h5,h6 {
   font-size:100%;
   font-weight:normal;
}

q:before,q:after {
   content:’;
}

abbr,acronym {
   border:0;
}

This is a commonly used reset called YUI 2 Reset CSS. You can copy and paste it from here, and you can also find it on the Yahoo Developer Network.

Overall structure The overall structure consists of the body, the header section and the website container. Let’s concentrate on putting these in the right places and adding the proper background to our website.

html, body {
   background:url('images/bg.png');
   color:#555;
   font-size:14px;
}

#site_header {
   width:740px;
   margin:0 auto;
}

#site_header h1 {
   font-family: Mountains of Christmas;
   font-size:42px;
   letter-spacing:1px;
   color:#fff;
   position:relative;
   top:60px;
   font-weight:700;
   text-shadow:-1px -1px 1px #3a5e40
}

#site_logo {
   float:left;
   position:relative;
   top:36px;
   left:-24px;
}

#site_container {
   background:#fff;
   width:700px;
   padding:42px 20px;
   margin:0 auto;
   border:3px solid #7b9971;
}

This should be pretty easy to follow. Let me expand on a few points of interest.

Embedding fonts It’s a good bet that the Mountains of Christmas font is not installed on your or your visitors’ computers, but we can still use it through the magic of CSS3 and with help from Google Web Fonts. Go to Google’s website now, choose any font, and follow the instructions to use it.

Before referring to a font in your CSS file, you need to embed it with the following code, which goes in the <head> section of your website:

<link href='https://fonts.googleapis.com/css?family=Mountains+of+Christmas:700' rel='stylesheet' type='text/css'>

Clearing elements If you load the website with this code, the header will seem to collapse into the content area, because the image inside the header is floated left. Whenever you float an element inside another element, you need to make sure the floats are cleared to prevent the content from collapsing like this.

While this behavior might seem odd, it has a logic. Have a look at A List Apart for a good article on floats. Right now, all we need to do is create an empty element at the bottom of the parent element, which will clear the floats above it. To accomplish this, paste the following into the CSS sheet:

.clear {
   clear:both;
}

Now that we’re done with our little framework, let’s add some content to the website!

Creating The Database

Before moving on, you’ll need to put a database structure in place. If you are using your localhost to follow along, you should be able to get to your database through one of the following:

  • https://localhost/phpMyAdmin
  • https://localhost/phpMyAdmin
  • https://localhost:8888/phpMyAdmin
  • https://localhost:8888/phpMyAdmin

The usual problem with accessing phpMyAdmin is that it needs to be capitalized in some installations but not in others. If you use a port number after localhost, make sure to add it.

Once you’re there, find the “Create a new database” field and type christmaslist to create the database. Then, create two new tables with the following structure:

screenshot
Creating the user table.

screenshot
Creating the item table.

Go to the “Privileges” tab in phpMyAdmin to set up a user who can access the database that you just created. Later on, you will need the name of the database, the user name and the password to access it.

Once that’s done, manually create a user in the database. Make sure to select the SHA1 function next to the password field.

screenshot
Encoding a field with SHA1.

Connecting to the Database

While you now have a database, you will need to actually connect to it before you are able to use it. Whenever you need data from the database, you will need to connect to it, retrieve the data and then close the connection.

Just like with the header section of the website, you will be using this database so often that writing the code once and including it on every page would be best.

Let’s go into config.php and add the following code.

<php
$db = new mysqli("localhost", "db_username", "db_password", "db_name");
?>

This code connects to your database and makes it possible for you to read the data and write to the database. But you might have noticed that the config.php file is not referred to anywhere on our page. Let’s fix that by including it in the header.php file, which is included on every page.

include("config.php");

Our code now opens a connection to the database. Because it is included in the header file, we will be able to grab data on any page that we are on. Once we’ve done that, we’ll need to close the connection. We can do this in the footer.php file, because that is the last thing on the page and we won’t need to perform any database actions after the <html> closing tag. So, paste the following line there:

<?php $db->close(); ?>

Logging In

We want to let only people who we know in on this project, so let’s protect it with a log-in function. A user will enter their name and password, and our script will check whether a user with these credentials is in the database.

HTML and CSS

Go to the login.php file, and create a log-in form like this:

<form method="post" action="script-login.php">

   <h1>Please log in</h1>

   <label for="username">username</label>
   <input type="text" name="username" id="username">
   <br>
   <label for="password">password</label>
   <input type="password" name="password" id="password">
   <br>

   <input type="submit" value="Log In" class='submit'>

</form>

While the form is usable as is, it looks a bit funny, so let’s add some CSS style to spruce it up:

h1 {
   font-family:georgia;
   margin:0 0 12px 0;
   font-size:32px;
   color:#7b9971;
}

input, textarea {
   border:1px solid #7b9971;
   background:#e9fce3;
   padding:5px;
   font-size:16px;
   width:300px;
   margin:0 0 6px 0;
   display:block;
}
textarea {
   height:140px;
}
label {
   font-size:13px;
   margin:0 0 4px 0;
}
input.submit {
   border:0px;
   width:auto;
   color:#fff;
   background:#941f1f;
   font-size:12px;
   padding:5px 12px;
   border:1px solid #7d1515;
}

The data from this form will be submitted using the POST method, which is safer for most data transfers. The difference between the POST and GET methods is mainly that data transmitted via the GET method can be seen in the URL, while data passed with the POST method cannot.

The PHP Script

Once the user has submitted the form, the data will be transferred to script-login.php, so let’s go there now and deal with the data:

include("config.php");

$username = $_POST['username'];
$password = sha1($_POST['password']);

$query = "SELECT ID FROM user WHERE username = '$username' AND password = '$password'";

if ($result = $db->query($query)) {
   while ($user = $result->fetch_object()) {
      $_SESSION['user'] = $user->ID;
      header("Location: index.php");
   }
}
else {
   header("Location: login.php");
}

$db->close();

Remember how we included config.php file in every file? What gives? Why are we including it here as well? Since this is a script, we do not need our usual header section. We just need to process some data and move the user along.

Our config.php file is included in header.php, so if there’s no header.php file here, then there won’t be any config.php code either. So, we need pull in the config.php file separately and also close the database connection in the last line.

Let’s break the code down. First, we store the user name and password in two separate variables. This is not necessary but adds clarity. You might have noticed the sha1() function being used. This is an algorithm that converts plain-text passwords into a string that is almost impossible to decipher.

When a user registers and gives “superawesome” as their password, this is immediately run through the SHA1 algorithm. Instead of storing the plain-text password, we store the result of the algorithm, which is 0ac45a31f4576e727746e0ab26b916df0e0eb890.

When the user logs in, we don’t actually know (or care) what the plain-text password is. We simply take the value that they’ve entered and use the SHA1 algorithm to see whether the SHA1-encoded password stored for the user’s name is the same as this SHA1-encoded string. If it is, then the user is allowed to log in.

The power of this method is that an intruder would not be able to see the user’s password even if they had direct access to the database. Sure, they’d see that the user’s SHA1-encoded password is 0ac45a31f4576e727746e0ab26b916df0e0eb890, but they wouldn’t be able to do a whole lot with that.

Moving on, we write our query and store it in a variable. We check that the ID of the person whose user name has been entered is correct and that the password belonging to that user name is the given password. If both criteria are met at once, we can log the user in.

The query is executed by the following code: $result = $db->query($query). I have encased this in an if statement. If we get a result, we will be able to use it through the $result variable. If we don’t get a result, then the whole expression will be false, and so we wouldn’t let the user log in and would just redirect them to the login.php page.

If we do get a result, then we know that the user has already entered a correct user name and password combination, so we’d let them log in and redirect them to the main list.

We do this by looping over the retrieved users (there will be only one) and storing the user’s data in the $user variable. We then create a session variable using the user’s ID and check for its presence on every page. If it is not present, then we’d know that the user is not logged in and so we would redirect them to login.php. Finally, we would direct the user to the main list.

To make this work, we need to do one more thing. Session variables can be used to pass data from one page to another, but we need to start a session first. As long as a session has been started on a page, we can pass values to it from other pages. This sounds like something we’ll need on all pages, so let’s add it to our config.php file, right at the top.

session_start();

If you go now to the log-in form and enter a valid user name and password combination, the following should happen. You should be directed to the script that checks for a user with the given credentials. If it finds them, it creates $_SESSION[‘user’] with the value of the user’s ID. It will then direct you to main list (index.php), where the session variable should be available.

You can check this by going to the index.php file and echoing the contents of $_SESSION[‘user’].

Redirecting Logged-Out Users

Because we’ve set the session variable for all logged-in users, we can redirect anyone who doesn’t have this variable set to the log-in page. By the way, these session variables will remain usable until the user closes the browser.

We will also need redirection on all pages, so let’s add it to the config.php file at the very bottom, like so:

if( (!isset($_SESSION['user']) OR empty($_SESSION['user'])) AND $_SERVER['REQUEST_URI'] != '/login.php') {
   header("Location: login.php");
}

If the session variable is not set or is empty (and the user is not on the log-in page), then we redirect the user to the log-in page. The last bit is needed because we want to allow everyone on to the log-in page — even people without a session variable, of course.

The Admin Page

Our admin.php file will consist of a simple form to add new items. By now you should not be able to visit this page if you are merely logged in. Open up the admin.php file and let’s add our form.

<form method="post" action="script-additem.php" enctype="multipart/form-data">
   <h1>Add an Item</h1>

   <label for="name">name</label>
   <input id="name" name="name" type="text">

   <label for="description">description</label>
   <textarea id="description" name="description"></textarea>

   <label for="link">link</label>
   <input id="link" name="link" type="text">

   <label for="price">price</label>
   <input id="price" name="price" type="text">

   <label for="image">image</label>
   <input type="file" name="file" id="file">

include("config.php");

Our code now opens a connection to the database. Because it is included in the header file, we will be able to grab data on any page that we are on. Once we’ve done that, we’ll need to close the connection. We can do this in the footer.php file, because that is the last thing on the page and we won’t need to perform any database actions after the <html> closing tag. So, paste the following line there:

<?php $db->close(); ?>

Logging In

We want to let only people who we know in on this project, so let’s protect it with a log-in function. A user will enter their name and password, and our script will check whether a user with these credentials is in the database.

HTML and CSS

Go to the login.php file, and create a log-in form like this:

<form method="post" action="script-login.php">

   <h1>Please log in</h1>

   <label for="username">username</label>
   <input type="text" name="username" id="username">
   <br>
   <label for="password">password</label>
   <input type="password" name="password" id="password">
   <br>

   <input type="submit" value="Log In" class='submit'>

</form>

While the form is usable as is, it looks a bit funny, so let’s add some CSS style to spruce it up:

h1 {
   font-family:georgia;
   margin:0 0 12px 0;
   font-size:32px;
   color:#7b9971;
}

input, textarea {
   border:1px solid #7b9971;
   background:#e9fce3;
   padding:5px;
   font-size:16px;
   width:300px;
   margin:0 0 6px 0;
   display:block;
}
textarea {
   height:140px;
}
label {
   font-size:13px;
   margin:0 0 4px 0;
}
input.submit {
   border:0px;
   width:auto;
   color:#fff;
   background:#941f1f;
   font-size:12px;
   padding:5px 12px;
   border:1px solid #7d1515;
}

The data from this form will be submitted using the POST method, which is safer for most data transfers. The difference between the POST and GET methods is mainly that data transmitted via the GET method can be seen in the URL, while data passed with the POST method cannot.

The PHP Script

Once the user has submitted the form, the data will be transferred to script-login.php, so let’s go there now and deal with the data:

include("config.php");

$username = $_POST['username'];
$password = sha1($_POST['password']);

$query = "SELECT ID FROM user WHERE username = '$username' AND password = '$password'";

if ($result = $db->query($query)) {
   while ($user = $result->fetch_object()) {
      $_SESSION['user'] = $user->ID;
      header("Location: index.php");
   }
}
else {
   header("Location: login.php");
}

$db->close();

Remember how we included config.php file in every file? What gives? Why are we including it here as well? Since this is a script, we do not need our usual header section. We just need to process some data and move the user along.

Our config.php file is included in header.php, so if there’s no header.php file here, then there won’t be any config.php code either. So, we need pull in the config.php file separately and also close the database connection in the last line.

Let’s break the code down. First, we store the user name and password in two separate variables. This is not necessary but adds clarity. You might have noticed the sha1() function being used. This is an algorithm that converts plain-text passwords into a string that is almost impossible to decipher.

When a user registers and gives “superawesome” as their password, this is immediately run through the SHA1 algorithm. Instead of storing the plain-text password, we store the result of the algorithm, which is 0ac45a31f4576e727746e0ab26b916df0e0eb890.

When the user logs in, we don’t actually know (or care) what the plain-text password is. We simply take the value that they’ve entered and use the SHA1 algorithm to see whether the SHA1-encoded password stored for the user’s name is the same as this SHA1-encoded string. If it is, then the user is allowed to log in.

The power of this method is that an intruder would not be able to see the user’s password even if they had direct access to the database. Sure, they’d see that the user’s SHA1-encoded password is 0ac45a31f4576e727746e0ab26b916df0e0eb890, but they wouldn’t be able to do a whole lot with that.

Moving on, we write our query and store it in a variable. We check that the ID of the person whose user name has been entered is correct and that the password belonging to that user name is the given password. If both criteria are met at once, we can log the user in.

The query is executed by the following code: $result = $db->query($query). I have encased this in an if statement. If we get a result, we will be able to use it through the $result variable. If we don’t get a result, then the whole expression will be false, and so we wouldn’t let the user log in and would just redirect them to the login.php page.

If we do get a result, then we know that the user has already entered a correct user name and password combination, so we’d let them log in and redirect them to the main list.

We do this by looping over the retrieved users (there will be only one) and storing the user’s data in the $user variable. We then create a session variable using the user’s ID and check for its presence on every page. If it is not present, then we’d know that the user is not logged in and so we would redirect them to login.php. Finally, we would direct the user to the main list.

To make this work, we need to do one more thing. Session variables can be used to pass data from one page to another, but we need to start a session first. As long as a session has been started on a page, we can pass values to it from other pages. This sounds like something we’ll need on all pages, so let’s add it to our config.php file, right at the top.

session_start();

If you go now to the log-in form and enter a valid user name and password combination, the following should happen. You should be directed to the script that checks for a user with the given credentials. If it finds them, it creates $_SESSION[‘user’] with the value of the user’s ID. It will then direct you to main list (index.php), where the session variable should be available.

You can check this by going to the index.php file and echoing the contents of $_SESSION[‘user’].

Redirecting Logged-Out Users

Because we’ve set the session variable for all logged-in users, we can redirect anyone who doesn’t have this variable set to the log-in page. By the way, these session variables will remain usable until the user closes the browser.

We will also need redirection on all pages, so let’s add it to the config.php file at the very bottom, like so:

if( (!isset($_SESSION['user']) OR empty($_SESSION['user'])) AND $_SERVER['REQUEST_URI'] != '/login.php') {
   header("Location: login.php");
}

If the session variable is not set or is empty (and the user is not on the log-in page), then we redirect the user to the log-in page. The last bit is needed because we want to allow everyone on to the log-in page — even people without a session variable, of course.

The Admin Page

Our admin.php file will consist of a simple form to add new items. By now you should not be able to visit this page if you are merely logged in. Open up the admin.php file and let’s add our form.

<form method="post" action="script-additem.php" enctype="multipart/form-data">
   <h1>Add an Item</h1>

   <label for="name">name</label>
   <input id="name" name="name" type="text">

   <label for="description">description</label>
   <textarea id="description" name="description"></textarea>

   <label for="link">link</label>
   <input id="link" name="link" type="text">

   <label for="price">price</label>
   <input id="price" name="price" type="text">

   <label for="image">image</label>
   <input type="file" name="file" id="file">

   <input type="submit" class="submit" value="add item">

</form>

Our existing styles should make this look OK, so we can go straight to the points of interest. This is all pretty standard unless you’ve never looked at file uploads.

To enable file uploads, we need to add two things to the form: an input field of the type file, and the encoding type (in this case, multipart/form-data). If you want to be able to upload files, you will always have to specify the enctype parameter.

We blew through that pretty quickly, so let’s continue with the script that does the heavy lifting, script-additem.php:

include("functions.php");

$target_path = $_SERVER['DOCUMENT_ROOT']."/uploads/";
$target_path = $target_path . basename( $_FILES['file']['name']);
$file_location = "uploads/".$_FILES['file']['name'];
move_uploaded_file($_FILES['file']['tmp_name'], $target_path);

$query = "INSERT INTO item (name, description, price, link, image) VALUES ('$_POST[name]', '$_POST[description]', '$_POST[price]', '$_POST[link]', '$file_location')";
$result = $db->query($query);

$db->close();
header("Location: admin.php?uploaded=true");

The first thing to take care of is file uploading. We created the uploads directory for this purpose, so let’s pass the path to it to our script.

Take care because this may not be the code you need to use. If you are using a subdirectory, you may need to use $_SERVER[‘DOCUMENT_ROOT’].“/subdirectory/uploads/” or something like it. If in doubt, print out the contents of the $_SERVER variable and try to find your path from there.

Once our target directory path is set, we need to add the file name to it to make it a complete path. Our target path now contains the absolute path to the file, something like /home/username/public_html/christmaslist/uploads/myuploadedimage.jpg.

To make sure we have the URL that we’ll add to the database, we store it in the $file_location variable. This is different from $target_path because it doesn’t have the /home/username/public_html/christmaslist part of the URL.

We use the move_uploaded_file($_FILES[‘file’][‘tmp_name’], $target_path); line to upload the files. The move_uploaded_file() function takes two arguments: the temporary location of the file and the new location that we want to put it in.

At this point, our image has been uploaded. It’s time to add the data of the item to the database. The data we need contains the URL of the image as well, which is why we had to upload the image before adding the item to the database.

We store our query inside the $query variable, execute it, close the connection and redirect the user back to admin.php. I added the ?uploaded=true query string to the URL; we will use this to show the user a confirmation message. Head back to the admin.php file and paste the following in it just under the include:

<?php
   if(isset($_GET['uploaded']) AND $_GET['uploaded'] == "true") {
      echo "
<div class="success">The item has been added</div>
";
   }
?>

This is the GET method, and as you can see, the data is visible in the URL. Using this here is OK, of course; we’re just using it to detect whether an item has been successfully added. To make it prettier, we’ll style this div like so:

.success {
   padding:5px 12px;
   background:#7b9971;
   border:#7b9971 - #222 solid 1px;
   color:#fff;
   margin:0 0 12px 0;
}

The form should now be ready to use, so go ahead and add three to four test items, complete with images and all.

Listing Our Gift Ideas

The only thing left to do is list the items in the database for our visitors. Let’s head over to the index.php file and add the following code to do this:

<?php include('header.php'); ?>

<table class='item_list'>

<?php
   $query = "SELECT * FROM item";
   $result = $db->query($query);

   if($result) : while ($item = $result->fetch_object()):
?>

   <tr class='item'>
      <td class='item_image'>
         <img width="150" src='<?php echo $item->image ?>'>
      </td>
      <td class='item_content'>
         <h2 class='item_name'><?php echo $item->name ?></h2>
         <div class='item_description'><?php echo nl2br($item->description) ?></div>
      </td>
      <td class='item_price'>
         <h3>
            <a href='<?php echo $item->link ?>'>
               <span class='price'>$<?php echo number_format($item->price) ?></span>
            </a>
         </h3>
         <a class='text' href='<?php echo $item->link ?>'>Buy This</a>
      </td>
   </tr>

<?php endwhile; endif; ?>
</table>

<?php include('footer.php') ?>

The frame of this page is created by including the header at the top and the footer at the bottom. This includes pulling in our config.php file and closing our connection, so we can forget about that here.

We’ll pull in all of the items and show them using a table. We use while ($item = $result->fetch_object()) to iterate through all of the results. We want the exact same structure for each results: a three-column row. The first column will show the image, the second will show the name and a description, and the third will show the price and a purchase link.

The two functions worth mentioning here are nl2br() and number_format(). The former converts line breaks into <br> tags. The latter converts numbers into a nicely formatted string, adding in thousand separators as needed.

To make this all nice and proper, let’s add some CSS:

.item td {
   border-bottom:1px solid #ccc;
   padding:12px 0;
}

.item_image img {
   margin:0 16px 0 0;
}

.item_price h3 {
   margin:0px;
}

.item_price a {
   font-weight:700;
   font-family:Mountains of Christmas;
   color:#941f1f;
   text-decoration:none;
}

.item_price a:hover {
   color:#941f1f + #252525;
}

.item_price .price {
   font-size:52px;
}

.item_price .text {
   position:relative;
   top:-12px;
}

.item_content {
   padding:0 42px 0 0;
   width:340px;
}

.item_content h2 {
   font-size:24px;
   font-family:georgia;
}

A Note On SQL Security

While this article as a whole should not be used in production environments (it is only a basic example to aid in your studies) commenters have pointed out - correctly - that some level of SQL security should be added.

A great (and simple) method of protecting against any naughtiness (like SQL injection) is to escape strings.

Instead of using the data from variables directly we’ll first run them through the mysqli_real_escape_string() function which will take care of the work for us. Let’s take a look at an example which we could use to search our items.

$term = $_GET['term']; // This is the term the user searched for
$term = $db->real_escape_string($term);
$query = "SELECT * FROM item WHERE name LIKE '%$term%' ";
$result = $db->query($query);

You can use this method to make sure all your SQL code is better suited to a working environment. A more elaborate (but even safer) way to go is preparing your statements. Preparing your statements using mysqli_prepare() is a more effective way of ensuring proper safety.

In addition to all this you should be sanitizing and validating any data that your application receives. This involves making sure that when you’re looking for a date you receive the correct format (or convert into it), if a value needs to be between 1-10 it indeed is, and so on.

Creating a secure environment is a mammoth task which you won’t be able to lear all at once. However there are many small things (like escaping string) you can do which will make your site much more secure.

The Way Forward

If you’ve been following along, you should now have a rudimentary but functioning website that lists your gift ideas. It is far from complete, but you can already expand on the knowledge and practice you’ve gained here by adding a user-registration form to admin.php that enables you to add users from the back end.

If you use a couple of other readily available techniques, you can add a whole bunch of other useful features, such as:

  • Enabling pagination of gift items (if you have a ton of requests);
  • Enabling public access to the list but not the admin section;
  • Enabling public access to the list but restricting access to the admin section so that only you can use it (i.e. not even other logged-in users);
  • Enabling themes for other holidays;
  • Allowing logged-in users to reserve gifts or indicate that they have bought one;
  • Enabling different currencies;
  • So much more.

Closing Thoughts

I would like to stress again that this was not an exercise in best practices, safety, security or flawless coding standards. It was an exercise to get beginners started, without bogging them down with a lot of supplementary information; a fun exercise to give you something tangible quite quickly. You can work on making it better and more secure and feature-rich on your own.

If you have any questions or get stuck, do drop a comment. I or someone in the community will surely help out as soon as we can! Don’t forget to check out the online demo.

Happy holidays, and I hope you get all the items on your list!