Blog Design Solutions (2006)
.pdf
B L O G D E S I G N S O L U T I O N S
Stepping through the code, the first line extracts the post_id from the query string:
$post_id = (isset($_REQUEST["post_id"]))?$_REQUEST["post_id"]:"";
Next, the preg_match function uses a regular expression to check that the post_id is a number. This is important for two reasons. First, from a practical point of view, the only valid post_ids are numbers, so there is no point dealing with post_ids that are not numbers. Second, from a security point of view, anyone can type anything into a query string, so it’s vital to filter out any potentially harmful input. So if a valid-looking post_id has been identified, a flag (by way of the variable editmode) is set so the script knows it is in edit mode (this will be used later):
if (preg_match("/^[0-9]+$/", $post_id)) { $editmode = true;
Using regular expressions is a way of searching and matching patterns in text. The syntax is complex and seemingly obscure, but they are nonetheless a fast and powerful method of performing extremely specific searches and replacements in text. If you want to learn more about regular expressions, I recommend Nathan A. Good’s book Regular Expression Recipes (for more details see www.apress.com/book/bookDisplay.html?bID=396).
Next, using
This |
line, |
the |
an |
array; |
This |
array |
results, |
the |
not |
null, |
the |
The |
each |
input |
|
286
W R I T E Y O U R O W N B L O G E N G I N E
<input type="text" name="title" size="40"
value="<?php if (isset($title)) {echo $title;} ?>" />
Similarly for each textarea:
<textarea name="summary" rows="5" cols="60"> <?php if (isset($summary)) {echo $summary;} ?> </textarea></p>
And this:
<textarea name="post" rows="20" cols="60"> <?php if (isset($post)) {echo $post;} ?> </textarea>
If you now enter http://127.0.0.1/cms/addpost.php?post_id=1 into your browser, the form should populate with the first post you added. However the submit button will still say Add a post (and if you click it, that’s exactly what will happen—you will get a second post identical to the first, but with a post_id of 2). To deal with this, I will introduce case switching in PHP. Replace the submit button with this code:
<?php
switch ($editmode) { 7 case true:
echo "<input type='hidden' name='post_id' value='$post_id' />"; echo "<input type='Submit' name='submitUpdate'
value='Update post' />"; break;
case false:
echo "<input type='Submit' name='submitAdd' value='Add post' />"; break;
}
?>
The switch statement is similar to a series of if statements on the same expression. On many occasions, you might want to compare the same variable with many different values and execute a different piece of code depending on which value it equals to. This is exactly what the switch statement is for. In this case, you are echoing a different submit button depending on the value of $editmode. You can apply a similar approach to the title and heading of your page. Note that I have also added a hidden form field to the Update post button so the form also submits the post_id of the current post. The script will then know which post to update.
And finally the SQL script to update the post. Insert this code just after where you set $editmode=true:
// If form has been submitted, update post if (isset($_POST["submitUpdate"])) {
$sql = "UPDATE posts SET title='$db_title', postdate='$db_postdate',
287
B L O G D E S I G N S O L U T I O N S
summary='$db_summary', post='$db_post'
WHERE post_id = $post_id"; $result = mysql_query($sql); if (!$result) {
$message = "Failed to update post. MySQL said " . mysql_error();
}else {
$message = "Successfully update post '$title'.";
}
}
This script is very similar to the insert post code. To start off, the script checks that the Update post button has been clicked. Then an SQL query to update the post is constructed using an UPDATE statement with this syntax:
UPDATE table SET column1=value, column2=value WHERE condition is met
Note that WHERE clauses in UPDATE statements work in exactly the same way as in SELECT statements.
Now you can add new posts and update existing ones. Reload http://127.0.0.1/cms/ addpost.php?post_id=1 in your browser and try updating the text of the post you added earlier. Your result should look something like Figure 7-8.
Figure 7-8. Successfully updating a blog post in your CMS
288
W R I T E Y O U R O W N B L O G E N G I N E
Here is the final code for the addpost.php page:
<?php
//If magic quotes is turned on then strip slashes if (get_magic_quotes_gpc()) {
foreach ($_POST as $key => $value) { $_POST[$key] = stripslashes($value);
}
}
//Extract form submission
$title = (isset($_POST["title"]))?$_POST["title"]:""; $postdate = (isset($_POST["postdate"]))?$_POST["postdate"]:""; $summary = (isset($_POST["summary"]))?$_POST["summary"]:""; $post = (isset($_POST["post"]))?$_POST["post"]:"";
$submitAdd = (isset($_POST["submitAdd"]))?$_POST["submitAdd"]:"";
// Open connection to database include($_SERVER["DOCUMENT_ROOT"] . "/db_connect.php");
// Prepare data for database $db_title = addslashes($title);
$db_postdate = addslashes($postdate); 7 $db_summary = addslashes($summary);
$db_post = addslashes($post);
// If form has been submitted, insert post into database if ($submitAdd) {
$sql = "INSERT INTO posts (title,postdate,summary,post)
VALUES ('$db_title', '$db_postdate', '$db_summary', '$db_post')"; $result = mysql_query($sql);
if (!$result) {
$message = "Failed to insert post. MySQL said " . mysql_error();
}else {
$message = "Successfully inserted post '$title'.";
}
}
// Get post_id from query string
$post_id = (isset($_REQUEST["post_id"]))?$_REQUEST["post_id"]:"";
// If post_id is a number, get post from database if (preg_match("/^[0-9]+$/", $post_id)) {
$editmode = true;
// If form has been submitted, update post if (isset($_POST["submitUpdate"])) {
$sql = "UPDATE posts SET
289
B L O G D E S I G N S O L U T I O N S
title='$db_title', postdate='$db_postdate', summary='$db_summary', post='$db_post'
WHERE post_id = $post_id"; $result = mysql_query($sql); if (!$result) {
$message = "Failed to update post. MySQL said " . mysql_error();
}else {
$message = "Successfully update post '$title'.";
}
}
$sql = "SELECT title, postdate, summary, post FROM posts WHERE post_id=$post_id";
$result = mysql_query($sql);
$mypost = mysql_fetch_array($result);
if($mypost) {
$title = $mypost["title"]; $postdate = $mypost["postdate"]; $summary = $mypost["summary"]; $post = $mypost["post"];
}else {
$message = "No post matching that post_id.";
}
}else {
$editmode = false;
}
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head>
<meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
<title><?php
switch ($editmode) { case true:
echo "Edit a post"; break;
case false:
echo "Add a post"; break;
}
?>
- Blog CMS</title> </head>
<body>
290
W R I T E Y O U R O W N B L O G E N G I N E
<h1>
<?php
switch ($editmode) { case true:
echo "Edit a post"; break;
case false:
echo "Add a post"; break;
}
?>
</h1>
<?php
if (isset($message)) {
echo "<p class='message'>$message</p>";
}
?>
<form method="post" action="<?php echo $_SERVER["PHP_SELF"] ?>"> <p>Title: <input type="text" name="title" size="40"
value="<?php if (isset($title)) {echo $title;} ?>" /></p> 7 <p>Date/time: <input type="text" name="postdate" size="40"
value="<?php if (isset($postdate)) {echo $postdate;} ?>" /> yyyy-mm-dd hh:mm:ss </p>
<p>Summary:<br />
<textarea name="summary" rows="5" cols="60"> <?php if (isset($summary)) {echo $summary;} ?> </textarea></p>
<p>Post:<br />
<textarea name="post" rows="20" cols="60"> <?php if (isset($post)) {echo $post;} ?> </textarea></p>
<p>
<?php
switch ($editmode) { case true:
echo "<input type='hidden' name='post_id' value='$post_id' />"; echo "<input type='Submit' name='submitUpdate'
value='Update post' />"; break;
case false:
echo "<input type='Submit' name='submitAdd' value='Add post' />"; break;
}
?>
</p>
</form>
</body>
</html>
291
B L O G D E S I G N S O L U T I O N S
Creating a screen for listing posts
At this point, you can add and edit blog posts, but when it comes to editing a post you still have to know its post_id and type the number into your browser. Not exactly convenient, I’m sure you’ll agree. Far more useful would be a list showing all your blog posts so you can select which post to edit. And that’s what I’ll show you now.
All that is required is one SELECT statement and a PHP loop. Here’s the entire code; save it in your cms directory as index.php (because you’ll want it to be the default page in your CMS):
<?php
//Open connection to database include("../db_connect.php");
//Select all posts in db
$sql = "SELECT post_id, title, DATE_FORMAT(postdate, '%e %b %Y at %H:%i') AS
dateattime FROM posts ORDER BY postdate DESC"; $result = mysql_query($sql);
$myposts = mysql_fetch_array($result); ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head>
<meta http-equiv="content-type" content="text/html; charset= iso-8859-1" />
<title>All blog posts - Blog CMS</title> </head>
<body>
<h1>All blog posts</h1>
<?php
if (isset($message)) {echo "<p class='message'>".$message."</p>";}
if($myposts) { echo "<ol>\n"; do {
$post_id = $myposts["post_id"]; $title = $myposts["title"]; $dateattime = $myposts["dateattime"]; echo "<li value='$post_id'>";
echo "<a href='addpost.php?post_id=$post_id'>$title</a> posted $dateattime";
echo "</li>\n";
} while ($myposts = mysql_fetch_array($result)); echo "</ol>";
292
W R I T E Y O U R O W N B L O G E N G I N E
}else {
echo "<p>There are no blog posts in the database.</p>";
}
?>
</body>
</html>
Stepping through the code: first a connection to the database is opened as before. Then an SQL statement is constructed to pull all the posts out of the database:
SELECT post_id, title, DATE_FORMAT(postdate, '%e %b %Y at %H:%i')
AS dateattime FROM posts ORDER BY postdate DESC
I introduced a couple of new functions here. First, the ORDER BY clause at the end of the query sorts all the posts into date order with the newest first. You can order by more than one field at a time by using a comma-separated list. For example this clause would sort by postdate first and then by title for those posts with the same date:
ORDER BY postdate DESC, title ASC
The other new function is DATE_FORMAT. Because the postdate field was created with a data
type of DATETIME, MySQL can manipulate the date and time data—it couldn’t do this if the 7 date were stored as normal text. DATE_FORMAT allows you to output a date and time exactly as
you wish. In this case, the postdate, which is currently stored as something like 2005-04-03 15:30:00, is formatted and displayed as 3 Apr 2005 at 15:30. An alias for the formatted date is created using AS dateattime (this is for convenience, as will become apparent later in this section). More information about the date_format function can be found on the MySQL website at http://dev.mysql.com/doc/refman/4.1/en/date-and-time-functions.html.
Moving on to the main body of the HTML document, I have inserted a chunk of PHP after the main heading. This script steps through all the posts retrieved from the database in the afore-mentioned SELECT query and displays them in a list with links through to the edit page. Here’s how:
<?php if($myposts) {
echo "<ol>\n";
Firstly the code checks that the $myposts array is not null; in other words, it checks that there are actually some posts to display. If so, it opens an HTML ordered list.
do {
..
} while ($myposts = mysql_fetch_array($result));
Then a do-while loops through all the posts returned by the SELECT query. A do-while loop repeats everything within it until the while expression is no longer true. The key here is that every time the mysql_fetch_array function is executed it advances to the next row in the query result thus populating the $myposts array with the next post pulled from the
293
B L O G D E S I G N S O L U T I O N S
database. $myposts becomes false after there are no more posts to show, and at this point the while expression knows to stop looping.
$post_id = $myposts["post_id"]; $title = $myposts["title"]; $dateattime = $myposts["dateattime"]; echo "<li value='$post_id'>";
echo "<a href='addpost.php?post_id=$post_id'>$title</a> posted $dateattime";
echo "</li>\n";
Inside the do-while loop, the blog post information is extracted from the $myposts array and used to display a list item for each post. This list item links through to addpost.php, appending the post_id in the query string as you had to do in your browser earlier. Note that the post date element is accessed using the dateattime alias created in the SELECT statement. If the alias hadn’t been created, the formatted date would be accessed using this rather clumsy code:
$dateattime = $myposts["DATE_FORMAT(postdate, '%e %b %Y at %H:%i')"];
Enter http://127.0.0.1/cms/ in your browser and you should see the list of blog posts currently in your database. The resulting page should look something Figure 7-9.
Figure 7-9. Your CMS homepage showing a list of all blog posts in the database
Deleting a post
The final operation required for your CMS is the capability to delete unwanted posts. This functionality will be built into your index.php page by adding a delete button next to the link for each post and adding a delete script. First, insert the delete link into each list item by amending your code as follows:
echo "<li value='$post_id'>";
echo "<a href='addpost.php?post_id=$post_id'>$title</a> posted $dateattime";
echo " [<a href='".$_SERVER["PHP_SELF"]."?delete=$post_id' onclick='return confirm(\"Are you sure?\")'>delete</a>]"; echo "</li>\n";
This link sends delete=post_id as the query string so the delete script will know which post to delete. I have also added a JavaScript confirmation to help prevent any unwanted deletions because MySQL has no built-in undo functionality. Here’s the delete code; insert it just after the database connection. That way, a deletion will occur before the SELECT query, and hence the post list will be updated correctly:
294
W R I T E Y O U R O W N B L O G E N G I N E
// If delete has a valid post_id
$delete = (isset($_REQUEST["delete"]))?$_REQUEST["delete"]:""; if (preg_match("/^[0-9]+$/", $delete)) {
$sql = "DELETE FROM posts WHERE post_id = $delete LIMIT 1"; $result = mysql_query($sql);
if (!$result) {
$message = "Failed to delete post $delete. MySQL said
". mysql_error();
}else {
$message = "Post $delete deleted.";
}
}
The script is almost identical to that used for updating a post. The only slight difference is the SQL query itself:
DELETE FROM table WHERE condition is met
LIMIT max num of records to act upon
The WHERE clause works in an identical way to that used with UPDATE and SELECT. I have added a LIMIT clause as a safety measure to ensure that the DELETE command deletes only
a single row. The LIMIT clause can also be used with UPDATE and SELECT statements. As I 7 said earlier, MySQL has no inbuilt undo functionality so the DELETE command is danger-
ously simple. For example, this command will delete all the posts in your table!
DELETE FROM table
Note that when your post is deleted, the remaining post_ids do not renumber. So if you have three posts and delete post number 2, the remaining posts will still be numbered 1 and 3, and any subsequent new post will be numbered 4. This is a feature of MySQL that ensures database entries remain uniquely identified with the same id. Figure 7-10 shows the result.
Figure 7-10. Your CMS index page showing the first post and delete link
Finishing touches
To tie together the CMS functionality, you need a way of navigating between the Index page and the Add a Post page, and back. A neat way to achieve this is with another include. The following code simply links between the two CMS pages you have created thus far:
<ul id="nav">
<li><a href="index.php">List of posts</a></li> <li><a href="addpost.php">Add a post</a></li>
</ul>
295
