A database is an collection of structured data. “Structured” means that the same data is stored for a bunch of different things. This timetable has some structured data:

Figure 1. Timetable
There are a bunch of rail stops. Each one has a name (e.g., Hicksville) and a price (56 cents). The same type of information is stored for each stop: name and price.
Here’s another example of structured data. It’s part of actress Alyson Hannigan’s filmography.

Figure 2. Alyson Hannigan’s filmography
OK, so I have a thing for Willow.
There are a bunch of films. For each one, there’s the same data: year, film name, and role. The format of the data is predictable.
Structured data is often shown in tables, as you can see.
Here’s one more example.

Figure 3. Most popular dog breeds in the US
Again, there’s a table, with rows and columns. Each row is about a dog breed. Each column has data about breeds. Every row has the same format.
So structured data has predictable information about things of the same type, like rail stops, films, or dog breeds. What’s unstructured data look like?
Here’s some unstructured data.

Figure 4. Ozymandias
It’s the poem Ozymandias, by Shelley. There’s no predictable format.
PHP programs have an easier time processing structured than unstructured data. For example, a PHP program could take the dog breed data and compute the total number of registrations.
But there isn’t much a PHP program can do with a poem, except show it. It couldn’t tell you what the poem meant.
So a database is a collection of structured data. Some Web sites draw information from databases to create Web pages. For example, Amazon.com has a database, with data on the products they have. When your browser asks for a page, a program runs on their server. It gets product information from their database, and makes an HTML page out of it.

Figure 5. Amazon page
When you order something, your order goes into the database. Order data is structured. Each order has the same type of data: date, customer, products, etc. So one database can have structured data about different types of things: orders, customers, products, ...
A program tells the shipping people that there is a new order. They look in the database, and see your order. They put your stuff in a box, mail it to you, and update the database to show that the order was shipped.
The database ties different parts of the business together. Sales, shipping, receiving, billing, ... They all use data from the database.

Figure 6. DB ties business together
This chapter shows you how database Web applications work. As usual, you’ll do a lot of hands-on stuff.
The chapter talks about databases that have just one table in them. I’ve found that people have trouble understanding databases with more than one table, because of table relationships. So I’ll delay talking about relationships until you know how single-table databases work.
You’ll need a Web hosting account with MySQL. If you followed my earlier recommendations, you have one.
Let’s start by looking at some sample database applications, so we know what we’re trying to acomplish.
This chapter is about creating database Web applications. We need to know what a “database Web application” is.
In this lesson, we:
Let’s look at some examples, where DB tech is key to a Web application.
DogToys is an online store selling toys, like frisbees, chew ropes, and squeaky balls.
Customers see pages like this:

Figure 1. Product catalog
The catalog is the public face of the business. There’s a lot of activity behind the scenes. DogToys has to:
DogToys has Web pages to help with these tasks. Customers can’t use these pages, just DogToys’ employees.
For example, here’s a screen that would let someone change a price:

Figure 2. Updating product data
Changing prices is quick and easy. No HTML. Change a number in a form, and click the Save button.
Here is what the customer sees after the change:

Figure 3. The new catalog
How to make such an application? It’s easiest if there is one central data store, with all of the information about the products. A database.
The database has a products table, with every price in it, Here’s part of the product data:

Figure 4. Part of the product data in the DB
When a customer visits the catalog Web page, a PHP program fetches the product from the database and shows it.

Figure 5. Customer viewing product data
When a marketer changes a price, s/he fills in an HTML form. The form sends the data to a PHP program that writes the new price to the database.

Figure 6. Marketer changing product data
DogToys has a bunch of PHP programs that all connect to the same database.

Figure 7. The system
One PHP page shows the product catalog. This is all the customers are allowed to see.
Another PHP page updates product data. Another page adds a new product. Another removes a product. Employees use these pages to keep the catalog up-to-date.
The database is at the center of it all. Different PHP pages use the same database to show different people what they need.
Let’s look at another example of a Web application.
DogRock is a site about dog rock music. It has articles about new dog bands, albums, concerts, tours, and such.
Here’s a sample article:

Figure 8. An article
People who write for DogRock aren’t Web experts. They don’t know what FTP means, or about nav bars and such. How to let writers add new stories without knowing much about Web tech?
That’s what a content management system (CMS) is for. CMS let nontech people manage Web site content.
When a user goes to the site, s/he sees an article list:

Figure 9. Article list
Suppose Turlough, one of DogRock’s authors, writes a new article. He creates it by filling in a form:

Figure 10. Writing an article
When Turlough has finished writing his story, it will show up on the article list:

Figure 11. New article list
Click on the link, and read the article:

Figure 12. Article
Turlough created the new article by filling in a form on a Web page. He didn’t make an HTML file, or FTP anything.
This is a Big Thing that CMS do: non-tech people can create Web pages.
Articles aren’t stored in HTML files. Instead, they’re stored in a database table.

Figure 13. Article table
A bunch of PHP pages let people read articles, write new ones, edit articles, and delete them. Which pages people are allowed to access depends on whether they are a reader or writer.

Figure 14. DogRock architecture
Both of these systems – ecommerce and CMS – help different people do different tasks. But they have things in common.
There’s a central database in each one.
In both systems, people do four things to data:
DogToys and DogRock are CRUD systems.
People do different tasks with the same system, that is, they have different roles. For the ecommerce system, people with the “customer” role do some things (like look at the catalog). People with the “employee” role do different things, like change prices.
With the CMS, there’s the “writer” role and the “reader” role. Writers can create and edit stories. Readers can only read them.
Here are the main steps in making a CRUD Web application:
For example, for the DogRock CMS: “Create article, read article, update article, and delete article.”
Each article has a title (“The Barkers to tour”), a publication date (June 15, 2011), and a body (“Those old time rockers, The Barkers, are at it again! The band last toured in 2007, when…”).
add-article.phpis a page that lets people add a new article,delete-article.phpis a page that lets people erase an article, ...
articles.phplists all the articles,show-article.phpshows a particular article, ...
Learning how to do all of this at once is too hard. Let’s start with the things that are easy to learn (like making a table), and move towards the more complicated stuff (like updating data).
By the time we get to the complicated stuff, you’ll have so much background that it won’t be so difficult.
We looked at two examples of Web applications: ecommerce and CMS. For each one, there is:
To create an application, you:
Let’s dig down a little, and see how a PHP page and a database management system (DBMS) work together in a Web application.
You’ve seen an overview of PHP database applications that run two small businesses. We’ll work slowly towards being able to write applications like that.
Let’s see how PHP programs and MySQL work together. Not in detail yet. That comes later.
In this lesson, you will learn:
OK) and, sometimes, some data.Remember that a server is software (or an entire computer) that does something for other software (or other computers).
An email server helps email clients. They exchange messages using protocols. Recall that a protocol is a language, a set of standards for things like saying hello and saying goodbye. SMTP is an email protocol.

Figure 1. Email server and client
A Web server helps Web clients (like browsers). They exchange messages in the protocol HTTP.

Figure 2. Web server and client
A database server works with database clients. The client sends requests in the language SQL. The server responds with data and status messages.

Figure 3. DB server and client
Figure 3 is a little different from the others. There’s a client and a server, and they communicate. But often the client and server software are on the same machine. They don’t use the Internet to communicate. Instead, they use something like a socket file to exchange messages. A socket file is a way of sending messages between software running independently on the same computer.
Another difference is the type of client. We will be writing our own clients, in PHP. In the DogRock CMS, each PHP program like add-article.php and delete-article.php is a client. It will generate SQL, and send it to the server.
DB server software is usually called a database management system (DBMS). An individual data set is a database.
The pattern for interaction is:
Suppose you want your PHP page to add data to a database.

Figure 4. Adding new data
Your page sends an SQL statement that includes the data to add. The statement would start with the word INSERT. The DBMS executes the SQL, and returns a status code, like “OK.”
If you want your PHP page to delete some data, your page would send a DELETE statement.

Figure 5. Deleting data
To fetch some data from the database, send an SQL statement. This one starts with the word SELECT.

Figure 6. Fetching data
The DBMS returns a status code, and some data. Your PHP can then do something with the data, like show it to the user.
There are many different DBMS. The one we’ll talk about is MySQL. Why this one? Because:
MySQL is included with the XAMPP package we talked about earlier. So if you installed XAMPP, you already have MySQL. W00f!
SQL stands for structured query language. It’s been part of the geek toolkit for about 30 years. Yes, you read that right. SQL has survived for a long time. And it’s not going away anytime soon.
We’ll talk more about SQL throughout this chapter. But let’s start with some examples, so you get the flavor of it.
Remember, don’t get hung up on the details. I just want you to understand the idea of “send SQL, get status and data.”
Suppose we have some dog data. For each dog, we know its name, breed, and weight. Like this:

Figure 7. Dog data
This data is sitting on the DB server.
We could send this SQL query to the server:
select name, weight from dogs;
This means “fetch the name and weight of every dog.” Here’s what the DBMS would return:

Figure 8. Two columns
Earlier you wrote about SELECT, and now about select. Which is right?
Both are. MySQL doesn’t care whether keywords like select and insert are in upper- or lowercase.
It also doesn’t care whether SQL statements have semicolons(;) at the end. Strictly, they should be there, but you can leave them off if you like.
Here’s another query:
select name from dogs where weight > 50;
This says “Give me the names of the dogs whose weight is more than 50.”
The DBMS would return:

Figure 9. Heavy dogs
This shows that a DBMS can filter data, sending back only what the client asks for. In our case, the client will be a PHP program.
Another example:
select name, weight from dogs order by weight;
This gives:

Figure 10. Sorted dogs
The DBMS sorts the data before returning it.
Another example:
insert into dogs (name, breed, weight) values ('Brian', 'Lab', 51);
This adds a new row to the dogs table:

Figure 11. New dog
This shows that SQL statements can change a database, as well as fetch data from it.
One more example:
delete from dogs where weight < 20;
This would remove all of the light dogs. You would get:

Figure 12. No light dogs
PHP pages send SQL statements to DBMS. So, to mess with data, you write PHP that writes SQL statements. Then you send the SQL to the DBMS.
For example:
$query = "insert into dogs (name, breed, weight)
values ('Brian', 'Lab', 51)";
$db->query($query);
Figure 13. Adding a dog
The first line puts the SQL statement into a variable. The second line sends it to the DBMS.
Here’s another one:
$query = "delete from dogs where weight < 20"; $db->query($query);
Figure 14. Removing light dogs
The first line creates the SQL, and the second one executes it.
What happens when you get a bunch of data back from a database? Remember this query:
select name from dogs where weight > 50;
This gives:

Figure 9 (again). Heavy dogs
You write PHP that to handle each row. The general pattern is:
Here’s an example. Don’t worry about all of the details; just look for the general pattern. The comments explain what each line does.
//Run the SQL query.
$query = 'select name from dogs where weight > 50';
$record_set = $db->query($query);
//Loop across records.
while( $row = $record_set->fetch_assoc() ) {
//Get fields for a row.
$name = $row['name'];
//Output
print "<p>Dog name: $name</p>";
}
Figure 13. Showing heavy dogs
Lines 2 and 3 run the query. The DBMS sends back some data, in a table (see Figure 9). The table has one column, and a bunch of rows. Each row is also called a record. The bunch of rows the DBMS returns is called a record set.
Lines 5 to 10 form a loop. The loop runs once for each row in the record set. (Don’t worry about how it works; just know that each record is passed through lines 6 to 9.)
Line 7 gets some data from a record, and puts it into a variable. Line 9 shows the data.
I’m getting worried here. It’s looking awfully complex.
I don’t blame you. It does get messy. It’s important to take baby steps. Just one little thing at a time.
That’s what we’ll do. But it won’t work if you skip the exercises. The more difficult the material, the more important the hands-on stuff is.
OK) and, sometimes, some data.Let’s look at a tool that can make your database work easier.
You’ve seen that clients send SQL statements to DBMS. There are tools that write SQL statements for you, saving you some work. Let’s talk about phpMyAdmin, the most widely used tool for managing MySQL databases.
You’ll learn:
Almost everything you do with MySQL is done by sending SQL commands to the server:

Figure 1. Everything is SQL
When you make Web applications, there’s some work you need to do to get things ready for your PHP pages. You create databases, make tables, add fields (columns), and other things.
This is done with SQL. But doing it all manually can be a pain. You have to type out long commands like this:
CREATE TABLE products (
product_id int(11) NOT NULL AUTO_INCREMENT,
'name' char(50) NOT NULL,
description text NOT NULL,
image_file_name char(50) NOT NULL,
price decimal(10,2) NOT NULL,
PRIMARY KEY (product_id)
);
And you have to get everything just right. You might make a mistake, like this:
CREATE TABLE products (
product_id int(11) NOT NULL AUTO_INCREMENT,
'name' char(50) NOT NULL,
description text NOT NULL,
image_file_name char(50) NOT NULL,
price decimal(10,2) NOT NULL,
PRIMARY KEY (prodct_id)
);
It wouldn’t work, but the problem is hard to spot. Grrrr!
There are tools that make it easier.
The one that most people use is phpMyAdmin. It’s actually just a bunch of PHP pages.
Most Web hosting companies install phpMyAdmin for you. It’s also part of the XAMPP package. If you installed XAMPP, you already have phpMyAdmin installed.
So you will have two copies of phpMyAdmin:
You also have two different MySQL instances:
Open the XAMPP control panel. Start both Apache and MySQL:

Figure 2. XAMPP control panel
phpMyAdmin is a bunch of Web pages. It needs Apache (or another Web server) to run.
Start your browser, and go to http://localhost/. You’ll see this on the left:

Figure 3. XAMPP home page
Click the phpMyAdmin link to start it.
Log in to your Hostgator control panel. Scroll down to the Databases section:

Figure 4. Starting phpMyAdmin on Hostgator
If you aren’t using Hostgator, you’ll start phpMyAdmin in a different way. Check you hosting company’s help pages.
How do I start MySQL on my hosting account?
You don’t. MySQL is always running, just like Apache is always running.
Start MySQL on your computer. Then open a browser, and run phpMyAmdin.
Run phpMyAdmin on your hosting account.
(Log in to enter your solution to this exercise.)
The phpMyAdmin installation on your local computer might be a different version from the one on your hosting account. As I write this, I’m running version 2.10.3 on my local machine, and version 2.11.9.5 on my hosting account.
Even the versions of MySQL are different. I’m running MySQL version 5.0.45 on my local machine, and version 5.1.30 on my hosting account.
That’s OK. We’re only covering DB basics. At the level we’re working, having different versions won’t matter. The things we’re looking at have been more-or-less the same for twenty years.
And they’re still current. The core of DB tech has been unusually stable.
In earlier chapters, I recommended that you install XAMPP on your own computer, and write PHP programs there. When everything is working, then you upload the finished programs to your hosting account.
Adding databases complicates things a little, but not much. You can create databases on your local machine, and then move them to your hosting account when you’re ready.
phpMyAdmin can both import and export databases, tables, and other objects. So you can just copy most of your work from your local computer to your hosting account.
I’ll go over an example when we get to creating a table.
There are many other tools that can help you work with databases. Earlier, I recommended Netbeans for writing Web pages. Netbeans has an integrated DB tool that works nicely with MySQL.
However, to keep things simple, we’ll only use phpMyAdmin. It’s already installed on both your development (local) and production (hosting account) servers. The same tool works no matter where you are.
You can read a review of Netbean’s database explorer.
You’re starting to see how PHP programs can work with databases. Let’s look at DogToys and DogRock again. We’ll go through all of their screens, and see how they fit together.
You’ve seen how PHP pages can send SQL statements to MySQL. We’re going to see how you use this ability to write Web applications.
But first, let’s look at two simple applications. Understanding what they do will help you understand why database stuff is done the way it is.
Recall that DogToys has an online product catalog, with things like squeaky balls, and chew ropes. In this lesson, we’ll look at what the site does. Later, you’ll see how it can be built.
By the end of this lesson, you should:
The ToyDogs site has three main goals:
Let’s talk about each one.
Customers want to be able to see pictures, descriptions, and prices of products. The product list should be easy to get to, from anywhere in the site.
Customers should be able to sort product data the way they want. We’ll let them sort the data by price (low to high, and high to low), and by product name.
We want DogToys employees to be able to change product data. The employees who change product data will be from the marketing department. They will not have strong technical skills. They don’t know any HTML.
We’ll create an administration section of the Web site. Employees will be able to fill in forms to change product data.
We will want to change the site. Maybe change the color scheme, add buttons to the nav bar, change the page footer, etc. Webers should be able to make these changes quickly and easily.
We’ll make a template-based site.
You can try the site. The admin parts of the site don’t actually update the database.
You can download the code.
Time for some screens. Here’s the home page:

Figure 1. Home page
Notice the nav bar on the left. This will be the same on every page. Customers can click on the Products button to see the products catalog. This meets the goal of having the products list available from every page on the site. It’s always just one click away.
Here’s the product catalog:

Figure 2. Product catalog
Each product has a name, picture, description and price.
The user can sort the data. Clicking on the Name link sorts the data by name. Clicking on the up and down arrows next to the price sorts by price.
You can try it.
The administration section of the site lets employees change product data. The administration section is just a bunch of Web pages.
There is no clickable link from the main part of the DogToys site to the administration section. To get to it, you need to know the URL. Add /admin to the home page’s URL to get to the main administration menu.
That isn’t very secure.
You’re right, it isn’t. Later in the book, you’ll learn how to add user names and passwords to sites.
Here’s the main administration screen:

Figure 3. Main administration screen
There’s a link users can click to add a new product.
All the products are listed. It looks like the product catalog, but each product has an Edit and Delete link.
Here’s the form for adding product data:

Figure 4. Adding product data
There are four fields:
Most are familiar, but what about the image file name? What’s that about?
We want to show a picture of each product. We’ll take a photo of each one, with a digital camera. We’ll store the image files on the Web site. Let’s put all the photos in a directory called product-images.
How to show the photo on a Web page? With the <img> tag, like this:
<img src="product-images/Image file name" alt="Product name">
Suppose we take a photo of the ball. We name the file with the photo ball.jpg, and put the file into the product-images directory. To show it on the product catalog page:
<img src="product-images/ball.jpg" alt="Squeaky ball">
So the user would type “ball.jpg” into the image file name field in Figure 4.
Image file name is just a regular text field. It’s used to make the <img> tag. The tag itself is just text. But when the browser renders the tag, it will download the image file.
The user clicks a product’s Edit link to edit a product record:

Figure 5. Edit link
This goes to a page like this:

Figure 6. Edit form
It looks like the new product form in Figure 4, but the fields already have data in them.
When on the main administration page, the user can click the Delete link to remove product data:

Figure 7. Delete link
This shows a confirmation page, to make sure the user really wants to delete the product record:

Figure 8. Confirm delete
The user has to click the Confirm button to actually delete the record.
Using a site is a good way to get to know it. But you should use it in the same way that other people would use it.
Pretend you’re a DogToys customer, who wants to buy the most expensive toy. Start at the site’s home page. Write down the steps you would take to complete the task.
Now pretend you are a DogToys employee. DogToys is having a sale on squeaky balls. You want to reduce the price of squeaky balls by 50 cents. Start at the site’s home page. Write down the steps you would take to complete the task.
Put your answers below.
(Log in to enter your solution to this exercise.)
We’ve talked about two of the site’s goals so far:
Now for the last one.
We want to make it easy for Webers to change the site. We’ll use the PHP template approach we talked about before.
So we’ll be using PHP for two things:
We looked at the DogToys Web site. The site lets:
Let’s tour the DogRock site.
Let’s look at another application. Understanding it will help you understand why database stuff is done the way it is.
Recall that DogRock’s site shows articles about dog rock music. It’s a content management system (CMS).
In this lesson, we’ll look at what the site does. Later, you’ll see how it can be built.
By the end of this lesson, you should:
The DogRock site has three main goals:
You can try the site. The admin pages don’t actually change the database.
You can download a zip file of all of the sites files.
Let’s talk about each goal.
Users want to get to new articles quickly. They also want to be able to access a list of older articles.
Each article has a title, a body, an author, and a publication date. Users want to be able to see a list of articles sorted by publication date, so they can see recent stuff. They also want to be able to sort by title and author.
DogRock’s writers are not computer experts. They can handle some basic HTML tags, like <p>, but that’s about it. They don’t know how to create entire Web pages.
We’ll create an administration section of the Web site. Writers will use forms to add and change articles. We’ll let them delete articles as well.
We will want to change the site. Maybe change the color scheme, add buttons to the nav bar, change the page footer, etc. Webers should be able to make these changes quickly and easily.
We’ll make a template-based site.
Time for some screens. Here’s the home page:

Figure 1. Home page
The latest three articles are listed on the home page, from most recent to least recent. You can try it.
Notice the nav bar on the left. This will be the same on every page. Users can click on the Articles button to see a complete article list.
Here’s the article list:

Figure 2. Article list
Clicking on the column names (title, date, and author) sorts the list. Clicking on the title of an article shows the article.
You can try it.
The administration section of the site lets writers add, edit, and remove articles. The administration section is just a bunch of Web pages.
There is no clickable link from the main part of the DogRock site to the administration section. To get to it, you need to know the URL. Add /admin to the home page’s URL to get to the main administration menu.
Here’s the main administration screen:

Figure 3. Main administration screen
There’s a link users can click to add a new article.
All the articles are listed. It looks like the article list, but each article has an Edit and Delete link.
Here’s the form for adding an article:

Figure 4. Adding an article
There are three fields:
Some PHP sets the publication date when the article is saved.
The user clicks an article’s Edit link to edit an article record:

Figure 5. Edit link
This goes to a page like the add article form, but with the fields filled in.
When on the main administration page, writers can click the Delete link to remove an article:

Figure 6. Delete link
This shows a confirmation page, to make sure the user really wants to delete the article record:

Figure 7. Confirm delete
The user has to click the Confirm button to actually delete the record.
Using a site is a good way to get to know it. But you should use it in the same way that other people would use it.
Pretend you’re a DogRock reader, who wants to read the latest article. Start at the site’s home page. Write down the steps you would take to complete the task.
Now pretend you’re a reader who wants to see what Bounder has written. Start at the site’s home page. Write down the steps you would take to complete the task.
Now pretend you are a DogRock writer. You want to fix a spelling error in the article “A new howl on the prowl.” Start at the site’s home page. Write down the steps you would take to complete the task.
Put your answers below.
(Log in to enter your solution to this exercise.)
We want to make it easy for Webers to change the site. We’ll use the PHP template approach we talked about before.
So we’ll be using PHP for two things:
The DogRock site lets:
Each application – DogToys and DogRock – uses a database. Let’s see how you create a database, and then add a table to it.
You’ve seen how PHP programs can send SQL statements to MySQL. You’ve seen two sample applications. Time to get your hands dirty.
By the end of this lesson, you should:
Start phpMyAdmin on your computer (see this explanation).
You’ll see the phpMyAdmin home page. If you get lost, click the home button to get back to it:

Figure 1. Go to the phpMyAdmin home page
Type in the name of the new database on the phpMyAdmin home page, and click the Create button:

Figure 2. Creating a database
phpMyAdmin will make the SQL statement that creates a database, and send it to MySQL. You’ll see something like this:

Figure 3. Feedback from creating a database
That’s it! You’ve created a database.
There are lots of options you can choose. As usual, CoreDogs only talks about the really important stuff.
Create two databases on your local machine:
You’ll use them to install your own copies of the DogToys and DogRock applications.
(Log in to enter your solution to this exercise.)
MySQL uses its own security system, with user names and passwords. When you create a database, the next thing is:
You don’t have to create a new user for each database, but it’s common practice.
Remember this:
MySQL user accounts have nothing to do with other user accounts!
MySQL accounts are used for MySQL access, and nothing else. Not FTP, or email, or Web access. Just MySQL.
How you do it is a little different on different versions of phpMyAdmin. But the concepts are the same.
Go to the phpMyAdmin home page:

Figure 1 (again). Go to the phpMyAdmin home page
Depending on your version of phpMyAdmin, either click the Privileges link, or the Privileges tab:

Or

Figure 4. Privileges link or tab
Click the add user link:

Figure 5. Add user link
Fill in the user’s name, host, and password and click the Go button:


Figure 6. Add user
Don’t set any global privileges for the user.
I often make the user name the same as the database name. You can type in your own password, or have MySQL generate one for you. If you do that, make sure you copy and paste the password to a file on your computer. You don’t want to lose it!
What’s that “host” thing? This is which computer(s) the user is allowed to connect from. The right value is almost always localhost, because your Web server and your DB server are running on the same machine. Type in localhost unless your hosting company tells you otherwise.
I’ve only ever run into one hosting company that didn’t use localhost. That was Dreamhost.
Once you have created the user, you can give the user access to a database. Scroll down to the “Database-specific privileges” section, and fill it in:

Figure 7. Add database privileges
Enter the database you want to give the user access to.
<Begin alternate route>
You can also get to Figure 7 from the home page. Go to the home page:

Figure 1 (again). Go to the phpMyAdmin home page
Then go to the privileges screen:

Or

Figure 4 (again). Privileges link or tab
Click the edit user icon:

Figure 8. Edit user privileges
</End alternate route>
Now that you’ve give the user access to the database, you tell MySQL what that user is allowed to do:


Figure 9. Select privileges
Check the privileges in the figure.
You’re done.
Create two database users on your local machine, one for each of the databases you created earlier. You can name them anything you like. I usually give them the same names as the databases:
You’ll use them for your own copies of the DogToys and DogRock applications.
Choose your own passwords, and write them down!
Give the users access to the server at localhost.
Give the users access to their respective databases. Give them the following privileges:
(Log in to enter your solution to this exercise.)
But did it work? When you create a new database and user, you should run some PHP to make sure it worked.
Here’s a test page.
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Strict//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title></title>
</head>
<body>
<h1>PHP-MySQL connection test</h1>
<?php
//DB connection data.
//Probably leave $host alone.
$host = 'localhost';
//Set $db to the name of your database.
$db = 'dogrock';
//Set user_name to the name of the MySQL user you made.
$user_name = 'dogrock';
//Set $password to the user's password.
$password = '[Secret password here]';
?>
<p>Trying to connect to MySQL.</p>
<ul>
<li>Host name: <?php print $host; ?></li>
<li>Database name: <?php print $db; ?></li>
<li>User name: <?php print $user_name; ?></li>
<li>User password: (Secret)</li>
</ul>
<?php
//Connect to the MySQL server.
$db = new mysqli($host, $user_name, $password, $db);
//Did it work?
if ( mysqli_connect_error() ) {
print '<p>Error! Could not connect to the database. ';
print 'Error message: '.mysqli_connect_error().'</p>';
}
else {
print '<p>W00f! It worked!</p>';
}
?>
</body>
</html>
Figure 10. Test a connection
Line 29 makes the connection. It needs four pieces of data:
localhostdogrockdogrockYou type in those values in lines 11 to 18.
Line 31 tests whether the connection worked or not. If not, it shows an error message. If there is no error, line 36 will tell you about it.
Grab this code, and put it somewhere on your local computer.
Here’s what happens when everything works:

Figure 11. Connection OK
Here’s what happens if MySQL is not running:

Figure 12. MySQL not running
The error message is “Can’t connect to MySQL server.”
Here’s what happens if the password is wrong:

Figure 13. Wrong password
If the database name is wrong? Here it is:

Figure 14. Wrong database name
Write PHP programs to test your two local databases:
Copy the test code, and adjust the connection parameters.
If there’s a problem:
localhost and the databases.(Log in to enter your solution to this exercise.)
Now you know how to:
What about your hosting account?
When you create a database on your hosting account, you do things a little differently. The exact details depend on your hosting company. I’ll run through the procedures for Hostgator.
Log in to your control panel. Find the database icons:
![]()
Figure 14. Control panel database icons
Click the “MySQL databases” icon:
![]()
Figure 15. Control panel database icons
Type in the name of the new database and click the button:

Figure 16. Create database
You’ll get some feedback, like this:

Figure 17. Create database feedback
The next thing is important!
The real name of the database might not be the name you typed!
Hostgator prepends (adds in front) your user name to the database name, and chops off extra characters, if there are any. So it your Hostgator user name is woof121 and you type dogrock for your database name, the real name of your database might be woof121_dogrock.
You would use woof121_dogrock as the database name in your PHP programs.
Why does Hostgator do this?
Remember that many sites run on the same server. That what’s “shared hosting” means. There’s one copy of MySQL that everyone shares.
Several people might create a database names books. How is the server to tell them apart?
Oh, I see. Hostgator changes the names, so they are all different.
Right! So if the user lab200 typed in books as the name of a database, the actual name of the database would be lab200_books. If the user pug77 typed in books as the name of a database, the actual name of the database would be pug77_books.
Copy and paste the real name of the database into your code.
Go back to the MySQL database page. Remember, to get there from the control panel:
![]()
Figure 15 (again). Control panel database icons
To create a user, scroll down to the Add User form, and fill it in:

Figure 18. Create a user
You’ll get feedback like this:

Figure 19. Create a user – feedback
But this is misleading. The control panel changes the user name the same way it changes the database name. So if lab200 creates the user dogrock, the user is really lab200_dogrock.
Hostgator’s control panel won’t change the password. If you type a password of secret_ThinG_12, then that will be the password you use in your PHP.
Now you need to give the user access to the database.
On the MySQL Databases page, you’ll see something like this:

Figure 20. Giving a user access to a database
Select the user and the database, and click the Add button.
Then you select which privileges the user has:

Figure 21. Setting privileges
Use the ones shown in the figure.
You’ll get feedback like this:

Figure 22. Setting privileges – feedback
Hooray! You’ve:
Remember: use the real names of the database and the user. You can see them on the control panel’s MySQL Databases page.
How to test that everything worked? Same as before. Put a file with this code on your hosting account:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Strict//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title></title>
</head>
<body>
<h1>PHP-MySQL connection test</h1>
<?php
//DB connection data.
//Probably leave $host alone.
$host = 'localhost';
//Set $db to the name of your database.
$db = 'dogrock';
//Set user_name to the name of the MySQL user you made.
$user_name = 'dogrock';
//Set $password to the user's password.
$password = '[Secret password here]';
?>
<p>Trying to connect to MySQL.</p>
<ul>
<li>Host name: <?php print $host; ?></li>
<li>Database name: <?php print $db; ?></li>
<li>User name: <?php print $user_name; ?></li>
<li>User password: (Secret)</li>
</ul>
<?php
//Connect to the MySQL server.
$db = new mysqli($host, $user_name, $password, $db);
//Did it work?
if ( mysqli_connect_error() ) {
print '<p>Error! Could not connect to the database. ';
print 'Error message: '.mysqli_connect_error().'</p>';
}
else {
print '<p>W00f! It worked!</p>';
}
?>
</body>
</html>
Figure 10 (again). Test a connection
Replace the database name, user name, and password. Use the real names, like lab200_dogrock. Load the page in your browser, and see what you get.
Create two databases on your hosting account:
Your hosting account’s control panel might adjust the names of the databases. Make sure you know the real names.
Create users for each database. Give them access to localhost. Write down the users’ passwords!
A few hosting companies don’t use localhost.
Give the users the following privileges on their respective databases:
Copy the test code, and adjust the connection parameters. Upload to your server, and run your test programs.
Put the URLs of your test pages below.
If there’s a problem:
locahost. (Log in to enter your solution to this exercise.)
In this lesson, you learned how to:
Now you have some databases. Time to add a table to each one.
You know how to create a database. Now learn how to add a table to a database.
Learn:
INT, DECIMAL, CHAR, TEXT, and DATE.INT field to be a primary key. Make it unsigned and auto_increment. MySQL will fill in a unique value.All MySQL data is in tables. Here’s an example:

Figure 1. dogs table
The data is in rows and columns.
Each row is data about a single dog. So all the data on the first row is about Francis. A row is also called a record.
Each column is a characteristic of a dog, like the dog’s name, breed, or weight. Columns are also called fields. Every row has the same fields.
Here’s the articles table from the DogRock content management system (CMS):

Figure 2. articles table
Each row is data about one article. Each column in an attribute of articles, like the title.
Here’s the products table from the DogToys product catalog:

Figure 3. products table
Each row is data about one product. Each column in an attribute of products, like the price.
Suppose you want a table to store your favorite jokes. Each row of the table would have a joke.
What fields would you put in the table?
(Log in to enter your solution to this exercise.)
Each column contains a specific type of data. Here are the data types for the dogs fields.

Figure 4. dogs data types
When you create a table, you tell MySQL the data type of each column. Don’t worry about how you do that just yet; we’ll talk about it soon. For now, let’s focus on the data types.
There are many different data types. The core types are in three categories:
Characters are text, as in “I like the number 96.” Character fields are also called string fields.
You can tell MySQL how long character fields should be. For example…
CHAR(10)
... tells MySQL that a field can have up to 10 characters. Try to put more in the field, and the extra characters will be cut off.
Another character data type is:
TEXT
A TEXT field can have up to about 65,000 characters.
We’ll only talk about two number types:
Integer fields can contain whole numbers only, like 7. Try to put 7.32 into an integer, and the .32 will be cut off.
Make integer fields with the INT data type.
Decimals are declared like this:
DECIMAL(10,2)
This is a number that is 10 digits long, with 2 decimal places.
MySQL has several date/time types. You can store times down to a fraction of a second, if you want.
We’ll just use the simplest date/time type: DATE. It stores just a date, and not a time.
But how to format a date? In the US, it’s month/day/year, so June 3, 2013 would be 6/3/13. In Australia, you would use day/month/year, as in 3/6/13.
Just as there’s a standard for HTTP, and another one for HTML, and another one for CSS, there’s an international standard for dates. It’s YYYY-MM-DD. So June 3, 2013 would be 2013-06-03.
Most Webers use the standard format when storing dates in a database. That’s what we’ll do. But when we show the dates to people, we’ll use the US format. You can adjust the format, if you like.
There are many other data types. Other types of strings, other types of numbers, and things that aren’t either. For example, coordinates of a place on the earth, used in mapping programs.
We’ll stick with CHAR, TEXT, INT, DECIMAL, and DATE for now. I’ll explain others if we need them.
Here’s the products table again:

Figure 3 (again). products table
Most tables have a column that is the table’s primary key. This uniquely identifies each row in the table.
For products, only one product will have a product_id of 1. Only one product will have a product_id of 2. There will never be any duplicates.
Could the name field be a primary key? No. There might be two products from different manufacturers that have the same name. Maybe two manufacturers make a product they call “Giant chew rope.” They will have different product_ids, so the database can tell them apart.
Here’s the articles table again:

Figure 2 (again). articles table
Two articles could have the same name. For example, Wendy could write an article called “Old Time Howl.” In three years, Bounder could write an article called “Old Time Howl.”
The field article_id takes care of it. Each row has a different value for article_id. It’s the table’s primary key.
Here’s the dogs:

Figure 1 (again). dogs table
What is the primary key? Is it name? That’s the best field we have, but it isn’t very good. Two dogs could have the same name. There might be two Ralphs, for example.
This table isn’t designed well. It would be better if it had a primary key:

Figure 5. dogs table with primary key
It’s common practice to add an integer field (whole number, remember) to a table, and make it the primary key. That’s what I did with the dogs table.
The first row has a value of 1 for this field. The next row has 2, and so on. Actually, it doesn’t matter what the values are, as long as each one is different. I could make the dog_id of the first row 432, the second row 89, the third row 299, etc. But making them 1, 2, 3, etc., is usual.
When we add a new record to the table, we want dog_id to have a new unique value. We can tell MySQL to do this for us. When we create the table, we can mark an integer primary key field as auto_increment.
When our PHP program adds a new record to dogs, we send the values for each field to MySQL. Like this:
insert into dogs (name, breed, weight) values ('Jamie', 'Scottie', 9);
Notice that we didn’t give a value for dog_id. Since dog_id is an auto_increment field, MySQL will set the value for us. It will take the next value in sequence. So if the last record we added have a value of 3,409, then MySQL will fill in the value 3,410.
You listed the fields for a jokes table. What is the data type of each one? Choose from INT, DECIMAL, CHAR, TEXT, and DATE.
Remember to include a primary key.
(Log in to enter your solution to this exercise.)
Let’s see how you actually add a table to the a database.
I added a humans table to the dog database. It has fields for:
First, I opened the database in phpMyAdmin. Do this by selecting the database in the drop-down:

Figure 5. Open a database
Then I typed the name of the new table and the number of fields into the Create table form:

Figure 6. Create a new table
Then I clicked the Go button.
Here are the settings for the primary key, human_id:

Figure 7. The human_id field
The data type is INT. I didn’t need to give a length; INT has a standard length, and MySQL knows what it is.
Primary keys are not negative; they could be, but it would be strange. So I made the field UNSIGNED. It’s also set to AUTO_INCREMENT. You should make your primary keys INT, UNSIGNED, and AUTO_INCREMENT.
I marked as human_id the table’s primary key, using the radio button next to the picture of a table with a key on it. Don’t forget to mark your primary keys!
Here are the settings for name:

Figure 8. The name field
It’s set to CHAR and given a length of 20. You always need to give a length to CHAR fields.
Here are the settings for weight:

Figure 9. The weight field
It’s set to INT.
I clicked the Save button to make the table. phpMyAdmin created an SQL statement, and told MySQL to run it. Here’s the feedback I got.

Figure 10. Create table feedback
Now I have some tables. How about adding data?
Let’s say I want to add data to the dogs table. First, I open the table.

Figure 11. Opening the dogs table
The I click the Insert tab.

Figure 12. Insert tab
I see a form that lets me add data.

Figure 13. Add record form
I fill in the form, but not the auto_increment field!

Figure 14. Adding data
dog_id, the primary key, is an auto_increment field. MySQL will fill in that value for me.
I click the Save button, and get this feedback:

Figure 15. Feedback on adding data
You can see the SQL INSERT statement that phpMyAdmin created. You can also see the message “Data truncated for column ‘breed’ at…” Why? Because I declared breed as CHAR(10). The value I typed in – Whale hound – is 11 characters long. So the last character was lost.
Here’s what the table looks like with the new record:

Figure 16. New data
You can see the truncated breed.
You can also see that the dog_id is 6. Remember that I didn’t type in that value. Because it’s an auto_increment field, MySQL filled in the value itself.
I created the dogs table on my local PC, and added some data. How do I get it on my Hostgator server? I could retype everything, but there’s an easier way:
When you export a table in phpMyAdmin (on your local machine), phpMyAdmin recreates the SQL statements that made the table and added the data. Then you can tell phpMyAdmin (the one on your hosting account) to execute those statements.
Open the table you want to export:

Figure 17. Open table
Click the Export tab:

Figure 18. Export tab
Make sure SQL is the export format:

Figure 19. Export format
Check “Save as file”:

Figure 20. Save as file
Click the Go button. Your browser will save the file.
Open the file in Notepad++, Netbeans, or some other editor. You’ll see the SQL that phpMyAdmin exported.
Start phpMyAdmin on your hosting account. If you forget how, here’s a reminder.
Open the database you want to put the table into.
Click the import tab:

Figure 21. Import tab
Click the Browse button:

Figure 22. Browse button
Select the file to import. Click the Go button. phpMyAdmin will grab the file and run the SQL statements inside it.
In this exercise, you will get DogToys and DogRock running on your computer.
If you haven’t already, download the zip files for DogToys and DogRock. Extract them into separate directories on your computer. Put them under the document root of your local Web server (probably c:\xampp\htdocs\).
If you haven’t already, create two databases on your computer: dogtoys and dogrock. Create a user for each one, with access to the databases.
Now it’s time to create the tables. Both DogToys and DogRock have an SQL file you can import into phpMyAdmin. Each file will create a table and add some data. For DogToys, the file is dogtoys.sql. For DogRock, it’s dogrock.sql.
Import the files into their respective databases on your local computer.
The last step is to tell DogToys and DogRock what connection data (database, host, and user) to use. Both applications have a file called /library/db-connect.php. Open the one for DogToys. You’ll see something like this:
<?php //DB connection data. $host = 'localhost'; $db = 'dogtoys'; $user_name = 'dogtoys'; $password = 'dogtoys'; ?>
Figure 1. DogToys database connection parameters
Change these values to match your database. For example, if you gave your dogtoys user a password of playallday, use that.
Do the same for DogRock.
Start Apache and MySQL on your computer. Try the applications.
(Log in to enter your solution to this exercise.)
You created a jokes table on your local computer. Export it. This will create an SQL file on your computer. Store the file wherever you like.
Now import the file into the jokes database on your hosting account.
(Log in to enter your solution to this exercise.)
INT, DECIMAL, CHAR, TEXT, and DATE.INT field to be a primary key. Make it unsigned and auto_increment. MySQL will fill in a unique value.You’ll need a way to apply your database skills. In the next lesson, you’ll set up the Jokes application. You’ll add to it as we work through the rest of the chapter.
You’ll need a way to apply your database skills. In this lesson, you’ll set up the Jokes application. As we work through this chapter, you’ll add pages to Jokes, until it’s complete.
The Jokes application is much like DogToys and DogRock. It’s a template application, so it uses PHP for more than database work. You can try it.
In this lesson, you’ll:
jokes databaseCreate a database called jokes on your machine. A brief recap of the steps:
Create a MySQL user for the database. You’ll need this to connect you PHP code to the database.
Run the test program we used earlier. Here is the code again:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Strict//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title></title>
</head>
<body>
<h1>PHP-MySQL connection test</h1>
<?php
//DB connection data.
//Probably leave $host alone.
$host = 'localhost';
//Set $db to the name of your database.
$db = 'jokes';
//Set user_name to the name of the MySQL user you made.
$user_name = '[User name here]';
//Set $password to the user's password.
$password = '[Secret password here]';
?>
<p>Trying to connect to MySQL.</p>
<ul>
<li>Host name: <?php print $host; ?></li>
<li>Database name: <?php print $db; ?></li>
<li>User name: <?php print $user_name; ?></li>
<li>User password: (Secret)</li>
</ul>
<?php
//Connect to the MySQL server.
$db = new mysqli($host, $user_name, $password, $db);
//Did it work?
if ( mysqli_connect_error() ) {
print '<p>Error! Could not connect to the database. ';
print 'Error message: '.mysqli_connect_error().'</p>';
}
else {
print '<p>W00f! It worked!</p>';
}
?>
</body>
</html>
Figure 1. Test a database connection
Change the code on lines 12 to 18 to match your database.
jokes tableCreate a jokes table in the jokes database. Here are the fields and types I used in mine:
joke_id – INT, primary key, auto_incrementtitle – CHAR(30). A few words reminding me what the joke is about.joke_text – TEXT. The text of the joke itself.funniness – INT. How funny the joke is, from 1 (not funny) to 5 (hilarious).when_added – DATE. When the joke was added to the database.You can modify this list, if you want.
Add two or three jokes to the table.
Rather than asking you to write the entire application from scratch, I’ll give you all of the files with the database stuff removed. The PHP for templating is included.
Download the zip file. Extract it to a directory under your Web root, like c:\xampp\htdocs\jokes.
Try it in your browser.
Look through the files. Remind yourself how templating works.
You set up the Jokes application. It’s a starting point. You’ll add database code to it as we work though the chapter. You:
Let’s see how you let users add data to a database.
You know how to create databases, add tables, and test connections. Now let’s get our hands (brains?) dirty. Let’s see how you help users add data.
In this lesson, you will learn:
INSERT statement does the work.stripslashes() to remove backslashes that PHP adds to form data.$db->escape_string() to foil SQL injection attacks.Let’s see how data gets added to the DogToys database.
What’s the workflow when a user enters a new record?
First, the user clicks the Add product link on the administration menu.

Figure 1. Main DogToys administration screen
The user sees a form:

Figure 2. Adding product data
The user types in the data, and clicks the Save button. This sends the data to a PHP page that saves the data. That page then goes back to the administration menu.
So the workflow is:

Figure 3. Adding product workflow
Here’s the code for the form. I omitted some validation stuff; I’ll add it back in the next lesson.
<form id="new_product_form" method="post" action="save-new-product.php">
<p>
Name<br>
<input type="text" name="name" id="name" size="30">
</p>
<p>
Description<br>
<textarea name="description" id="description" rows="5" cols="30"></textarea>
</p>
<p>
Image file name<br>
<input type="text" name="image_file_name" id="image_file_name" size="30">
</p>
<p>
Price<br>
<input type="text" name="price" id="price" size="10">
</p>
<p>
<button type="submit">Save</button>
</p>
</form>
Figure 4. HTML for the form
First there’s the <form> tag:
<form id="new_product_form" method="post" action="save-new-product.php">
action tells the browser where to go to save-new-product.php when the user submits the form.
The rest of the code should be familiar. The <input type="text"> tags create one-line input fields. <textarea> creates a multi-line input field.
OK, let’s look at save-new-product.php, the PHP that saves the data. The pattern for the page is:
INSERT statement.Here is the entire code. We’ll run through it a step at a time.
<?php
//Save a new product.
//Input (all POST):
// name: Name of the product.
// description: Description of the product.
// image_file_name: Name of the file containing an image of the product.
// price: Selling price of the product.
$path_to_root = '..';
//Connect to DB.
require $path_to_root . '/library/db-connect.php';
$db = new mysqli($host, $user_name, $password, $db);
//Get the form fields.
$name = stripslashes($_POST['name']);
$description = stripslashes($_POST['description']);
$image_file_name = stripslashes($_POST['image_file_name']);
$price = stripslashes($_POST['price']);
//Make the fields safe.
$name = $db->escape_string($name);
$description = $db->escape_string($description);
$image_file_name = $db->escape_string($image_file_name);
$price = $db->escape_string($price);
//Create and run the SQL.
$query = "insert into products
(name, description, image_file_name, price)
values ('$name', '$description', '$image_file_name', $price)";
$db->query($query);
//Back to admin menu.
header('location:index.php');
exit();
?>
Figure 5. save-new-product.php
Let’s break it down into steps.
Lines 2 to 7 are documentation. They explain what the page does, and what input it expects. It’s good practice to add comments like this.
Line 9 sets the variable $path_to_root to the path from save-new-product.php (the file we’re talking about) to the root of the site. This is part of the templating system. Recall that one of the business goals of DogToys was to make the site easy to change. The templating system lets us do that.
The first step in the pattern is:
INSERT statement.Lines 11 and 12 connect to the database.
You could make a database connection with a line like this:
$db = new mysqli('localhost', 'dogtoys', 'password', 'dogtoys');
We’re going to be making connections to the database in many different PHP pages. The page that adds a record to the database needs to connect to the database. So does the page that saves edited data. And the page that deletes a record. And the product catalog page itself.
All of these pages would have a line like:
$db = new mysqli('localhost', 'dogtoys', 'password', 'dogtoys');
Now, what if we need to change the password? We’d need to find every one of these lines, in every page, and change it.
Ack!
There’s an easier way: put the connection information in a separate file, then include that file when needed. Here are the two lines from save-new-product.php:
require $path_to_root . '/library/db-connect.php'; $db = new mysqli($host, $user_name, $password, $db);
Part of Figure 5 (again). save-new-product.php
Line 11 loads db-connect.php. Here’s what’s in it:
<?php //DB connection data. $host = 'localhost'; $db = 'dogtoys'; $user_name = 'dogtoys'; $password = 'password'; ?>
Figure 6. db-connect.php
Line 12 uses the variables set in db-connect.php to make the connection.
Every page that needs to connect to the database uses db-connect.php.
If I want to change the password? I change one line in db-connect.php, and it’s changed for every page on the site.
Hooray! That’s another productivity win from reuse.
So we have a connection to the database. What’s next? Let’s look at the pattern.
INSERT statement.Here’s some more code from save-new-product.php.
//Get the form fields. $name = stripslashes($_POST['name']); $description = stripslashes($_POST['description']); $image_file_name = stripslashes($_POST['image_file_name']); $price = stripslashes($_POST['price']);
Part of Figure 5 (again). save-new-product.php
This gets the data the user typed into the form fields, and puts it into variables.
As we talked about earlier, PHP sometimes adds backslashes (\) to data typed into form fields. It’s trying to be “helpful.” The stripslashes() function gets rid of them. It’s good to send every value from the user through stripslashes().
What’s next?
INSERT statement.The next few lines are:
//Make the fields safe. $name = $db->escape_string($name); $description = $db->escape_string($description); $image_file_name = $db->escape_string($image_file_name); $price = $db->escape_string($price);
Part of Figure 5 (again). save-new-product.php
We’re going to create an SQL command from the data the user types in. It’s possible for an Evil Doer – one who knows SQL – to make trouble. Unless we do something about it.
Suppose someone typed this into the form:

Figure 7. Evil Doer at work
The DROP statement is an SQL command that erases a table. By inserting quotes, semicolons, and SQL, a clever Evil Doer can make our PHP program do bad things.
This is called an SQL injection attack. You can see the consequences at XKCD.
escape_string() will foil the Evil Doer. It will mess up the quotes, semicolons, and other special characters needed for an SQL injection attack.
Hooray!
So run all form data through escape_string() before you do anything with it.
What’s next?
INSERT statement.Here are the next few lines:
//Create and run the SQL.
$query = "insert into products
(name, description, image_file_name, price)
values ('$name', '$description', '$image_file_name', $price)";
$db->query($query);
Part of Figure 5 (again). save-new-product.php
This makes an SQL statement, and puts it into the variable $query. I split the statement across several lines to make it easier to read. PHP lets you do this when you use double quotes (”) around the string. SQL doesn’t care that statements are split across lines.
Here’s another example of line splitting in PHP:
$temp = "
<blockquote>
<p>I love dogs!</p>
</blockquote>";
The HTML inside the quotes is easy to follow.
Note all the single quotes (’) on line 29. It’s important to get them right. The field name is a text field, so you need to put quotes around the values you put in it.
The field price is a number. So no quotes for its value.
Line 30 sends the SQL to MySQL for execution.
What’s next?
INSERT statement.The end of save-new-product.php is:
header('location:index.php');
This tells the browser to jump back to index.php. There’s no directory, just a file name. save-new-product.php is in the /admin directory, so the browser will jump to /admin/index.php.
So that’s it.
INSERT statement.W00f!
That was DogToys. Let’s have a look at DogRock.
Writers add articles to DogRock. Let’s look at the overall workflow.
What’s the workflow when a user enters a new record?
First, the user clicks the “Add article” link on the administration menu.

Figure 8. Main DogRock administration screen
The user sees a form. S/he user types in the data, and clicks the Save button.

Figure 9. Adding an article
This sends the data to a PHP page, which saves the data. That page then goes back to the administration menu.
So the workflow is:

Figure 10. Workflow for adding an article
Here’s the code for the form. Again, I stripped out some validation stuff.
<form id="new_article_form" method="post" action="save-new-article.php">
<p>
Title<br>
<input type="text" name="title" id="title" size="30">
</p>
<p>
Author<br>
<input type="text" name="author" id="author" size="30">
</p>
<p>
Body<br>
<textarea name="body" id="body" rows="10" cols="30"></textarea>
</p>
<p>
<button type="submit">Save</button>
</p>
</form>
Figure 11. HTML for the form
First there’s the <form> tag:
<form id="new_article_form" method="post" action="save-new-article.php">
action tells the browser to send the data to the page save-new-article.php.
The rest of the code should be familiar. The <input type="text"> tags create one-line input fields. <textarea> creates a multi-line input field.
Let’s look at save-new-article.php, the PHP that saves the data. The pattern is:
INSERT statement.The pattern is the same as before, except for the fourth line.
Here’s the code. I’ll go over it step by step in a moment.
<?php
//Save a new article.
//Input (all POST):
// title: Title of the article.
// body: Body of the article.
// author: Author of the article.
$path_to_root = '..';
//Connect to DB.
require $path_to_root . '/library/db-connect.php';
$db = new mysqli($host, $user_name, $password, $db);
//Get the form fields.
$title = stripslashes($_POST['title']);
$body = stripslashes($_POST['body']);
$author = stripslashes($_POST['author']);
//Make the fields safe.
$title = $db->escape_string($title);
$body = $db->escape_string($body);
$author = $db->escape_string($author);
//Prepare the publish date.
$when_published = date('Y-m-d');
//Create and run the SQL.
$query = "insert into articles
(title, author, body, when_published)
values ('$title', '$author', '$body', '$when_published')";
$db->query($query);
//Back to admin menu.
header('location:index.php');
exit();
?>
Figure 12. save-new-article.php
Let’s break it down.
Lines 2 to 6 are documentation. They explain what the page does, and what input it expects.
The pattern is:
INSERT statement.Lines 10 and 11 connect to the database. As before, the connection parameters (host, user name, etc.) are in a separate file.
What’s next?
INSERT statement.Here’s some more code from save-new-article.php.
//Get the form fields. $title = stripslashes($_POST['title']); $body = stripslashes($_POST['body']); $author = stripslashes($_POST['author']);
Part of Figure 12 (again). save-new-article.php
This gets the data the user typed into the form fields, and puts it into variables. The stripslashes() function gets rid of the backslashes that PHP adds to form field data.
What’s next?
INSERT statement.Here’s the code for that step:
//Make the fields safe. $title = $db->escape_string($title); $body = $db->escape_string($body); $author = $db->escape_string($author);
Part of Figure 12 (again). save-new-article.php
As before, this messes up SQL injection attack.
What’s next?
INSERT statement.One of the things we store in the record for each article is the publication date. Rather than making the user type it, we can ask PHP what the current date is, and use that.
Here’s the line that does it:
$when_published = date('Y-m-d');
The date() function gets today’s date. The Y-m-d stuff gives a format: four digits for year, then a dash, then two digits for month, then a dash, then two digits for day. This is the international format that MySQL usually expects dates to be in.
What happens to the formatted date? It goes into the variable $when_published. We can use it just like the variables that contain the form data ($title, $body, and $author).
What’s next?
INSERT statement.Here’s the code:
//Create and run the SQL.
$query = "insert into articles
(title, author, body, when_published)
values ('$title', '$author', '$body', '$when_published')";
$db->query($query);
Part of Figure 12 (again). save-new-article.php
This makes an SQL statement, and puts it into the variable $query. Again, the single quotes need to be right on line 29.
Line 30 sends the SQL to MySQL for execution.
What’s next?
INSERT statement.The end of save-new-article.php is:
header('location:index.php');
This tells the browser to jump back to index.php. save-new-article.php is in the /admin directory, so the browswer will jump to /admin/index.php.
So that’s it.
INSERT statement.W00f!
Change the Jokes application so that users can add jokes.
Remember that there are two pages:
admin/add-joke.php).admin/save-new-joke.php). The first one you already have. It was in the zip file you downloaded. Your job is to write save-new-joke.php.
You can base it on save-new-product.php from DogToys and save-new-article.php from DogRock.
Don’t forget to adjust library/db-connect.php to use the right connection parameters.
Once you’ve finished, you can compare your solution with mine. But don’t look at it now!
If you want to share your solution with other people, you’ll need to:
(Log in to enter your solution to this exercise.)
INSERT statement does the work.stripslashes() to remove backslashes that PHP adds to form data.$db->escape_string() to foil SQL injection attacks.You know how to add a record. But what happens when something goes wrong? Let’s see.
Yow know how to help people add new data to a database. But what happens when things go wrong?
We’ve talked about error handling before. Both on the client side and the server side.
Using databases adds more things that can go wrong. In this lesson, we’ll see how you can handle database errors.
We’ll talk about two new types of errors:
You can check for both.
We’ll also talk about simple server-side validation. Even if you can check all data on the client, you should check it on the server as well. To foil Evil Doers.
There are two new types of errors:
A connection error is when a PHP program can’t connect to the database. An SQL error is when MySQL says something is wrong with an SQL statement. Like SELECT being typed as SELCT.
Let’s look at each type of error.
We had this earlier:
require $path_to_root . '/library/db-connect.php'; $db = new mysqli($host, $user_name, $password, $db);
Figure 1. Connecting to a database
If all is well, $db will be a valid connection object, something your PHP can use to send SQL to the database. But what if you have the wrong password, or user name? Or if the database server is down? Here’s how you check:
$db = new mysqli(...);
//Did it work?
if ( mysqli_connect_error() ) {
print '<p>Error! Could not connect to the database. ';
print 'Error message: '.mysqli_connect_error().'</p>';
exit();
}
Do something.
Figure 2. Checking for a connection error
mysqli_connect_error() will be FALSE if there was no connection error. Otherwise, it will contain an error message. So if there was no connection error, the if will skip over lines 4 to 7, and continue with normal processing on line 8.
exit() causes PHP to stop immediately and end the page.
So this is how you handle a connection error. But there are also…
It’s easy for errors to find their way into SQL. For example, suppose I tried to use this to create a new record in the DogRock database:
$db = new mysqli(...); ... $query = "insert into articles (title, author, body, when_published) values ($title, $author, $body, $when_published)"; $db->query($query);
Figure 3. SQL error
Can you see what’s wrong?
Quotes are missing. Suppose $title had the value Why I like fish. This:
...
values($title,…
would become:
...
values(Why I like fish,…
What we want is:
...
values('Why I like fish',…
The quotes keep everything together, so the title is treated as one value. When the quotes are missing, MySQL doesn’t know what to do.
Here’s how to check that an SQL query is correct:
$db = new mysqli(...);
...
$query = "insert into articles
(title, author, body, when_published)
values ($title, $author, $body, $when_published)";
$db->query($query);
if ( $db->error != '' ) {
print '<p>SQL error! Message: ' . $db->error . '</p>';
print "<p>Query:</p>
<blockquote>
$query
</blockquote>";
exit();
}
Figure 4. SQL error checking
Line 7 is:
if ( $db->error != '' ) {
If there is no error, then $db->error will be empty. If there is an error, then $db->error will not be empty.
If there’s an error, line 8 shows the error message. Lines 9 to 12 show the query that caused the error.
Line 13 stops the program immediately.
Summary so far – There are two new types of errors:
You’ve seen how to test for them.
So, if you don’t get any errors from the $db->error check, that means that everything is OK?
No!
It means that the format of the SQL statement was correct. But you might still have coding errors.
Suppose this was in your PHP:
$query = "insert into products
(name, description, image_file_name, price)
values ('$name', '$image_file_name', '$description', $price)";
$db->query($query);
Does that look OK?
Let me check… Hmm… Wait, the image file name and description are backwards. You have:
(name, description, image_file_name, price)
in one place, and:
('$name', '$image_file_name', '$description', $price)
in another.
Right! It’s a bug.
But the format of the SQL statement is just fine. MySQL would detect no errors, and would do what you told it to.
So what’s the point, if it won’t tell you about all of the errors?
$db->error will tell you about some of the errors. And that’s better than nothing. But you still need to look in the database, and make sure that your data was added correctly.
And we could use phpMyAdmin to check the data, right?
Yes. You can use phpMyAdmin to check whether your PHP program did what you thought it should.
Let’s see a new version of save-new-product.php. Recall that save-new-product.php is sent the form data the user types, and saves it to the database.
Here is the code with error checking:
<?php
//Save a new product.
//Input (all POST):
// name: Name of the product.
// description: Description of the product.
// image_file_name: Name of the file containing an image of the product.
// price: Selling price of the product.
$path_to_root = '..';
//Connect to DB.
require $path_to_root . '/library/db-connect.php';
$db = new mysqli($host, $user_name, $password, $db);
//Did it work?
if ( mysqli_connect_error() ) {
print '<p>Error! Could not connect to the database. ';
print 'Error message: '.mysqli_connect_error().'</p>';
exit();
}
//Get the form fields.
$name = stripslashes($_POST['name']);
$description = stripslashes($_POST['description']);
$image_file_name = stripslashes($_POST['image_file_name']);
$price = stripslashes($_POST['price']);
//Make the fields safe.
$name = $db->escape_string($name);
$description = $db->escape_string($description);
$image_file_name = $db->escape_string($image_file_name);
$price = $db->escape_string($price);
//Create and run the SQL.
$query = "insert into products
(name, description, image_file_name, price)
values ('$name', '$description', '$image_file_name', $price)";
$db->query($query);
if ( $db->error != '' ) {
print '<p>SQL error! Message: ' . $db->error . '</p>';
print "<p>Query:</p>
<blockquote>
$query
</blockquote>";
exit();
}
//Back to admin menu.
header('location:index.php');
exit();
?>
Figure 5. save-new-product.php
The code starting at line 14 checks whether the connection to MySQL was made successfully.
The code starting at line 37 checks whether the SQL statement ran without error.
Let’s do the same with DogRock.
<?php
//Save a new article.
//Input (all POST):
// title: Title of the article.
// body: Body of the article.
// author: Author of the article.
$path_to_root = '..';
//Connect to DB.
require $path_to_root . '/library/db-connect.php';
$db = new mysqli($host, $user_name, $password, $db);
if ( mysqli_connect_error() ) {
print '<p>Error! Could not connect to the database. ';
print 'Error message: '.mysqli_connect_error().'</p>';
exit();
}
//Get the form fields.
$title = stripslashes($_POST['title']);
$body = stripslashes($_POST['body']);
$author = stripslashes($_POST['author']);
//Make the fields safe.
$title = $db->escape_string($title);
$body = $db->escape_string($body);
$author = $db->escape_string($author);
//Prepare the publish date.
$when_published = date('Y-m-d');
//Create and run the SQL.
$query = "insert into articles
(title, author, body, when_published)
values ('$title', '$author', '$body', '$when_published')";
$db->query($query);
if ( $db->error != '' ) {
print '<p>SQL error! Message: ' . $db->error . '</p>';
print "<p>Query:</p>
<blockquote>
$query
</blockquote>";
exit();
}
//Back to admin menu.
header('location:index.php');
exit();
?>
Figure 6. save-new-article.php
It has shiny new error checking code.
I can see how this error checking is needed.
But aren’t most errors made by people? Maybe typing something wrong? How are they checked?
Ooo, good question! You’re right, people make more mistakes than computers. Let’s see how to check for those sorts of errors
We looked earlier at how to do validation. Remember that we did some client-side checking, and some server side checking.
We ended up with an architecture like this:

Figure 7. Validation architecture
order.php would do client-side checking with JavaScript. It would also do server-side checking with PHP. order.php would send form data to itself to do any server-side checking. Then, if all was OK, it would send the data to process.php.
We hadn’t written process.php at that point. But now we can write it, storing information to a database.
Let’s see how that will work for our two sample applications
Here’s the input form for adding a new product:

Figure 8. Add product form – add-product.php
What errors can the user make? Not many.
We could add other checks, like making sure that the image name is a valid file name. But let’s leave it for now.
Both of these checks can be done on the client side. And I added code to do them to add-product.php. Like this:
...
//Check the name field.
var name = $("#name").val();
if ( name == '' ) {
data_ok = false;
show_field_error_message('Sorry, there must be a name.', 'name');
}
else {
hide_error_message('name');
}
...
<p>
Name<br>
<input type="text" name="name" id="name" size="30"><br>
<span id="name_message_container" class="message_container">
<img src="<?php print $path_to_root; ?>/library/error.png" alt="Error">
<span id="name_message"/>
</span>
</p>
...
Figure 9. Client-side error checking
I used the same approach we used earlier to show error messages. The result is like this:

Figure 10. Client-side error
So all of our validation can be done on the client, in JavaScript.
But we want to add server-side validation anyway, in PHP.
Why?
Security. A smart hacker could grab the code from add-product.php (the page that shows the form). S/he could delete the JavaScript code that did the client-side error checks, and run the modified page. It would accept bad data (like a negative price), and send it to save-new-product.php for storage in the database. If save-new-product.php didn’t check the data, it would the evil data into the database.
So we’ll add some simple validation to save-new-product.php. Nothing fancy, but enough to make sure that evil data doesn’t get into the database.
Here’s yet another version of save-new-product.php.
<?php
//Save a new product.
//Input (all POST):
// name: Name of the product.
// description: Description of the product.
// image_file_name: Name of the file containing an image of the product.
// price: Selling price of the product.
$path_to_root = '..';
//Connect to DB.
require $path_to_root . '/library/db-connect.php';
$db = new mysqli($host, $user_name, $password, $db);
//Did it work?
if ( mysqli_connect_error() ) {
print '<p>Error! Could not connect to the database. ';
print 'Error message: '.mysqli_connect_error().'</p>';
exit();
}
//Get the form fields.
$name = stripslashes($_POST['name']);
$description = stripslashes($_POST['description']);
$image_file_name = stripslashes($_POST['image_file_name']);
$price = stripslashes($_POST['price']);
//Validate
if ( $name == '' ) {
print '<p>Error! Name is missing.</p>';
exit();
}
if ( $description == '' ) {
print '<p>Error! Description is missing.</p>';
exit();
}
if ( $image_file_name == '' ) {
print '<p>Error! Image file name is missing.</p>';
exit();
}
if ( $price == '' ) {
print '<p>Error! Price is missing.</p>';
exit();
}
if ( is_nan($price) ) {
print "<p>Error! Price is not a number: $price.</p>";
exit();
}
if ( $price <= 0 ) {
print "<p>Error! Price is too low: $price.</p>";
exit();
}
//Make the fields safe.
$name = $db->escape_string($name);
$description = $db->escape_string($description);
$image_file_name = $db->escape_string($image_file_name);
$price = $db->escape_string($price);
//Create and run the SQL.
$query = "insert into products
(name, description, image_file_name, price)
values ('$name', '$description', '$image_file_name', $price)";
$db->query($query);
if ( $db->error != '' ) {
print '<p>SQL error! Message: ' . $db->error . '</p>';
print "<p>Query:</p>
<blockquote>
$query
</blockquote>";
exit();
}
//Back to admin menu.
header('location:index.php');
exit();
?>
Figure 11. Another version of save-new-product.php
The new stuff starts at line 26. If there’s an error, a message is shown, and the program exits at once. This gives ugly error messages, but that’s acceptable. These errors should never happen, unless either:
Line 43 shows the “is not a number” function in PHP:
is_nan()
The syntax is a little different from JavaScript. In JS, it’s:
isNaN()This is JavaScript, not PHP.
But they do the same thing.
You could also use:
if ( ! is_numeric($price) ) {
is_numeric() is TRUE if you give it a numeric value. ! means “not.” So this version of the if says: “if it is not true that $price is numeric.”
Use either isNaN() or ! is_numeric(). Use the one that makes more sense to you.
Line 44 shows a good practice: it outputs the value that is incorrect. This helps in debugging.
Here’s some new code for save-new-article.php:
//Validate
if ( $title == '' ) {
print '<p>Error! Title is missing.</p>';
exit();
}
if ( $body == '' ) {
print '<p>Error! Body is missing.</p>';
exit();
}
if ( $author == '' ) {
print '<p>Error! Author is missing.</p>';
exit();
}
Figure 12. Code for save-new-article.php
It just makes sure that each field has a value.
Add error checking to save-new-joke.php. Check for:
How to check whether your error checking worked? You need to break stuff. Here are some suggestions.
add-joke.php form, and, before you click the Save button, use the XAMPP control panel to stop MySQL. Then click the Save button. This will simulate a DBMS crash.add-joke.php form, and, before you click the Save button, change admin/db-connect.php. Use the wrong password. Then click the Save button.save-joke.php. Now try to save bad data, like a joke with a funniness rating of -8. This simulates a hacker attack.One way to sabotage the client-side error checking is to add a new line:
$("#new_joke_form").submit(function() {
return true;
The return statement will make sure that the form data is always accepted, even if some fields are invalid.
Once you have it working, you can check my solution. But don’t look at it now!
If you want to share your solution with other people, you will need to upload it to your hosting server.
(Log in to enter your solution to this exercise.)
We talked about error checking and validation on this page. We saw two new types of errors:
You can check for them both.
We also talked about simple server-side validation. Even if you can check all data on the client, you should check it on the server as well. To foil Evil Doers.
You know how to add data to the database. But how do you show the data that was added? That’s coming up next.
You know how to add data to a table. Now let’s see how you can show it.
In this lesson, you learn that:
SELECT statement returns a record set. A record set has one or more rows of data.We want to make something like this:

Figure 1. Product catalog
The HTML uses the <table> tag. There’s a row for each product.
<Start table detour>
Let’s have a quick review of <table>s. Suppose we want to make a table like this:

Figure 2. Rendered table
Here’s the HTML code:
<table>
<thead>
<tr>
<th>Food</th>
<th>Rating</th>
</tr>
</thead>
<tbody>
<tr>
<td>Bacon</td>
<td>Yum!</td>
</tr>
<tr>
<td>Brussel sprouts</td>
<td>Yuck!</td>
</tr>
</tbody>
</table>
Figure 3. The <table> tag
The whole thing is wrapped in <table>.
The <tr> tag makes a row. The <td> makes a cell in a row. The “d” in <td> stands for “data.”
<th> makes a cell as well, but with some special formating that’s good for column headers (hence the “h” in <th>).
The <thead> and <tbody> tags separate the table into two regions, header and body. The tags help with styling. For example, you can easily give different background colors to the header and body of the table.
To get the spacing in Figure 2, use the following CSS:
table {
border-spacing: 10px;
}
td {
padding: 10px;
}
Figure 4. CSS for table spacing
This puts 10 pixels between each cell (line 2) and 10 pixels between the edges of each cell and its content (line 5).
That’s all you need to know about tables for now. You can read the table chapter if you want to know more.
</ End table detour>
So our PHP will output HTML code with the table tag. Here’s the rendered product page:

Figure 1 (again). Product catalog
Here’s the HTML that will make it.
<tr>
<td>
<img class='product_image' src='./product-images/ball.jpg'>
</td>
<td class='product_name_description_container'>
<p class='product_name'>Squeaky ball</p>
<p class='product_description'>Bouncing, squeaking fun!</p>
</td>
<td class='product_price'>1.99</td>
</tr>
<tr>
<td>
<img class='product_image' src='./product-images/frisbee.jpg'>
</td>
<td class='product_name_description_container'>
<p class='product_name'>Frisbee</p>
<p class='product_description'>A whirling disk of pure goodness! From <a href="http://www.wham-o.com/">Wham-O</a>.</p>
</td>
<td class='product_price'>10.95</td>
</tr>
<tr>
<td>
<img class='product_image' src='./product-images/rope.jpg'>
</td>
<td class='product_name_description_container'>
<p class='product_name'>Giant chew rope</p>
<p class='product_description'>Nom nom nom, and nom again. Chew this, not human shoes. You know how touchy they are.</p>
</td>
<td class='product_price'>12.95</td>
</tr>
Figure 5. HTML created by PHP
Here’s the pattern for PHP that will take data from a MySQL table, and make HTML to show it:
SELECT statement. SELECT is the SQL command to grab data.SELECT. Could be one record, or five, or 1,289.Here’s the code that will generate the HTML to show the product list. To make the code easier to follow, I’ve omitted some stuff having to do with sorting. I’ll add it back in the next lesson.
I’ve also omitted error checking, e.g., checking that the database connection was successful. That would clutter things, making it harder for you to focus on the core ideas of this lesson.
Here’s the code. We’ll go through it piece by piece.
<?php
//Connect to DB.
require $path_to_root . '/library/db-connect.php';
$db = new mysqli($host, $user_name, $password, $db);
//Create the query.
$query = "
select product_id, name, description, image_file_name, price
from products";
//Run the query.
$record_set = $db->query($query);
//Start the output table.
?>
<table class="product_table">
<thead>
<tr>
<th> </th>
<th>Name</th>
<th>Price</th>
</tr>
</thead>
<tbody>
<?php
//Loop across records.
while( $row = $record_set->fetch_assoc() ) {
//Get fields for a product.
$product_id = $row['product_id'];
$name = $row['name'];
$description = $row['description'];
$image_file_name = $row['image_file_name'];
$price = $row['price'];
//Output
print "
<tr>
<td>
<img class='product_image' src='$path_to_root/product-images/$image_file_name'>
</td>
<td class='product_name_description_container'>
<p class='product_name'>$name</p>
<p class='product_description'>$description</p>
</td>
<td class='product_price'>$price</td>
</tr>";
} //End while.
?>
</tbody>
</table>
Figure 6. PHP to show the product catalog
Lines 3 and 4 open the database. As usual.
Lines 6 to 8 create the SQL query that fetches the data. I broke it across lines to make it easier for me to read. The statement has the form:
SELECTfieldsFROMtable;
When you send this to MySQL:
select name, description, image_file_name, price from products;
You are telling MySQL:
Open up the
productstable, and, for each row, fetchname,description,image_file_name, andprice.
Here’s what you get:

Figure 7. A record set from MySQL
Hmm, that looks like part of a phpMyAdmin screen.
It is. I copied the SQL query from my code, clicked the SQL tab in phpMyAdmin, and pasted in the query. Clicked the Go button, and MySQL ran it.
That’s a good way to test your own queries.
Once the query has been created, it gets run in line 10:
$record_set = $db->query($query);
query() returns a record set, that gets put into the variable $record_set. The record set is this:

Figure 7 (again). A record set from MySQL
It’s the results of running the query.
We’re used to a variable containing something simple, like a number or some text:
$x = 42;
$y = 'The answer!';
But $record_set is different. It contains a complex object.
We don’t care too much what an object really is. Just think of it as a blob of data. There can be all kinds of data in an object.
You need special functions to get at the data inside an object. We’ll see some in a moment.
OK, so we’ve:
Now we need to show the records in a <table>. There’s some code in Figure 6 to output the <table> tag and the header:
<table class="product_table">
<thead>
<tr>
<th> </th>
<th>Name</th>
<th>Price</th>
</tr>
</thead>
<tbody>
Part of Figure 6. PHP to show the product catalog
Line 16 is a little strange.
<th> </th>
The first column in the output table has the item’s picture. It doesn’t need a heading. So line 16 creates an empty heading cell.
Here’s the next part of the code, where the meat is:
<?php
//Loop across records.
while( $row = $record_set->fetch_assoc() ) {
//Get fields for a product.
$name = $row['name'];
$description = $row['description'];
$image_file_name = $row['image_file_name'];
$price = $row['price'];
//Output
print "
<tr>
<td>
<img class='product_image' src='$path_to_root/product-images/$image_file_name'>
</td>
<td class='product_name_description_container'>
<p class='product_name'>$name</p>
<p class='product_description'>$description</p>
</td>
<td class='product_price'>$price</td>
</tr>";
} //End while.
?>
Part of Figure 6. PHP to show the product catalog
while is a new PHP statement for us. It’s a loop. It executes the same PHP statements a bunch of times.
Its format is:
while(test) {
Do this
}
It says:
Keep doing “Do this” while the test is true.
Here’s an example:
<?php
$x = 1;
while ( $x <= 5 ) {
print "$x<br>";
$x += 1;
}
?>
Figure 8. while loop example
This says:
While
$xis less than or equal to 5:
Output$xand a line break tag.
Add 1 to$x.
The PHP engine will keep doing lines 4 and 5 while the condition is true.
Line 5 is a common PHP shortcut. += means “add to.” So:
$x += 1;
means “Add 1 to what is already in $x.”
Here’s the HTML the code generates:
1<br>2<br>3<br>4<br>5<br>
Here’s how it renders:

Figure 9. Rendered output
Let’s have another look at the loop in our database program:
while( $row = $record_set->fetch_assoc() ) {
Stuff to do
}
The PHP engine will keep looping around, doing “Stuff to do” while $row = $record_set->fetch_assoc() is true. When $row = $record_set->fetch_assoc() stops being true (when it is false), the PHP engine stops the loop. It will skip to the code after the loop.
Let’s look at:
$record_set->fetch_assoc()
Recall that $record_set is a record set object. It contains this data:

Figure 7 (again). A record set from MySQL
fetch_assoc() is a function that returns one row.
When a function belongs to an object, it’s really called a “method.” But let’s keep calling it a function for simplicity.
A record set object has other data besides some rows. It also has a record pointer:

Figure 10. Record pointer
The record pointer starts off pointing to the first record.
When you use the fetch_assoc() function, you are saying two things:
What does “Give me the current record” mean? Here’s the code again:
$row = $record_set->fetch_assoc()
fetch_assoc() copies $record_set’s current record into $row. fetch_assoc() doesn’t remove the data from $record_set. It’s still there. fetch_assoc() copies the row, and sends it to $row.
Then fetch_assoc() moves the pointer down. So after fetch_assoc() has run once, we would have:

Figure 11. fetch_assoc() has run once
fetch_assoc() has returned the record it was pointing to, and moved the pointer down one record.
Run fetch_assoc() a second time, and you get:

Figure 12. fetch_assoc() has run twice
Run fetch_assoc() a third time, and you get:

Figure 13. fetch_assoc() has run three times
The record pointer has moved beyond the end of the table.
Run fetch_assoc() a fourth time, and you get:

Figure 14. fetch_assoc() has run four times
The record pointer is pointing to an empty space, so $row = $record_set->fetch_assoc() puts NULL into $row. NULL acts like FALSE in PHP’s if(), while(), and other statements.
Here’s the loop again:
while( $row = $record_set->fetch_assoc() ) {
Stuff to do
}
This will do “Stuff to do” again and again. The first time, $row will have the first record. The second time, $row will have the second record. And so on.
The last time, $row will have the last record. When the while() tries to run again, fetch_assoc() will return NULL, which acts like FALSE. So the loop will stop. Execution will pick up after the brace (}) that ends the loop.
What we have now is a way to grab each record in the record set. Each one gets put into $row. But what do we do with $row?
Remember our goal. We want to create a <table> that contains rows like this:
<tr>
<td>
<img class='product_image' src='./product-images/ball.jpg'>
</td>
<td class='product_name_description_container'>
<p class='product_name'>Squeaky ball</p>
<p class='product_description'>Bouncing, squeaking fun!</p>
</td>
<td class='product_price'>1.99</td>
</tr>
<tr>
<td>
<img class='product_image' src='./product-images/frisbee.jpg'>
</td>
<td class='product_name_description_container'>
<p class='product_name'>Frisbee</p>
<p class='product_description'>A whirling disk of pure goodness! From <a href="http://www.wham-o.com/">Wham-O</a>.</p>
</td>
<td class='product_price'>10.95</td>
</tr>
<tr>
<td>
<img class='product_image' src='./product-images/rope.jpg'>
</td>
<td class='product_name_description_container'>
<p class='product_name'>Giant chew rope</p>
<p class='product_description'>Nom nom nom, and nom again. Chew this, not human shoes. You know how touchy they are.</p>
</td>
<td class='product_price'>12.95</td>
</tr>
Figure 5 (again). HTML created by PHP
Lines 1 to 10 are for the first product. They were created from the first row in the record set:

Figure 7 (again). A record set from MySQL
The second row in the table was created from the second row in the record set.
Here’s the entire loop again:
while( $row = $record_set->fetch_assoc() ) {
//Get fields for a product.
$name = $row['name'];
$description = $row['description'];
$image_file_name = $row['image_file_name'];
$price = $row['price'];
//Output
print "
<tr>
<td>
<img class='product_image' src='$path_to_root/product-images/$image_file_name'>
</td>
<td class='product_name_description_container'>
<p class='product_name'>$name</p>
<p class='product_description'>$description</p>
</td>
<td class='product_price'>$price</td>
</tr>";
} //End while.
Part of Figure 6 (again). PHP to show the product catalog
This from line 24:
$row = $record_set->fetch_assoc()
puts a row from the record set into $row.
Remember that $record_set doesn’t have a single value, like 7 or bark. It’s an object, with a bunch of data.
@$row$ doesn’t contain a single value, either. But it isn’t a record set object. It’s an array. We don’t care right now about arrays. Just know that an array contains a bunch of values that you can access.
Remember the SQL query used to create the record set:
select name, description, image_file_name, price from products
So record (row) in the record set has the fields name, description, image_file_name, and price. When:
$row = $record_set->fetch_assoc()
puts the current record in $row, it puts four values into $row.
Here are the next few lines of code:
//Get fields for a product.
$name = $row[‘name’];
$description = $row[‘description’];
$image_file_name = $row[‘image_file_name’];
$price = $row[‘price’];
You can access array elements like this:
$array_name['element name']
So…
$name = $row['name'];
... gets the name element from $row and puts it into the variable $name. $name is not a complex object. It’s just a variable with a simple string in it.
We end up with four variables containing the name, description, image file name, and price for the current record.
Then we can output the variables:
//Output
print "
<tr>
<td>
<img class='product_image' src='$path_to_root/product-images/$image_file_name'>
</td>
<td class='product_name_description_container'>
<p class='product_name'>$name</p>
<p class='product_description'>$description</p>
</td>
<td class='product_price'>$price</td>
</tr>";
Part of Figure 6 (again). PHP to show the product catalog
This PHP statement outputs the fields, inserting them into some HTML, to create a table row.
Here’s all the code again:
<?php
//Connect to DB.
require $path_to_root . '/library/db-connect.php';
$db = new mysqli($host, $user_name, $password, $db);
//Create the query.
$query = "
select product_id, name, description, image_file_name, price
from products";
//Run the query.
$record_set = $db->query($query);
//Start the output table.
?>
<table class="product_table">
<thead>
<tr>
<th> </th>
<th>Name</th>
<th>Price</th>
</tr>
</thead>
<tbody>
<?php
//Loop across records.
while( $row = $record_set->fetch_assoc() ) {
//Get fields for a product.
$product_id = $row['product_id'];
$name = $row['name'];
$description = $row['description'];
$image_file_name = $row['image_file_name'];
$price = $row['price'];
//Output
print "
<tr>
<td>
<img class='product_image' src='$path_to_root/product-images/$image_file_name'>
</td>
<td class='product_name_description_container'>
<p class='product_name'>$name</p>
<p class='product_description'>$description</p>
</td>
<td class='product_price'>$price</td>
</tr>";
} //End while.
?>
</tbody>
</table>
Figure 6 (again). PHP to show the product catalog
You should be able to follow it now.
Lines 3 and 4 connect to the database.
Lines 6 to 10 run a SELECT query, fetching a record set from MySQL.
Lines 13 to 21 output the table header.
Lines 24 to 43 loop across the record set, showing a table row for each product.
Lines 45 and 46 close the table’s HTML.
W00f!
That’s a lot of new stuff we’ve covered. If you need a brain break, take one.
Let’s take a look at the second sample application, the DogRock CMS.
We want to make something like this:

Figure 15. Article list
Here’s part of the HTML that shows the table:
<tr> <td>A new howl on the prowl</td> <td>February 5, 2010</td> <td>Lumis</td> </tr> <tr> <td>Somebody let them out!</td> <td>February 3, 2010</td> <td>Bounder</td> </tr>
Figure 16. HTML for the article list
I’ve omitted some stuff for simplicity. It’ll come back later.
Here’s the PHP that creates the HTML that makes the page.
<?php
//Connect to DB.
require $path_to_root . '/library/db-connect.php';
$db = new mysqli($host, $user_name, $password, $db);
//Create the query.
$query = "
select title, author, when_published
from articles";
//Run the query.
$record_set = $db->query($query);
//Start the output table.
?>
<table cellpadding="10" cellspacing="10" border="0">
<thead>
<tr>
<th>Title</th>
<th>Date</th>
<th>Author</th>
</tr>
</thead>
<tbody>
<?php
//Loop across records.
while( $row = $record_set->fetch_assoc() ) {
//Get fields for an article.
$title = $row['title'];
$when_published = $row['when_published'];
$author = $row['author'];
//Format the date.
$when_published = date('F j, Y', strtotime($when_published));
//Output
print "
<tr>
<td>$title</td>
<td>$when_published</td>
<td>$author</td>
</tr>";
} //end while
?>
</tbody>
</table>
Figure 17. Article list code
Lines 3 and 4 connect to the database.
Lines 6 to 8 create an SQL query:
select title, author, when_published from articles
This says:
From the
articlestable, fetch the fieldstitle,author, andwhen_publishedfor every record.
Why “every record?” Because there’s no WHERE clause. Like this:
select title, author, when_published from articles where author='Bounder'
This would fetch data only for the rows that have Bounder in the author field.
But we don’t have a WHERE clause, so all rows are returned.
Lines 13 to 21 output the table header and such.
The fun starts here:
//Loop across records.
while( $row = $record_set->fetch_assoc() ) {
//Get fields for an article.
$title = $row['title'];
$when_published = $row['when_published'];
$author = $row['author'];
//Format the date.
$when_published = date('F j, Y', strtotime($when_published));
//Output
print "
<tr>
<td>$title</td>
<td>$when_published</td>
<td>$author</td>
</tr>";
} //end while
Part of Figure 15. Article list code
Line 24 fetches a row from the record set. Lines 26 to 28 fetch fields from the row into variables.
Line 30 formats the date in $when_published. The format in the database is YYYY-MM-DD, like 1960-01-03 (my birthday – yes, I’m old). This is the standard international format, but most people aren’t used to it.
This code…
date('F j, Y', strtotime($when_published))
... uses the date() function to format the date in the common “Month Day, Year” format, such as “January 3, 1960.” date() expects the date you give it to be in a certain format – a Unix timestamp. strtotime() converts a YYYY-MM-DD date (from the database) into a Unix timestamp (that the date() function needs).
The output from the date() function is put back into $when_published:
$when_published = date('F j, Y', strtotime($when_published));
The last few lines show the record:
//Output
print "
<tr>
<td>$title</td>
<td>$when_published</td>
<td>$author</td>
</tr>";
So that’s it! We now have a list of articles. W00f!
There’s something missing here, though. Here’s the output we want:

Figure 15 (again). Article list
We want the name of the article to be a clickable link. We’ll look at that later.
Write jokes.php, a page that will show the jokes in your database. You already have a version of the page; it was in the download. You need to add the database logic.
Include error checking code. Check that:
Don’t worry about sorting the records. We’ll talk about that later.
Don’t include the links to the individual jokes. That comes later, too.
Once you have it running, you can check my solution. But don’t look at it now!
Jokes also puts a list of jokes on the home page, like DogRock does. Do that too. Don’t worry about sorting, links, or limiting the number of records. That comes later.
You can check my solution once you have it running.
(Log in to enter your solution to this exercise.)
SELECT statement returns a record set. A record set has one or more rows of data.Let’s see how you can let users sort the data.
You know how to show users the data in a database. What if they want to sort the data? Let’s see how you do that.
In this lesson, you will learn:
ORDER BY clause of the SELECT statement tells MySQL to sort records in the record set.ORDER BY, depending on the parameter.LIMIT clause of the SELECT statement sets the maximum number of records MySQL will add to a record set.Recall that the SQL SELECT command returns a bunch of rows:
select name, description, image_file_name, price from products;
You can tell MySQL how to sort the returned rows. Add ORDER BY and then the field to sort by.
For example, this…
select name, description, image_file_name, price from products order by name;
... returns:

Figure 1. Sorted by name
This…
select name, description, image_file_name, price from products order by image_file_name;
... returns:

Figure 2. Sorted by image_file_name
This…
select name, description, image_file_name, price from products order by price;
... returns:

Figure 3. Sorted by price
By default, the records are sorted in ascending order, from smaller to larger. But you can change this. For example, if you want to sort by price from high to low:
select name, description, image_file_name, price from products order by price desc;
I added desc for “descending” after the sort field name. This returns:

Figure 4. Sorted by price, descending
Sorting is not difficult. But it’s trickier to…
We want to let the user choose how to sort the data. Here’s the interface for the product list:

Figure 5. Sort interface for products
The user can click on the product name to sort by name, or click on an arrow to sort by price, from low to high, or high to low.
You can try it.
How does this work? The URLs give a clue. Here’s how the URL changes when the user clicks on the “Price” link.

Figure 6. User sorting by price
The URL of the page is normally products.php. This gives a default sort order, by name. Then the user clicks the sort arrow. The HTML for the arrow is:
<a href="products.php?order=price_asc">
<img src="./library/arrow_down.png"
alt="Sort from low to high">
</a>
Figure 7. HTML for a sort arrow in products.php
Look at the href in line 1 of Figure 7:
products.php?order=price_asc
So the page points to itself, but with a parameter order.
When this renders (Figure 6), the user sees the data sorted ascending by price.
For this to work, we need:
Let’s look at the code for DogToys.
Here’s products.php:
<?php
//Connect to DB.
require $path_to_root . '/library/db-connect.php';
$db = new mysqli($host, $user_name, $password, $db);
//Create the query.
$query = "
select product_id, name, description, image_file_name, price
from products
order by ";
//Append the sort order.
$order = $_GET['order'];
if ( $order == 'price_asc' ) {
$query .= 'price asc';
}
else if ( $order == 'price_desc' ) {
$query .= 'price desc';
}
else {
//Default sort order.
$query .= 'name';
}
//Run the query.
$record_set = $db->query($query);
//Start the output table.
?>
<table cellpadding="5" cellspacing="0" border="0">
<thead>
<tr>
<th> </th>
<th><a href="products.php?order=name">Name</a></th>
<th>Price<br>
<a href="products.php?order=price_asc">
<img src="<?php print $path_to_root; ?>/library/arrow_down.png"
alt="Sort from low to high">
</a>
<a href="products.php?order=price_desc">
<img src="<?php print $path_to_root; ?>/library/arrow_up.png"
alt="Sort from high to low">
</a>
</th>
</tr>
</thead>
<tbody>
<?php
//Loop across records.
while( $row = $record_set->fetch_assoc() ) {
...
Figure 8. products.php
I’ve omitted some code for simplicity.
We need to add the ORDER BY to the SQL. That’s what lines 9 to 21 do.
Line 9 adds “order by” to the SQL statement. But the statement is not complete. Order by what? That’s what the next few lines figure out.
Line 11 …
$order = $_GET['order'];
... fetches the value of the GET parameter order, if there is one. Recall that GET passes data to a Web page through the URL. The value of order is put in the PHP variable $order.
Here are the next few lines (lines 12 to 14):
if ( $order == 'price_asc' ) {
$query .= 'price asc';
}
Recall that .= means “append” or “concatenate.” In normal words, “add to the end of.” So id the variable $order contained price_asc, then price asc would get appended to the SQL statement. It would end like this:
...
order by price asc
Now the order by clause is complete. When the SQL statement is run, the records will be sorted by price in ascending order.
Lines 15 to 17 append price desc to the SQL statement, if $order contains price_desc:
if ( $order == 'price_desc' ) {
$query .= 'price desc';
}
If $order contains anything else, or nothing at all, then line 20 sets the default sort order to name:
$query .= 'name';
The SQL query now has a complete ORDER BY clause, so MySQL will sort the data before returning it.
The last thing we need is the interface, that is, a way for the user to select the sort order. That’s what lines 31, 33, and 37 do:
<a href="products.php?order=name">
<a href="products.php?order=price_asc">
<a href="products.php?order=price_desc">
Clicking these links will cause products.php to reload itself, but with a different sort order.
Here’s the code for articles.php:
<?php
//Connect to DB.
require $path_to_root . '/library/db-connect.php';
$db = new mysqli($host, $user_name, $password, $db);
//Create the query.
$query = "select article_id, title, author, when_published
from articles
order by ";
//Append the sort order.
$order = $_GET['order'];
if ( $order == 'date' ) {
$query .= 'when_published desc';
}
else if ( $order == 'author' ) {
$query .= 'author';
}
else {
//Default sort order.
$query .= 'title';
}
//Run the query.
$record_set = $db->query($query);
//Start the output table.
?>
<table cellpadding="10" cellspacing="10" border="0">
<thead>
<tr>
<th><a href="articles.php?order=title">Title</a></th>
<th><a href="articles.php?order=date">Date</a></th>
<th><a href="articles.php?order=author">Author</a></th>
</tr>
</thead>
<tbody>
<?php
//Loop across records.
while( $row = $record_set->fetch_assoc() ) {
...
Figure 9. articles.php
It works the same way as products.php. Clicking a link (lines 28, 29, and 30) causes articles.php to reload itself, passing a sort order.
Line 10 gets the order from the URL.
Lines 11 to 20 complete the SQL statement, depending on which sort order the user selected.
The DogRock site also shows articles on its home page. It shows the three most recent:

Figure 10. Most recent articles
They are sorted descending by date, so the most recent article is first.
Here’s the code for the page, with some things omitted.
<?php
//Connect to DB.
require $path_to_root . '/library/db-connect.php';
$db = new mysqli($host, $user_name, $password, $db);
//Fetch the three most recently published articles.
$query = "select article_id, title, when_published
from articles
order by when_published desc
limit 3";
$record_set = $db->query($query);
//Loop across records.
while( $row = $record_set->fetch_assoc() ) {
//Get fields for an article.
$article_id = $row['article_id'];
$title = $row['title'];
$when_published = $row['when_published'];
//Format the date.
$when_published = date('F j, Y', strtotime($when_published));
print "<p>$title ($when_published)</p>";
} //End while.
?>
Figure 11. index.php for DogRock
Line 8 adds the sort order:
order by when_published desc
The desc means the the highest (most recent) articles are first.
Line 9 shows something new:
limit 3
MySQL will limit the record set it returns to the first three records.
The rest is as before.
Change jokes.php so that users can sort by title, funniness, and date. Don’t worry about linking a joke’s title to its content; that comes later.
You can check my solution. But do it yourself first!
Now change the home page so that the user sees the three funniest jokes.
You can check my solution. But, as before, do it yourself first!
(Log in to enter your solution to this exercise.)
ORDER BY clause of the SELECT statement tells MySQL to sort records in the record set.ORDER BY, depending on the parameter.LIMIT clause of the SELECT statement sets the maximum number of records MySQL will add to a record set.Let’s see how you let users “drill down,” that is, go from summary data (e.g., just the title of an article) to details (e.g., all of the data about the article).
You know how to create a listing of data, like the DogRocks’ article list. But each listing only shows the title of the article, not the article itself.
In this lesson, you will learn that:
“Drilling down” means to get more detailed information, to go from a summary to detail.
Here’s the article list from DogRock:

Figure 1. Article list, from articles.php
The reader gets a little information on what each article is about: the title. If the reader wants more, s/he can click on the title of the article. For example, clicking on the first one shows:

Figure 2. An article
How does this work? Here’s some of the HTML that creates Figure 1, the article list:
<tr> <td><a href='show-article.php?id=2'>A new howl on the prowl</a></td> <td>February 5, 2010</td> <td>Lumis</td> </tr> <tr> <td><a href='show-article.php?id=1'>Somebody let them out!</a></td> <td>February 3, 2010</td> <td>Bounder</td> </tr>
Figure 3. HTML generated by articles.php
Lines 1 to 5 show a table row for an article. You can see the link to the article on line 2:
<a href='show-article.php?id=2'>
show-article.php will show an article, if you send it the article’s id. If you want a different article, send it a different id.
Here’s a picture of what’s happening:

Figure 4. Link to article
You can see the HTML that makes the link. The user clicks on the link. The browser jumps to show-article.php, passing id of the article to show.
What are the ids? They’re from the database:

Figure 5. articles table
The article ids are the primary key values.
So we need to do two things to get this working:
articles.php, to create links to the articles, links like <a href='show-article.php?id=2'>.show-article.php, so it will take an id, and show the article with that id.Let’s look at each piece.
Here’s code from articles.php. I’ve omitted error control and sorting.
<?php
//Connect to DB.
require $path_to_root . '/library/db-connect.php';
$db = new mysqli($host, $user_name, $password, $db);
//Create the query.
$query = "select article_id, title, author, when_published
from articles";
//Run the query.
$record_set = $db->query($query);
//Start the output table.
...
//Loop across records.
while( $row = $record_set->fetch_assoc() ) {
//Get fields for an article.
$article_id = $row['article_id'];
$title = $row['title'];
$when_published = $row['when_published'];
$author = $row['author'];
//Format the date.
$when_published = date('F j, Y', strtotime($when_published));
//Output
print "
<tr>
<td><a href='show-article.php?id=$article_id'>$title</a></td>
<td>$when_published</td>
<td>$author</td>
</tr>";
} //end while
?>
Figure 6. articles.php
Lines 3 and 4 connect to the MySQL database.
Lines 6 to 9 create and run the MySQL query. Here it is:
select article_id, title, author, when_published from articles
The query retrieves article_id, because we’ll need it to make the link to show-article.php. show-article.php needs to know which article to show.
Here’s the data the query might fetch:

Figure 7. Article data
The while() loop (lines 13 t0 28) works as before. It grabs the fields from each row, including each record’s article_id and title. They’re put in the variables $article_id and $title (lines 15 and 16).
The program outputs the link to each article with line 24:
<a href='show-article.php?id=$article_id'>$title</a>
If $article_id is 2 and $title is A new howl on the prowl (from the first record in Figure 7), then we get:
<td><a href='show-article.php?id=2'>A new howl on the prowl</a></td>
W00f! We have the article list.
How do we show the article? Here’s show-article.php, with error checking omitted.
<?php
//Connect to DB.
require $path_to_root . '/library/db-connect.php';
$db = new mysqli($host, $user_name, $password, $db);
//Fetch article data.
$article_id = $_GET['id'];
$query = "select title, body, when_published, author
from articles
where article_id = " . $article_id;
$record_set = $db->query($query);
$row = $record_set->fetch_assoc();
//Get fields.
$title = $row['title'];
$body = $row['body'];
$when_published = $row['when_published'];
$author = $row['author'];
//Format the date.
$when_published = date('F j, Y', strtotime($when_published));
//For the body, change new lines into <br> tags.
$body = str_replace("\n", "<br>", $body);
//Output.
print "
<h2>$title</h2>
<p>By $author</p>
<p>$when_published</p>
<p>$body</p>
";
?>
Figure 8. show-article.php
Line 6 gets the id of the article from the URL.
Lines 7 to 9 use the id to create an SQL statement to fetch the article with that id. It will fetch just one article. Remember that article_id is the primary key of the article table, so each row in the table will have a different value for article_id.
Line 10 runs the query, getting a record set back from MySQL. It puts the record set into the variable $record_set.
Line 11 takes the row from the record set:
$row = $record_set->fetch_assoc();
There is only one row, so no loop is needed.
Lines 13 to 16 extract the fields from the $row. Line 18 formats the date.
I don’t see the article id there. Why don’t you have something like this?
$article_id = $row['article_id'];
article_id was passed into the page. The page got it on line 6:
$article_id = $_GET['id'];
So there was no need to fetch it again from the database.
Lines 20 is something new. The body field of the articles table contains the main text of the article. When a writer types it in, s/he might do something like this:

Figure 9. Typing in an article
The writer added a blank line between the two paragraphs. It gets stored this way my MySQL:

Figure 10. Article body shown by phpMyAdmin
But, as you know, browsers don’t render whitespace in HTML. And an empty line is whitespace.
Take this:
<blockquote> <p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas vitae quam quam. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec commodo vehicula neque, vitae mollis ligula bibendum sit amet. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas vitae quam quam. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec commodo vehicula neque, vitae mollis ligula bibendum sit amet. </p> </blockquote>
Figure 11. Empty line in body
It will render as:
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas vitae quam quam. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec commodo vehicula neque, vitae mollis ligula bibendum sit amet. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas vitae quam quam. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec commodo vehicula neque, vitae mollis ligula bibendum sit amet.
Figure 12. Empty line in body, rendered
The two paragraphs are smooshed together. There isn’t any whitespace between them, because browsers don’t render whitespace in HTML.
How can we make sure that the empty lines in the body are rendered by the browser?
Here’s one way, from line 20 of the code:
$body = str_replace("\n", "<br>", $body);
\n stands for the “new line” character. It’s the character that your keyboard makes when you press the Enter key. The str_replace() function goes through $body, and replaces all of the \ns with <br>s. As you, the <br> renders as a line break.
By the way, it’s important to use double quotes – "\n" – rather than single quotes – '\n'. PHP will only interpret the \n as the new line character when it’s in double quotes.
Here’s what $body looks like before the str_replace():
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas vitae quam quam. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec commodo vehicula neque, vitae mollis ligula bibendum sit amet. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas vitae quam quam. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec commodo vehicula neque, vitae mollis ligula bibendum sit amet.
It contains two \ns, one at the end of line 1, and other at the end of line 2. They’re might have been one at the end of line 3, but the writer didn’t type it.
Here’s what $body looks like after the substitution:
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas vitae quam quam. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec commodo vehicula neque, vitae mollis ligula bibendum sit amet. <br> <br>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas vitae quam quam. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec commodo vehicula neque, vitae mollis ligula bibendum sit amet.
The two <br>s make the browser show the empty line. W00f!
Here’s the rest of the code from show-article.php:
//Output. print " <h2>$title</h2> <p>By $author</p> <p>$when_published</p> <p>$body</p> ";
Figure 13. Part of show-article.php
It outputs the article data.
Change jokes.php so that each joke title is a link to the page show-joke.php. This page shows all of the details of a joke. You can see the page in action on my sample site.
Do the same for the home page.
You can see my code for:
But don’t look at them now! Do it yourself first.
(Log in to enter your solution to this exercise.)
You learned:
You know how to add records, and show them. What about maintaining the data? How do we let people edit and delete records?
That’s what we look at next. But we start with the administration menu. It lets users select the records they want to edit or delete.
Learn that:
Both DogToys and DogRock have two parts to their sites:
The pages for the administration part of the site are all in the admin directory. Here is the layout of the DogToys site:

Figure 1. DogToys directory layout
The administration menu (admin/index.php) gives users access to the all of the administrative functions. This is what it looks like:

Figure 2. DogToys administration menu
At the top is a link to add a new record. At the bottom is a list of existing records, with Edit and Delete links. This is much like the list of products on the product catalog, but with some extra stuff.
Let’s see how this works.
Here’s the code. I’ve included all the sorting and error checking.
<p>What do you want to do?</p>
<blockquote>
<p><a href="add-product.php">Add a new product</a></p>
</blockquote>
<h2>Current products</h2>
<?php
//List the current products, along with edit/delete action for each one.
//Connect to DB.
require $path_to_root . '/library/db-connect.php';
$db = new mysqli($host, $user_name, $password, $db);
if ( mysqli_connect_error() ) {
print '<p>Error! Could not connect to the database. ';
print 'Error message: '.mysqli_connect_error().'</p>';
exit();
}
//Fetch product data.
$query = "select product_id, name, description,
image_file_name, price
from products
order by ";
//Get the product list order, if given.
$order = $_GET['order'];
if ( $order == 'price_asc' ) {
$query .= 'price asc';
}
else if ( $order == 'price_desc' ) {
$query .= 'price desc';
}
else {
//Default sort order.
$query .= 'name';
}
$record_set = $db->query($query);
if ( $db->error != '' ) {
print '<p>SQL error! Message: ' . $db->error . '</p>';
print "<p>Query:</p>
<blockquote>
$query
</blockquote>";
exit();
}
//Start the product table.
?>
<table cellpadding="5" cellspacing="0" border="0">
<thead>
<tr>
<th> </th>
<th><a href="index.php?order=name">Name</a></th>
<th>Description</th>
<th>Price<br>
<a href="index.php?order=price_asc">
<img src="<?php print $path_to_root; ?>/library/arrow_down.png"
alt="Sort from low to high">
</a>
<a href="index.php?order=price_desc">
<img src="<?php print $path_to_root; ?>/library/arrow_up.png"
alt="Sort from high to low">
</a>
</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<?php
//Loop across records.
while( $row = $record_set->fetch_assoc() ) {
//Get fields for an product.
$product_id = $row['product_id'];
$name = $row['name'];
$description = $row['description'];
$image_file_name = $row['image_file_name'];
$price = $row['price'];
//Output
print "
<tr>
<td>
<img src='$path_to_root/product-images/$image_file_name'>
</td>
<td>$name</td>
<td>$description</td>
<td>$price</td>
<td>
<a href='edit-product.php?id=$product_id'>Edit</a><br>
<a href='confirm-delete-product.php?id=$product_id'>Delete</a>
</td>
</tr>";
} //End while.
?>
</tbody>
</table>
Figure 3. DogToys administration menu code
Line 3 makes a link to the add-product.php page.
Lines 9 and 10 connect to the database. Lines 11 to 15 check that the connection was successful.
Lines 17 to 32 create the SQL query. The statement has an ORDER BY clause attached.
Line 33 runs the query. Lines 34 to 41 check for error reports from the database.
Lines 66 to 87 output a table row for each product. Here are the lines that make the Edit and Delete links:
<a href='edit-product.php?id=$product_id'>Edit</a><br>
<a href='confirm-delete-product.php?id=$product_id'>Delete</a>
They use the “drill-down” method we used in the previous lesson. The product id is attached to the URL for the edit and delete pages.
We’ll look at how editing and deletion is done in future lessons. For now, just notice how the links are created to edit-product.php and confirm-delete-product.php.
The DogRock administration menu is much the same:

Figure 4. DogRock administration menu
Here are excerpts from the code that makes the menu:
<p>What do you want to do?</p>
<blockquote>
<p><a href="add-article.php">Add a new article</a></p>
</blockquote>
<h2>Current articles</h2>
<?php
//Connect to DB.
require $path_to_root . '/library/db-connect.php';
$db = new mysqli($host, $user_name, $password, $db);
...
//Fetch article data.
$query = "select article_id, title, author, when_published
from articles
order by ";
...
$record_set = $db->query($query);
...
//Loop across records.
while( $row = $record_set->fetch_assoc() ) {
//Get fields for an article.
$article_id = $row['article_id'];
$title = $row['title'];
$when_published = $row['when_published'];
$author = $row['author'];
...
//Output
print "
<tr>
<td><a href='../show-article.php?id=$article_id'>$title</a></td>
<td>$when_published</td>
<td>$author</td>
<td>
<a href='edit-article.php?id=$article_id'>Edit</a><br>
<a href='confirm-delete-article.php?id=$article_id'>Delete</a>
</td>
</tr>";
}
?>
Figure 5. DogRock administration menu code
It’s much the same as the other administration menu. Lines 33 and 34 create the Edit and Delete links.
Add an administration menu to your Jokes application. It should have a link to the add form, plus edit and delete links for each joke.
The links should refer to edit-joke.php and confirm-delete-joke.php. They don’t exist yet; you’ll add them later.
You can check my solution. But don’t look at it now! Do it yourself, first.
(Log in to enter your solution to this exercise.)
Let’s see how record deletion works.
You know how to create an admin menu, with a delete link for each record. But what happens when the user clicks on one? Let’s see.
In this lesson, learn:
DELETE statement.The SQL DELETE statement deletes records from a table. Here’s a typical example:
delete from dogs where weight < 25;
This will delete all records from the dogs table that have a weight less than 25.
Typically, you use the primary key value to delete a single record. Like this:
delete from articles where article_id = 17;
The administration menu looks like this:

Figure 1. DogToys administration menu
There’s a Delete link for each record. Here’s the PHP that makes it:
while( $row = $record_set->fetch_assoc() ) {
//Get fields for an product.
$product_id = $row['product_id'];
...
print "
...
<a href='confirm-delete-product.php?id=$product_id'>Delete</a>
?>
</tbody>
</table>
Figure 2. DogToys administration menu code
The code generates HTML like this:
<a href='confirm-delete-product.php?id=2'>Delete</a>
Figure 3. HTML generated by PHP
When the user clicks on a Delete link, we could delete the record immediately. But that’s not a good idea. It would be easy to accidentally click the Delete link, maybe when aiming for the Edit link.
So let’s ask the user to confirm the deletion. That’s why the link in Figure 3 calls the page confirm-delete-product.php. It renders like this:

Figure 4. Confirm deletion
The page shows the record that the user has chosen to delete. The user has to click the button before the record will be deleted.
Here’s code for this page.
<?php
//Connect to DB.
require $path_to_root . '/library/db-connect.php';
$db = new mysqli($host, $user_name, $password, $db);
//Fetch product data.
$product_id = $_GET['id'];
$query = "select name, image_file_name, price
from products
where product_id = " . $product_id;
$record_set = $db->query($query);
$row = $record_set->fetch_assoc();
//Get fields.
$name = $row['name'];
$image_file_name = $row['image_file_name'];
$price = $row['price'];
//Output confirmation.
print "
<p>You have chosen to delete this product:</p>
<blockquote>
<p><img src='$path_to_root/product-images/$image_file_name'> $name</p>
<p>$$price</p>
</blockquote>
";
?>
<form method="post" action="delete-product.php">
<p>Are you sure you want to do this?</p>
<p>The action cannot be undone.</p>
<p>
<input type="hidden" name="id" value="<?php print $product_id; ?>">
<button type="submit">Confirm</button>
</p>
</form>
<p><a href="index.php">< Back</a></p>
Figure 5. confirm-delete-product.php
Lines 3 and 4 connect to the database.
Line 6 gets the id of the record from the URL.
Lines 7 to 9 creates an SQL SELECT statement with the id. Line 10 runs the query.
Line 11 gets a row from the record set (there will be only one) and puts it into the variable $row. Lines 13 to 15 get the fields from $row, putting them into variables.
The next few lines show the field values:
print "
<p>You have chosen to delete this product:</p>
<blockquote>
<p><img src='$path_to_root/product-images/$image_file_name'> $name</p>
<p>$$price</p>
</blockquote>
";
Part of Figure 5. confirm-delete-product.php
Line 21 looks a little odd:
<p>$$price</p>
Why two dollar signs? The second dollar sign is part of the variable name. The first one is shown on the page. So if $price is 9.95, then the following shows:
<p>$9.95</p>
Let’s look at the screen shot again:

Figure 4 (again). Confirm deletion
We’ve done everything except for the button. Here’s the code:
<form method="post" action="delete-product.php">
<p>Are you sure you want to do this?</p>
<p>The action cannot be undone.</p>
<p>
<input type="hidden" name="id" value="<?php print $product_id; ?>">
<button type="submit">Confirm</button>
</p>
</form>
Another part of Figure 5. confirm-delete-product.php
The program that will delete the product is delete-product.php. It’s the action property of the form:
<form method="post" action="delete-product.php">
We only need to send one piece of data to the page: the id of the product to delete. How to add it to the form? With a hidden field. It’s in line 29:
<input type="hidden" name="id" value="<?php print $product_id; ?>">
If the product id is, say, 17, the HTML will be:
<input type="hidden" name="id" value="17">
A hidden field is like a regular <input> field, but it isn’t shown on the page. Its value, however, is passed to the destination page, like any other field on a form.
What happens when the user clicks the Confirm button? The id is sent to delete-product.php, which actually does the deletion.
Here’s the code for delete-product.php. I left out the error checking code.
<?php
//Delete a product.
//Input:
// id: id number of the product. POST.
$path_to_root = '..';
$product_id = $_POST['id'];
//Connect to DB.
require $path_to_root . '/library/db-connect.php';
$db = new mysqli($host, $user_name, $password, $db);
//Delete the product.
$query = "delete from products where product_id = $product_id";
$db->query($query);
//Back to admin menu.
header('location:index.php');
exit();
?>
Figure 6. delete-product.php
Line 6 gets the id passed to the page.
Lines 8 and 9 connect to the database.
Line 11 creates the SQL statement that will delete the product:
$query = "delete from products where product_id = $product_id";
Line 12 runs the query.
Finally…
header('location:index.php');
...back to the administration menu.
The administration menu for DogRock looks like this:

Figure 7. DogRock administration menu
There’s a Delete link for each record. Here’s the PHP that makes it:
while( $row = $record_set->fetch_assoc() ) {
//Get fields for an article.
$article_id = $row['article_id'];
...
print "
...
<a href='confirm-delete-article.php?id=$article_id'>Delete</a>
?>
</tbody>
</table>
Figure 8. DogRock administration menu code
Clicking on the Delete link loads a confirmation page:

Figure 9. Confirm deletion
Here’s the code for this page.
<?php
//Connect to DB.
require $path_to_root . '/library/db-connect.php';
$db = new mysqli($host, $user_name, $password, $db);
//Fetch product data.
$product_id = $_GET['id'];
$query = "select name, image_file_name, price
from products
where product_id = " . $product_id;
$record_set = $db->query($query);
$row = $record_set->fetch_assoc();
//Get fields.
$name = $row['name'];
$image_file_name = $row['image_file_name'];
$price = $row['price'];
//Output confirmation.
print "
<p>You have chosen to delete this product:</p>
<blockquote>
<p><img src='$path_to_root/product-images/$image_file_name'> $name</p>
<p>$$price</p>
</blockquote>
";
?>
<form method="post" action="delete-product.php">
<p>Are you sure you want to do this?</p>
<p>The action cannot be undone.</p>
<p>
<input type="hidden" name="id" value="<?php print $product_id; ?>">
<button type="submit">Confirm</button>
</p>
</form>
<p><a href="index.php">< Back</a></p>
Figure 10. confirm-delete-article.php
As before, it shows the record to be deleted, then a <form> with:
hidden field with the id of the record.Here’s the code for delete-article.php.
<?php
//Delete an article.
//Input:
// id: id number of the article. POST.
$path_to_root = '..';
$article_id = $_POST['id'];
//Connect to DB.
require $path_to_root . '/library/db-connect.php';
$db = new mysqli($host, $user_name, $password, $db);
//Delete the article.
$query = "delete from articles where article_id = $article_id";
$db->query($query);
//Back to admin menu.
header('location:index.php');
exit();
?>
Figure 6. delete-article.php
This is almost identical to delete-product.php.
Add confirm-delete-joke.php and delete-joke.php to your Jokes application. They should act like their DogToys and DogRock counterparts.
You can see my solutions for the confirmation and deletion pages. But write them yourself first!
(Log in to enter your solution to this exercise.)
DELETE statement.We’re almost done. Time to let users edit existing records.
You’ve created an administration menu, that lets users delete and edit records. You know how to program the delete part. Let’s talk about the edit part.
In this lesson, learn:
UPDATE statement changes data in an existing record. Usually, it has a single primary key value in its WHERE clause.Here’s a screenshot of someone editing a product record:

Figure 1. Editing a product record
The user changes the values and clicks the button.
Here’s the form for adding a product:

Figure 2. Adding a product
They’re almost the same. They have the same form fields, and the same client-side validation.
The main differences between add and edit are:
product_id. New records don’t have a product id. MySQL will choose an id value when it adds a record to the database. (Recall that we made product_id an auto_increment field.)UPDATE. To add a new record, we use the INSERT statement.UPDATE statementLet’s look at the dogs table again. Each row has data about one dog. The table has the fields:
dog_id: the dog’s id number (integer, primary key).name: name of the dog (character).breed: breed of the dog (character).weight: weight of the dog in pounds (integer).Here’s some sample data:

Figure 3. Dog data
Suppose Brian puts on two pounds. Here’s an SQL statement to show the change:
update dogs
set weight = 53
where dog_id = 5;
The statement tells MySQL three things:
dogs. dog_id of 5.weight to 53.Most update statements just change one record, using a primary key value.
You can change text values, like this:
update dogs
set name = 'Fido'
where dog_id = 5;
Don’t forget the quotes around the text.
You can change more than one field at a time. For example:
update dogs
set name = 'Fido',
weight = 56
where dog_id = 5;
When we changed the weight to 53. Could we do this?
update dogs
set weight = 53
where name = 'Brian';
Yes, we could. You can use text fields in the where clause. It would work in this case, but…
Could there be more than one dog named Brian?
Hmm, I suppose there could be.
Right! And your SQL statement would change the weight of all those dogs to 53.
So, usually, when you use the update statement, you use the primary key. This identifies a single record. Here it is again:
update dogs
set weight = 53
where dog_id = 5;
There is only one dog with an id of 5. Even if we had eight dogs called Brian, only one of them would have an id of 5.
Let’s see how we can use the update statement.
Suppose a user wants to edit product data on the DogToys site. Let’s look at the workflow.
The user starts at the admin menu, and selects a product to edit:

Figure 4. Admin menu
Here’s a sample Edit link:
<a href='edit-product.php?id=2'>Edit</a>
Clicking the link jumps to the page edit-product.php, passing an id of 2. This is the product_id of the product the user wants to edit.
edit-product.php shows a form like this:

Figure 1 (again). Editing a product record
The user changes the data and clicks the Save button. If all the validation checks are passed, the new data is saved into the database, and the user is taken back to the main menu.
Here’s a picture of the workflow:

Figure 5. Workflow for editing a product record
Let’s look at the code for edit-product.php, the edit form. Here’s what the code has to produce.

Figure 1 (again). Editing a product record
Each of the form fields shows the current data.
Here’s how edit-product.php works:
Here’s the page. Some of the code has been removed for simplicity.
<?php
...
//Connect to the database.
require $path_to_root . '/library/db-connect.php';
$db = new mysqli($host, $user_name, $password, $db);
//Fetch product data.
$product_id = $_GET['id'];
$query = "select name, description,
image_file_name, price
from products
where product_id = " . $product_id;
$record_set = $db->query($query);
$row = $record_set->fetch_assoc();
//Extract fields.
$name = $row['name'];
$description = $row['description'];
$image_file_name = $row['image_file_name'];
$price = $row['price'];
?>
...
<form id="edit_product_form" method="post" action="save-edited-product.php">
<p>
Name<br>
<input type="text" name="name" id="name" size="30"
value="<?php print $name; ?>">
</p>
<p>
Description<br>
<textarea name="description" id="description" rows="5" cols="30"><?php print $description; ?></textarea>
</p>
<p>
Image file name<br>
<input type="text" name="image_file_name" id="image_file_name" size="30"
value="<?php print $image_file_name; ?>">
</p>
<p>
Price<br>
<input type="text" name="price" id="price" size="10"
value="<?php print $price; ?>">
</p>
<p>
<input type="hidden" name="product_id" value="<?php print $product_id; ?>">
<button type="submit">Save</button>
</p>
</form>
Figure 6. edit-product.php
Line 4 to 5 connect to the database.
Line 7 gets the product id from the URL. Recall that the URLs are like this:
edit-product.php?id=2
Lines 8 to 11 create an SQL statement that looks up the data for the product. For example, if id was 2, the query would be:
select name, description, image_file_name, price
from products
where product_id = 2
Line 12 runs the query. The query only returns one row, because the where clause tests the primary key.
Line 13 fetches the row. Lines 15 to 18 get the individual fields, and put them into variables. For example:
$name = $row['name'];
Here’s how that data is used:
<input type="text" name="name" id="name" size="30"
value="<?php print $name; ?>">
If $name contained Frisbee, this line would become:
<input type="text" name="name" id="name" size="30"
value="Frisbee">
When the browser renders the field, it will put the value Frisbee into it:

Figure 7. Name field rendered
This page has to send the new data to save-product.php. It needs to include the product_id. But the id is not actually shown in the form. The user can’t change it, so there’s no point in showing it.
So how do you put some data into a form so that it can be sent, but not have it visible to the user?
The solution: use a hidden field. Like this:
<input type="hidden" name="product_id" value="<?php print $product_id; ?>">
The product_id will travel along with the rest of the form fields. But the user won’t see it.
W00f!
Here’s the workflow again:

Figure 5 (again). Workflow for editing a product record
When the user clicks the Save button, the browser sends the data to save-edited-product.php. This is the action property of the form in line 21 in Figure 6:
<form id="edit_product_form" method="post" action="save-edited-product.php">
Here’s what save-edited-product.php has to do:
update statement.Here’s the code. Error checking and some other code has been omitted.
<?php
$path_to_root = '..';
//Connect to DB.
require $path_to_root . '/library/db-connect.php';
$db = new mysqli($host, $user_name, $password, $db);
//Get the form fields.
$product_id = stripslashes($_POST['product_id']);
$name = stripslashes($_POST['name']);
$description = stripslashes($_POST['description']);
$image_file_name = stripslashes($_POST['image_file_name']);
$price = stripslashes($_POST['price']);
//Make the fields safe.
$product_id = $db->escape_string($product_id);
$name = $db->escape_string($name);
$description = $db->escape_string($description);
$image_file_name = $db->escape_string($image_file_name);
$price = $db->escape_string($price);
//Create and run the SQL.
$query = "update products
set name = '$name',
description = '$description',
image_file_name = '$image_file_name',
price = $price
where product_id = $product_id";
$db->query($query);
//Back to admin menu.
header('location:index.php');
exit();
?>
Figure 8. save-edited-product.php
Lines 4 and 5 connect to the database.
Lines 7 to 11 get the form data, including the product_id passed as a hidden field. Note that stripslashes() was used to undo PHP’s “helpful” insertion of backslashes.
Lines 13 to 17 make the data safe from Evil Doers. It defuses SQL injection attacks.
Lines 19 to 24 creates the SQL query. It includes quotes (’) for text fields, and a where clause to select the right product.
Line 25 runs the query.
Line 27 jumps back to the admin menu.
That’s it for DogToys. Let’s move on.
The workflow for editing an article is the same as the workflow for editing a product:
Here’s what the form looks like:

Figure 9. Editing an article
Here’s the code, with some stuff omitted:
<?php
//Connect to the database.
require $path_to_root . '/library/db-connect.php';
$db = new mysqli($host, $user_name, $password, $db);
//Fetch article data.
$article_id = $_GET['id'];
$query = "select title, body, when_published, author
from articles
where article_id = " . $article_id;
$record_set = $db->query($query);
$row = $record_set->fetch_assoc();
//Extract fields.
$title = $row['title'];
$body = $row['body'];
$when_published = $row['when_published'];
$author = $row['author'];
//Format the date.
$when_published = date('F j, Y', strtotime($when_published));
?>
<form id="new_article_form" method="post" action="save-edited-article.php">
<p>
Title<br>
<input type="text" name="title" id="title" size="40"
value="<?php print $title; ?>">
</p>
<p>
Author<br>
<input type="text" name="author" id="author" size="40"
value="<?php print $author; ?>">
</p>
<p>
When published<br>
<input type="text" name="when_published" id="when_published" size="40"
value="<?php print $when_published; ?>">
</p>
<p>
Body<br>
<textarea name="body" id="body" rows="8" cols="40"><?php print $body; ?></textarea>
</p>
<p>
<input type="hidden" name="article_id" value="<?php print $article_id; ?>">
<button type="submit">Save</button>
</p>
</form>
Figure 10. edit-article.php
Lines 3 and 4 connect to the database.
Lines 6 gets the id of the article wants to edit. Lines 7 to 9 create the SQL query, which includes the article’s primary key. Line 10 runs the query, and line 11 gets the row fetched by MySQL.
Lines 13 to 16 extract the individual fields from the row. Line 18 formats the date to a familiar format.
The form is then shown. Each field’s value property puts the current value into the field. Line 41 adds the article-id as a hidden field, so the id will get passed to the page that saves the data.
Here’s save-edited-article.php, the program that saves the new article data.
<?php
$path_to_root = '..';
//Connect to DB.
require $path_to_root . '/library/db-connect.php';
$db = new mysqli($host, $user_name, $password, $db);
//Get the form fields.
$article_id = stripslashes($_POST['article_id']);
$title = stripslashes($_POST['title']);
$body = stripslashes($_POST['body']);
$author = stripslashes($_POST['author']);
$when_published = stripslashes($_POST['when_published']);
//Make the fields safe.
$article_id = $db->escape_string($article_id);
$title = $db->escape_string($title);
$body = $db->escape_string($body);
$author = $db->escape_string($author);
$when_published = $db->escape_string($when_published);
//Format the date.
$when_published = date('Y-m-d', strtotime($when_published));
//Create and run the SQL.
$query = "update articles
set title = '$title',
author = '$author',
body = '$body',
when_published = '$when_published'
where article_id = $article_id";
$db->query($query);
//Back to admin menu.
header('location:index.php');
exit();
?>
Figure 11. save-edited-article.php
Lines 4 and 5 connect to the database. Lines 7 to 11 get the data passed into the form, and strips the excess backslashes with stripslashes().
Lines 13 to 17 sanitize the data. Line 19 converts the publication date into the format MySQL prefers.
Lines 21 to 26 create the SQL update statement. Line 27 runs the query.
Line 29 jumps back to the admin menu.
W00f!
Here’s the pattern for the edit page itself.
[node:pattern/updating-database-record noterms]
Give users the ability to edit existing jokes. Model your code on DogToys and DogRock.
You can see my code for edit-joke.php and save-edited-joke.php. But do it yourself first!
(Log in to enter your solution to this exercise.)
UPDATE statement changes data in an existing record. Usually, it has a single primary key value in its WHERE clause.W00f!
Time for some more exercises.
On your local machine, create a database called dogmovies. It will have data about movies that have dogs in them.
Make a user who can access the database.
Add a table to the database. Call it movies. Add fields for:
Add some movie data from this page.
Write a PHP test page to connect to the database.
Now duplicate the database and the test page on your hosting account.
Put the URL of the test page below.
(Log in to enter your solution to this exercise.)
On your local machine, create a database called dogbooks. It will have data about books about dogs.
Make a user who can access the database.
Add a table to the database. Call it books. Add fields for:
The last field is a paragraph or two about the book.
Add some book data from Dogwise, or another site. There’s lots of good stuff there.
Write a PHP test page to connect to the database.
Now duplicate the database and the test page on your hosting account.
Put the URL of the test page below.
(Log in to enter your solution to this exercise.)
On your local machine, create a database called dogsites. It will have data on Web sites about dogs.
Make a user who can access the database.
Add a table to the database. Call it sites. Add fields for:
Add some data about dog sites.
Write a PHP test page to connect to the database.
Now duplicate the database and the test page on your hosting account.
Put the URL of the test page below.
(Log in to enter your solution to this exercise.)
Create a version of the DogMovies site with a home page and a movies list. The home page should look something like this, but with your own graphics and colors:

Figure 1. Home page
Here’s the movie list page.

Figure 2. Movies
Clicking on the column headings sorts the data.
Add at least five movies to your database.
You can try my solution.
Upload your application to your hosting account. Put the URL below.
(Log in to enter your solution to this exercise.)
Create a version of the DogBooks site with a home page, a book list, and a page describing each book. The home page should look something like this, but with your own graphics and colors:

Figure 1. Home page
Here’s the book list page:

Figure 2. Book list
Clicking on a column header sorts the pages.
Here’s part of a book description page:

Figure 3. Book description
You can try my solution.
Upload your application to your hosting account. Put the URL below.
(Log in to enter your solution to this exercise.)
Create a version of the DogSites site with a home page, and a site list. The home page should look something like this, but with your own graphics and colors:

Figure 1. Home page
Here’s the site list page:

Figure 2. Site list
You can try my solution.
Upload your application to your hosting account. Put the URL below.
(Log in to enter your solution to this exercise.)
Add an administration section to the DogMovies Web site.
You can see my solution.
Enter the URL of your solution below.
(Log in to enter your solution to this exercise.)
Add an administration section to the DogBooks Web site.
You can see my solution.
Enter the URL of your solution below.
(Log in to enter your solution to this exercise.)
Add an administration section to the DogSites Web site.
You can see my solution.
Enter the URL of your solution below.
(Log in to enter your solution to this exercise.)