April 18th, 2010 §
Working off of the example code from the Basic SQL example files, here is a slightly more sophisticated set of pages that when used together, create a sort of Content Management System for our animals application.
How it works
Here is a user flow diagram outlining how this application fits together. Below the diagram is an overview of each step.

User flow diagram for Basic SQL CMS
read.php
In this version of the files, the pages that handle each of the CRUD commands are all linked together. A user can view the animals stored in the database on the main page, read.php. Read.php simply queries the database for a list of all the rows stored in the animals table, and outputs each of those rows into the XHTML for the page.
The user can click links to “update” or “delete” any individual animal. When they click either of those links, the id of the selected animal is passed in the query string of the link to the corresponding page, either update.php or delete.php.
update.php
Update.php displays a form that the user can use to change the name of the animal that was clicked. The first time this page is loaded, this form is prepopulated with the existing name of the animal that was clicked, which has been retrieved from the database.
The id of the animal and its new name are passed along with another request for update.php once the user clicks the submit button. Update.php this time updates the selected row in the animals table and redirects the user back to read.php, where the user can see the updated list of names.
delete.php
Delete.php simply runs a query that deletes the selected row from the database, and then redirects the user to read.php, where they can see the updated list of animals.
create.php
A user can click a link to “add a new animal” on read.php. This takes them to create.php, where they can enter a new animal name into a form. When they submit the form, the animal name is passed along with another request for create.php.
Create.php this time creates a new row in the database table for the new animal, and then redirects the user to read.php, where they can see the updated list of animals.
December 5th, 2009 §
Let’s say that we are building a social network. What we call a social network is a site that has a bunch of users, and those users can decide to be “friends” with any other user.
You can view this example live here.
The Views
There will be four pages that the user sees:
- Register – where new users go to register to become users
- Login – where registered users go to login
- Home – a page that shows a list of the logged-in user’s friends, and a list of people who are not his/her friends.
- Friends’s Profile – a page that shows details about another user
Each “page” requires a View in order to be displayed to the user in the browser. Anytime there is something displayed to the user, we should know that there is at least one View used to create that interface. So we can say that there are four Views in this application. In our example application, the files that contain the templates for these Views are:
- views/register_view.php – the template for the Register page
- views/login_view.php – the template for the Login page
- views/index_view.php – the template for the Home page
- views/profile_view.php – the template for the Friend’s Profile page
The Controllers
- We only want to let a user go to the Login page if they are not already logged in. If they are already logged in, we need to redirect them to the Home page. Anytime we have a script that performs some logic like this, we should consider it a Controller.
- Likewise, a user should only see the Register page if they are not already logged in. Again, this logic is handled by a Controller.
- Assuming a user is not already logged in, when they enter their username/password in the form on the Login page and click the submit button, there has to be some script that performs the logic to compare the username/password data the user entered in the Login page to the user data stored in the database. If the username/password matches what is found in the database, this script has to let the user in to the site. This logic is the job of a Controller
- Likewise, when a user fills out the Register form and clicks submit, a script has to check to make sure they entered a valid username/password, and then if everything is ok, the script has to somehow create a new row in the database that stores that username/password. Then the user should be redirected to the Home page. This business logic is the job of a Controller.
- The Home page needs to check to make sure the user is logged in. It then needs to retrieve the list of friends of the logged-in user, the list of people who are not friends of the logged-in user, and then display that data to the user. The decision of what data to retrieve from the Model, and the job of then forwarding that data to the View which displays the interface, is the job of a Controller.
- The Friend’s Profile Page has the same type of Controller as the Home page. Data must be retrieved from the Model, and then that data must be properly inserted into the View for this page. So a Controller must be present to take care of this.
The Controller scripts that I have created for this application to handle these tasks are:
- authenticate.php – handles all tasks related to Login and Register functionality
- index.php – handles all tasks related to viewing the Home page
- profile.php – handles all tasks related to viewing a Friend’s Profile
- friendship.php – handles all tasks related to adding/removing friends
The Models
As should be clear by now, Models are necessary to handle the parts of this site where direct access to the database is needed:
- compare any username/password combo to those already stored in the database (necessary for the Login and Register pages)
- create a new user in the database (necessary for the Register page)
- create/delete friend associations in the database (necessary for the Home and Friend’s Profile pages)
- get a list of a user’s friends (necessary for the Home and Friend’s Profile pages)
- get a list of people who are not a user’s friends (necessary for the Home and Friend’s Profile pages)
The Model scripts that do these tasks are:
- models/User.class.php – handles any tasks related to creating, reading, updating, or deleting users
- models/Authentication.class.php – handles any tasks related to logging in or registering a user
- models/Friendship.class.php – handles any tasks related to friendships between two users
- models/Santize.class.php – handles any tasks related to data sanitization
To be consistent and complete, I have added the standard CRUD functions to each Model script, as well as the functions which handle each of the database-related tasks listed above.
It’s object-oriented
You can see that I have created seperate class files for each Model. I am combining MVC architecture concepts with object-oriented programming techniques. I have created classes for each type of “object” or “entity” that I think may conceptually need specific actions taken on it.
Object oriented programming is a seperate concept from MVC architecture. But I have used this example to exhibit both.
It uses a home-brewed framework
You can see that I have organized my code in a specific way. All Model files are contained in the models/ folder. All View files are contained in the views/ folder, and all Controller files are contained in the root folder. Javascript files would go in the scripts/ folder. Style sheets are in the styles/ folder, and database connection info is in the dbinfo/ folder.
One of the core features of a so-called “framework” is a clear organization of the files involved in a project. So you could call this organization that I have come up with a sort-of home-brewed framework. It is very simple, and crude, but it is effective at helping organize our MVC object-oriented application.
The popular frameworks that PHP developers use, such as Zend, CakePHP, Symfony, and CodeIgnigter, do much more sophisitcated things than just seperate your code into folders. So I doubt my framework will become the next big thing. But it is useful for our purposes nonetheless.
What the user sees
It’s important to note that the users will be completely oblivious to our use of an object-oriented MVC architecture. This is a good thing: you don’t want users to have to worry about how a site was developed.
When a user goes to the Login or Register pages, they will see the address authenticate.php in the browser address bar. This is the Controller script that we know handles all tasks related to logging in and registering. This controller figures out whether the user wants to see the Login Page or the Register Page, and loads up the appropriate View for either page.
When a user goes to the Home Page, they see the address, index.php. This, as we know, is the Controller file for the tasks related to the Home page. This Controller calls functions in the Model that get the data related to the Home page, and then this Controller loads up the View file for the Home page, which displays this data nicely.
Similarly, when a user goes to a Friend’s Profile page, they see the address, profile.php in the browser’s address bar. This is the Controller file for all tasks related to viewing the Friend’s Profile page. This script gets all the data by calling functions in the Model, and then includes the appropriate View file to display that data.
November 23rd, 2009 §
As a general rule, any data that comes from a user is not to be trusted. So anytime you are dealing with data that may (or may not) have originated from a user, you need to sanitize that data before doing anything else with it. Think of it as basic web hygiene, akin to washing your hands in the restroom. Quoting Google’s CEO, Eric Schmidt, the intenet is a “cesspool”. None of us needed him to tell us that – it’s obvious.
Anytime your site deals with data that does not originate from your own code, you need to sanitize it before letting it touch the internal organs of your website. When we talk about sanitizing, we’re not talking about removing bad words from the code, we’re generally talking about preventing malicious hackers from trying to break into our website by sending data to the server that may allow them to exploit faults in our code or weaknesses on the server.
User-generated content may often come from any of the following sources:
Practical sanitization
No need to get paranoid yet. For our practical purposes, any data that you get from the $_REQUEST, $_GET, $_POST, or $_COOKIE arrays should be sanitized.
Let’s say you have code like this:
$dummyData = $_REQUEST['dummy_data'];
This is getting data from the $_REQUEST variable, which as we know is automatically populated with data from the query string in links, from form fields, or from cookies. In other words, it’s potentially tainted. And let’s say you are planning to store that $dummyData in a database table like so:
$myQuery = "INSERT INTO abloomberg_dummy (data) VALUES ('{$dummyData}')";$result = mysql_query($myQuery);
You absolutely must sanitize it to prevent malicious things like SQL injection attacks before you run that query.
An example
This example uses PHP code to do just that. It uses an object-oriented Sanitize class (as in classes and objects in object-oriented programming) that I based off of another well known (but not object-oriented) script.
To use this Sanitize class in your own PHP scripts, before you do anything else:
- download a copy of the zip archive, unzip it, and put the file Sanitize.class.php in the folder for your project.
- make sure your script includes this file by using require_once(“Sanitize.class.php”);
Once you have that set up, you’re ready to use this class. Here is an example usage:
<?php //file: index.php //an example of using the Sanitize class
//include the Sanitize class into this script require_once("Sanitize.class.php");
//on a live site, you'd want to sanitize all data that you got from the user //in otherwords, any time you use data you got frm the $_REQUEST, $_GET, $_POST, or $_COOKIE variables //For example, if the data was coming from a form or query string in a link: //$dirtyData = $_REQUEST['something'];
//in this example, for simplicity, i'm just sanitizing the contents of a variable that's hardcoded $dirtyData = "this is a test with an HTML tag <a href='#'>click me</a>";
/* First choose how you want to santize the data. The choices are: (PS: notice that these are static properties of the Sanitize class - hence the :: symbol)
Sanitize::HTML //replaces any HTML tags with "HTML entities" Sanitize::SQL //prevents against SQL injection attacks Sanitize::UTF8 //makes sure data is in UTF8 format Sanitize::INT //makes sure the data is an integer Sanitize::FLOAT //makes sure the data is a float (decimal) Sanitize::LDAP //prevets against any LDAP code Sanitize::SYSTEM //prevents any system commands from being run Sanitize::PARANOID //all of the above */
//set the $flags variable to be the sum of all the flags you want to use from the list above $flags = Sanitize::HTML + Sanitize::SQL; //this example removes any HTML or SQL commands from the string
//now pass the data and the $flags variable to the sanitize function to sanitize it $cleanData = Sanitize::sanitize($dirtyData, $flags); //call the static method "sanitize of the Sanitize class
//now your data is clean echo $cleanData; //the text stored in this variable has been "sanitized"
//you may want to "view source" in the browser to see what happened to the text?>
Understanding the Sanitize::sanitize() method
The most important part to understand is the command that actually does the sanitizing:
$cleanData = Sanitize::sanitize($dirtyData, $flags); //call the static method "sanitize of the Sanitize class
This line calls the Sanitize::sanitize() function and passes it two arguments: the data to be sanitized, and the flags that indicate what type of sanitization you want to do. The result of this sanitize() function is then put into the variable $cleanData, which now has the sanitized version of the data.
In this example, we have set the $flags variable to indicate that we want to remove any HTML or SQL code from the data:
$flags = Sanitize::HTML + Sanitize::SQL; //this example removes any HTML or SQL commands from the string
We can use any combination of the available flags by adding them together.
Now that the data has been sanitized, you can safely store that data in a database without worrying about SQL injection attacks:
$myQuery = "INSERT INTO abloomberg_dummy (data) VALUES ('{$cleanData}')";$result = mysql_query($myQuery);
Or do whatever else you want with it. But rest assured it does not have any malicious HTML or SQL code in it.
Note that since this example is object oriented, we never have to look at the source code of Sanitize.class.php. This is abstraction at work.
November 7th, 2009 §
If you have finished the in-class assignment of creating a message board, you are ready for the advanced assignment.
The advanced assignment is to add the ability for users to upload images along with their message posts.
add_post.php
So when users add new posts, they enter in their name, the title of the post, the body of the post, as well as the image that goes along with the post.
Here’s the updated wireframe for the add_post.php page:

updated add_post.php wireframe
You will want to check out the upload file example on the server here.
You use an <input type=”file” …> tag to allow users to select a file to upload.
Make sure your form has the “enctype” attribute set to “multipart/form-data”. This indicates to the server that it should expect to be receiving binary data for the file.
process_post.php
When the server receives the data that the user submits along with the HTTP POST request for process_post.php, it has to store the data in the database, as you have already done in the first part of this assignment.
You will want to create a new “image_path” field in your messages table in the database where you will store the path where you uploaded the image. You can get the path where the image is uploaded by going through the file upload example code linked above.
So when you create the new row in the database table, you will be storing the author, post title, image path, and post body in the database table.
index.php
And when a user views the list of all the messages, if there is an image that has been uploaded along with a particular message, that image shows up next to the message. If there is no image associated with a post, the layout should adjust accordingly.
Here is the updated index.php wireframe:

updated index.php wireframe
Very advanced assignment
If this was too easy for you, here is a very advanced assignment. Require the users to register with your site before they can post a message.
Users should be able to view the home page whether they are registered or not.
But only registered users should be able to post a new message. If a user has not registered, they should be redirected to the index.php page if they try to view the add_post.php or process_post.php pages.
November 7th, 2009 §
Your assignment this class is to create a message board. The message board allows users to view all of the messages that have been posted to the board so far. It also allows users to post new messages.
Here is the user flow of the site:

User flow of message board site
index.php – the message list page
When the user first comes to the site, they see the main page, index.php. This page shows them a list of all of the messages on the board in reverse chronological order. The page reads all of the rows of data from the database table and displays them.
This page also has a link to “add a new post”. When the user clicks that link, she is brought to add_post.php.

index.php wireframe
You will want to use code similar to what is available in the read.php example available on the server here.
Also, you will eventually want to format the dates that you retrieve from the created field of the database table, you will want to read this post about beautifying MySQL timestamps.
add_post.php – the post page
add_post.php consists of a form the user can fill out in order to post a new message. This form has three fields: the user’s name, the post title, and the post body. When the user clicks submit, this page makes an HTTP GET request for process_post.php, and passes along the data that the user entered into the form as part of the query string of the URL of the request.

add_post.php wireframe
This page can be a simple XHTML page. It’s ok to name it add_post.php even if it just has XHTML code inside of it.
process_post.php – the process post page
The process_post.php script receives the data that was passed in the GET request to the server by using the built-in PHP $_GET or $_REQUEST variables, and enters that information as a new row in the database table.
You will want to use code that is similar to the PHP used in the create.php example available on the server here.
This script will then redirect the user back to the main page, index.php. Check out this example of how to redirect a user from one page to another. You will be using the built-in header() function in PHP in order to pass a special “Location” HTTP header from the server to the client that instructs it to go make a request for a different page.
For example, this code redirects a user to the nytimes.com website:
header("Location: nytimes.com"); //redirect to another page
November 7th, 2009 §
Create
INSERT INTO abloomberg_animals (name, color) VALUE (‘zubr’, ‘brown’)
Read
SELECT * FROM abloomberg_animals WHERE 1
SELECT name, color FROM abloomberg_animals WHERE id=15
Update
UPDATE abloomberg_animals SET name=’zubr’ WHERE id=15
UPDATE abloomberg_animals SET name=’zubr’, color=’brown’ WHERE id=15
Delete
DELETE from abloomberg_animals WHERE id=15
November 6th, 2009 §
Let’s cover the details of the SELECT statement in SQL. The SELECT statement is the command we use to do the Read part of database CRUD. CRUD is an acronym that stands for Create, Read, Update, Delete – the four operations you are most likely to do with a database. At any given time, you are either creating data, reading data, updating data, or deleting data.
We’ll start with the simplest possible SELECT statement in action, and then move to more nuanced implementations.
Let’s say we run a business managing art collections for wealthy individuals. We might want to build an inventory management application to keep track of which paintings our clients own.
The first step towards that would be to have a table that holds a list of our clients. Let’s call this table “collectors”. And let’s say we put the following information in it:

collectors table
An easy read
To do a query on the database to get all the information on all the rows in our table, we might do a simple SELECT statement as follows:
SELECT * FROM collectors WHERE 1
And reacall that in PHP, we execute queries on the database using the mysql_query() built-in function:
$myQuery = "SELECT * FROM collectors WHERE 1";
$result = mysql_query($myQuery);
This would return the full data set internally represented in basically the same way as what we’ve already seen. The order that the data is stored inside of a database is not necessarily chronological order, so the data may show up in a different order if you don’t specify in what order you’d like to receive it.

result set of full select query
Selecting specific fields
Let’s hypothetically say we only wanted to get the id and last name for each collector in our table. To do this, we would specify which fields we were interested in as follows:
SELECT id, last_name FROM collectors WHERE 1
Notice that we use a comma delimited list of field names after the SELECT keyword. This query would return a data set that looked like this:

A result set with only two fields
Ordering results
And now let’s say we wanted to order the results alphabetically by last name starting with A and ending in Z:
SELECT id, last_name FROM collectors WHERE 1 ORDER BY last_name ASC
The “ORDER BY” clause takes two parameters: the field we want to order the results by, and the order we want them returned in: either ASC or DESC for ascending and desc ending respectively.
Our example query would return a result set that looked like this:

result set ordered by the last_name field
Modifying the WHERE clause
To do a query that only returned the results for the collector with id #3, we would do a query like this:
SELECT * FROM collectors WHERE id=3
This would return a data set that looked like this:

result set matching only a single row
We can specify any criteria we want in the WHERE clause. If we only wanted to get the data for users who were NOT id #3, we could do this:
SELECT * FROM collectors WHERE id<>3
The <> symbol is the SQL way of saying NOT EQUALS.
Paginating results
Lastly, but not leastly in this set of examples, let’s say we had 400 collectors in our table. And we want to make a web-based interface that only shows 10 at a time, ordered alphabetically by last name. Will will have page number links so users can click on which “page” of results they want to view: 1,2,3,4, etc.
To paginate results, we would require a LIMIT clause on the SELECT query. For example, this query would return only the first 10 rows:
SELECT * FROM collectors WHERE 1 ORDER BY last_name ASC LIMIT 0,10
The LIMIT clause in this case takes two arguments, separated by a comma: the result number to start from, and the number of results to return. In this case, we are telling SQL to start from the first result in the entire data set of results, and return only 10 results total.
To get the next “page” of results, we might make a query to start from the 11th result, and return the next 10:
SELECT * FROM collectors WHERE 1 ORDER BY last_name ASC LIMIT 11,10
And so on…
Joining multiple tables of data
Now that we’ve covered all the basic elements of the SELECT statement, let’s use them to select data from multiple tables.
Let’s say we had a second table in our example. This table was a list of all the paintings that each of our collectors owns. Let’s call this table “paintings”, and it might look like this:

paintings table
Notice that this table has a foreign key field, “collector_id” that holds the id of the collector who owns each particular piece.
So let’s now say we wanted to combine the two sets of data. We want to get a list of all the paintings in our database, as well as the last names of collectors who own them. We might do a query like this:
SELECT paintings.*, collectors.last_name FROM paintings, collectors WHERE paintings.collector_id=collectors.id
This would return a set of data that looked like this:

esult set with data from two tables
Notice that we have merged data from both tables. The result set now has all of the data from the “paintings table”, as well as the last_name field from the “collectors” table.
There are two new things going on in this example. Firstly, when we are dealing with multiple tables, we have to be very clear about which fields we want to get data from, and which table those fields are in.
In this case, we asked for all fields from the “paintings” table by specifying “paintings.*”, and we also asked for only the “last_name” field of the “collectors” table by specifying “collectors.last_name” in the query.
Secondly, the WHERE clause tells SQL how to merge the tables. By specyfing “WHERE paintings.collector_id=collectors.id”, we are telling SQL to match up the two sets of data by the fields “paintings.collector_id” and “collectors.id”. In other words, we are telling SQL that the “collector_id” field in the “paintings” table is a foreign key that points to the “id” field of the “collectors” table.
So for every row in the “paintings” table, SQL looks at the “collector_id” field, and finds the “last_name” field from the corresponding row in the “collectors” table.
This type of merging of data from two tables by matching up a field in one table with another field from another table is known as an inner join in SQL terminology. This is, by far, the most common type of join, or merge, that developers do.
November 6th, 2009 §
Tables of data
Relational databases treat data in the same grid-like way as an Excel spreadsheet. There are rows and columns of data. But databases have extra features that make them easier and more intuitive to work with.
Databases, tables, rows, and columns
Each relational database can hold multiple tables. Tables are the spreadsheet-like grids of rows and columns of data. So if you are familiar with Microsoft Excel documents. you can think of the database as the Excel document, and the tables as the individual sheets within that document.
Databases are faster
With plain text files, As the amount of data stored grows, so too does the amount of time it takes for the server to open the text file and go line-by-line through it. After only several hundred lines of data, there will be a noticeable lag when reading data from a text file. The same problems exist for writing, updating, and deleting data – the more data you have, the more time-consuming those operations get, and other complications crop up.
Databases are optimized to quickly do the most common tasks with data: Creating new data in storage, Reading data from storage, Updating data, and Deleting data. These tasks, called CRUD for short, are exactly what databases like MySQL are designed to do quickly and efficiently. A database server can loop through thousands of rows of data in a tiny fraction of a second.
The database uses indexes to pre-sort data in a variety of ways to make it faster to sort through when you eventually request data from it.
Databases respond to queries
Databases also allow you to pinpoint individual pieces of data relatively quickly. At a high level, you can make a request to the database to return particular rows and columns of data you want to access (whether it be for creating, reading, updating or deleting), and the database will automatically find that data and return it without you having to loop through the other rows and columns manually.
Of course it gets complicated, like everything else, but the idea is straightforward. You make a request for data, and the server responds. A request like this for a particular set of data is called a “query”, and SQL is the language we will use to make queries.
A typical SQL query to request the contents of all the rows out of a table called “users” would look something like this:
SELECT * FROM users
We’ll get into the details of SQL syntax later.
Relationships between tables
Data in one table is often related to data in other tables. This can be most easily explained through an example.
Let’s say, hypothetically, that we’re building a site where users can upload photos to a blog. The first step would be to have a ‘users’ table that stores just the basic information about each user. It might looks something like this:

users table
Notice that there is an “id” field. So each row, meaning each entry in this table, has a unique identifier. Each table must have a unique identifier, known in database parlance as the primary key.
So if I were to ask a question, such as “what is the username of user #3?”, it would be possible, in code, to write a SQL query that answers this question. The code would look for the “username” field of the row in the “users” table with id=3, and that query would return the answer. That query might look something like this:
SELECT username FROM users WHERE id=3
In order to keep track of which images have been uploaded by which users, we might create another table called “images”. This table would have the details of each image which has been uploaded, including which user uploaded it. The “images” table might be set up as follows:

images table
Again, we see that there is an “id” field, which is the primary key for the “images” table, just like we had an “id” field in the “users” table. Each table will have an “id” field that serves as the primary key for that table.
But notice that the “images” table also has a field called “user_id”, which would be used indicate the primary key of the user who uploaded the image in each row. So when we, in our code, create a new row in the “images” table, we would tell the database what data to put in each of the fields; and in the “user_id” field, we would tell it to put in the unique identifier of the particular user who uploaded that image in that field.
Then, if we were, in code, to read the data out of the “images” table, we be able to see that user #1 uploaded photo #2, and user #3 uploaded photo #3, for example.
This “user_id” field in this case is called a foreign key. The term, foreign key, just means that we are using the primary key of one table (in this case, the “users” table) as a piece of data in another table (in this case, the “images” table). That is how we conceptually link the data between two tables.
It’s important to note that the relationship between the “id” field of the “users” table and the “user_id” field of the “images” table is not something that is automatically managed and handled by MySQL. Some database servers do have a special feature where the database automatically “knows” about this relationship. But the MySQL server we are using is ignorant of this relationship, so we have to make sure our code is smart enough to know what data is related to what other data – it’s not built into the database.
July 22nd, 2009 §
A few of you may be interested in adding search functionality to your sites. Unfortunately, creating a really good search is something that is far beyond the scope of this course.
However, there are a few simple options: using MySQL’s built-in search features, and using Google Search on your site.
Using MySQL to do search
I have written a post outlining the built-in search features available in MySQL. It is obvious but nevertheless important to note that in order to use MySQL’s search features, you need to have all the searchable data on your site stored in MySQL tables.
Using Google Custom Search
Google Custom Search is relatively easy to add to your page, and does not require you to be using MySQL. You simply copy and paste some code that google generates for you when you sign up for the service. This is clearly an advantage, since it will make your entire site searchable, not just those pages that use MySQL. However, there are two disadvantages: The search bar is branded with the Google logo, and when a user performs a search, they are taken to Google’s web site, which means they are taken away from your website. Click here to see an example of Google Custom Search in action.
July 21st, 2009 §
Here are some posts that attempt to elucidate the concepts behind working with databases.
Intro to Relational Databases – the concepts behind databases, tables, rows, and fields
Administering MySQL using phpMyAdmin – another post explaining how to use phpMyAdmin to manage your tables
Select Statements in MySQL – reading data from databases can be more sophisticated than simply pulling all the data from a specific table. This post explains some of the finer techniques of reading from a table.
Debugging PHP and MySQL - some tips on how to figure out where any errors are coming from
Making Timestamps User-Friendly – the “created” field in all of our tables in the database stores the date a row was entered into the table. This post explains how to output those dates in a user-friendly way.
Pagination in PHP – when you pull lots of data from the database, you often will not want to display it all on one page. This post explains the concepts behind breaking up the set of results onto multiple pages. It’s a little advanced at this point, but if you’re brave…