As we are wont to do, before sitting down to write the SimpleBlog system, we spent some time planning out the pages we would write and the way in which we will divide the various tiers of the application. As shown in Figure 32-3, our application is a series of pages that interact with various middle-tier services, most notably the user manager, entry manager, and comment manager.
Figure 32-3. The various services and components of the SimpleBlog system.
The SimpleBlog web application consists of eight primary visual pages and an error page to display critical errors in our application that are often not the result of user input. Figure 32-4 shows the complete possible progression of pages; we can see that those pages that are forms (for creating accounts, or entering comments and entries) also have associated pages to process the input for them. (See the section titled "Processing Forms and Page Progression" in Chapter 31, "An Appointment Manager.")
Figure 32-4. Page progressions in the SimpleBlog web application.
The key pages in this web application are as follows:
As mentioned earlier, each of the pages based on form input (createnewacct.php, newentry.php, newcomment.php, and login.php) has an associated script to process the user data before sending the user to some other page. These four pages are submitnewacct.php, submitnewentry.php, submitnewcomment.php, and submitlogin.php respectively. None of these latter pages has a UI, and on failure either sends users back to the original form to correct their input or sends them to error.php, which displays more serious errors.
Database Structure and Notes
The database setup for this sample reflects the more ambitious scope of the web application. In addition to having a table for the entries that users write, we create a table for the users themselves and a table for the comments that people associate with entries. Finally, because we have a complete system for managing logged in users, we also have a table to keep track of them.
We make much greater use of foreign keys in this sample, so we will create all the relevant tables with the InnoDB table storage engine in MySQL. (See the section titled "Table Storage Engines" in Chapter 9, "Designing and Creating Your Database.") We create the database and accounts to work with it via the following queries and statements (MySQL syntax shown):
CREATE DATABASE SimpleBlog DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_general_ci; GRANT CREATE, DROP, SELECT, UPDATE, INSERT, DELETE ON SimpleBlog.* TO 'blog_admin'@'localhost' IDENTIFIED BY 'blog_admin'; GRANT SELECT, UPDATE, INSERT, DELETE ON SimpleBlog.* TO 'blog_user'@'localhost' IDENTIFIED BY 'blog_user'; USE SimpleBlog; SET NAMES 'utf8';
The Users table in the database is straightforward and just stores the key information for a user. Usernames must be unique, and they must specify a password, e-mail address, and full name. Birth dates and user descriptions are optional, and for birth dates, we will let the user specify only portions of itsuch as December 1900, or February 12. We will search frequently on the user's name, so we will create an index for that field:
CREATE TABLE Users ( user_id INTEGER AUTO_INCREMENT PRIMARY KEY, username VARCHAR(50) UNIQUE NOT NULL, password VARCHAR(50) NOT NULL, fullname VARCHAR(200) NOT NULL, email VARCHAR(200) NOT NULL, birthdate DATE, userbio TEXT, INDEX (username) ) ENGINE = InnoDB;
Journal entries are not at all complicated. They have a title and a body (at least one of which must be specified), a posting date/time (which is managed by the system), and an author ID, which is determined and set by the application:
CREATE TABLE JournalEntries ( entry_id INTEGER AUTO_INCREMENT PRIMARY KEY, title VARCHAR(200), posted DATETIME NOT NULL, author_id INTEGER NOT NULL, body TEXT, FOREIGN KEY (author_id) REFERENCES Users (user_id) ) ENGINE = InnoDB;
Comments are stored in a format very similar to entries in the database, except that they are associated with a particular entry, and thus have an enTRy_id field to point to this:
CREATE TABLE JournalComments ( comment_id INTEGER AUTO_INCREMENT PRIMARY KEY, entry_id INTEGER NOT NULL, author_id INTEGER NOT NULL, posted DATETIME NOT NULL, title VARCHAR(200), body TEXT, FOREIGN KEY (entry_id) REFERENCES JournalEntries (entry_id), FOREIGN KEY (author_id) REFERENCES Users (user_id) ) ENGINE = InnoDB;
Finally, to help manage logins, we create a table that connects user IDs to session IDs. This table has to be cleaned up periodically, because the user might close the web browser without clicking the Logout link, but this rarely turns out to be a problem for us. We also store a last access time in this table, which we update whenever a user accesses a page in our system. Logins that have gone more than two hours (configurable) without any activity are considered "stale" and deleted. We search frequently on the session ID, so we will create an index for that field:
CREATE TABLE LoggedInUsers ( user_id INTEGER NOT NULL, session_id VARCHAR(255) NOT NULL, last_access DATETIME, FOREIGN KEY (user_id) REFERENCES Users (user_id), INDEX (session_id) ) ENGINE = InnoDB;
In the Appointments web application, we demonstrated using file inclusion as a means of generating a user interface in our various pages. In the SimpleBlog system, on the other hand, we demonstrate a UI-generation class we have written called HhtmlGenerator. By calling a few key methods on this class, we generate most of the key interface elements in our web application. The general pattern of any pages in our application that generate a user interface will be similar to the following:
<?php ob_start(); require_once('simpleblog/coreincs.inc'); /** * Start generating the page. */ $hg = HtmlGenerator::getInstance(); $hg->startPage('SimpleBlog Login Page'); $hg->openBody(); $hg->emitLeftMenuBar(); $hg->openContent(); // generate page-specific HTML here. /** * Close out the page and exit. */ $hg->closeContent(); $hg->closeBody(); $hg->closePage(); ob_end_flush(); ?>
For the XHTML that we generate for each page, we use a combination of simply displaying markup elements for individual pages and creating new routines on the HtmlGenerator class to help share common UI features across pages.
All of our pages use Cascading Style Sheets (CSS) to further customize the final interface generated by our application. This is stored in basestyle.css and is included in all pages by the startPage method on HtmlGenerator. We use style sheets mostly to set color and font properties on various markup elements in our output.
Complete File Listing
Table 32-1 lists all the files in our SimpleBlog web application.