Skip to Content
Course content

61: Practice Exercise: Building a Simple Blog Engine

Click on the "Edit" button in the top corner of the screen to edit your slide content.

Alright, it's time to put everything we've learned into a single project. We've talked about PDO, routing, and form handling in isolation, but that's not how software is actually built. To really get this to stick, we're going to build a minimal blog engine. Nothing fancy—no CSS framework, no complex auth—just a functional system to create and read posts.

Setting up the Data Foundation

First, we need a place for our posts to live. I'm keeping it lean: one table. I usually start by sketching out the schema in my head before touching the database. For a blog, we need a unique ID, a title, the actual body content, and a timestamp so we can sort by the newest posts first.

CREATE TABLE posts (
    id INT AUTO_INCREMENT PRIMARY KEY,
    title VARCHAR(255) NOT NULL,
    content TEXT NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

I'll use a separate db.php file for the connection. This keeps the logic clean and prevents me from having to rewrite the PDO connection string in every single file.

Pulling the Feed Together

The homepage is the easiest part. We just want to grab everything from the posts table and loop through it. I'm going to use a foreach loop here because it's cleaner than a while loop when dealing with PDO's fetchAll().

// index.php
require 'db.php';

$stmt = $pdo->query("SELECT * FROM posts ORDER BY created_at DESC");
$posts = $stmt->fetchAll();

foreach ($posts as $post) {
    echo "<article>";
    echo "<h2>" . htmlspecialchars($post['title']) . "</h2>";
    echo "<p>" . substr(htmlspecialchars($post['content']), 0, 150) . "...</p>";
    echo "<a href='post.php?id=" . $post['id'] . "'>Read More</a>";
    echo "</article><hr>";
}

Notice I used htmlspecialchars(). I've seen too many junior devs skip this and leave their sites open to basic XSS attacks. Get in the habit of doing it every single time you echo user-generated content.

Handling the Single Post View (and a quick fix)

Now we need a way to view a specific post based on the ID in the URL. I'll start by grabbing the ID from $_GET and plugging it into a prepared statement.

// post.php
require 'db.php';

$id = $_GET['id'];
$stmt = $pdo->prepare("SELECT * FROM posts WHERE id = ?");
$stmt->execute([$id]);
$post = $stmt->fetch();

echo "<h1>" . htmlspecialchars($post['title']) . "</h1>";
echo "<p>" . nl2br(htmlspecialchars($post['content'])) . "</p>";

Actually, stop right there. I just tried visiting post.php without an ID in the URL, and the page threw a warning and a blank screen. It's a classic mistake—assuming the user will always provide the expected input. If $post is false because no record was found, trying to access $post['title'] will crash. Let's fix that with a simple guard clause.

// Updated post.php logic
$id = $_GET['id'] ?? null;

if (!$id) {
    die("Post ID is missing.");
}

$stmt = $pdo->prepare("SELECT * FROM posts WHERE id = ?");
$stmt->execute([$id]);
$post = $stmt->fetch();

if (!$post) {
    die("Post not found.");
}

echo "<h1>" . htmlspecialchars($post['title']) . "</h1>";
// ... the rest of the code

The Quick-and-Dirty Admin Page

To make this a real "engine," we need a way to add content without manually editing the database. I'll create a simple admin.php file. I'm using a POST request here to ensure we aren't creating posts via a simple URL link.

// admin.php
require 'db.php';

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $title = $_POST['title'];
    $content = $_POST['content'];

    if (!empty($title) && !empty($content)) {
        $stmt = $pdo->prepare("INSERT INTO posts (title, content) VALUES (?, ?)");
        $stmt->execute([$title, $content]);
        echo "<p>Post published successfully!</p>";
    }
}
?>
<form method="POST">
    <input type="text" name="title" placeholder="Post Title" required><br>
    <textarea name="content" placeholder="Write something..." required></textarea><br>
    <button type="submit">Publish</button>
</form>

It's not a polished CMS, but it's a fully functional data cycle: Create in admin.php, List in index.php, and Read in post.php. That is the core of almost every web application you'll ever build.




📋 Practical Task

Exercise: Implementing a Post Category System

Now that your blog engine is working, it's time to organize the content. Your task is to extend the existing system to support categories (e.g., "PHP", "Tutorials", "Life").

  • Database: Add a category column (VARCHAR) to your posts table using an ALTER TABLE statement.
  • Admin: Update admin.php to include a dropdown menu or text input so the user can assign a category when creating a post.
  • Feed: Update index.php to display the category next to the post title.
  • Filtering: Modify index.php so that if a category is passed in the URL (e.g., index.php?category=PHP), it only shows posts belonging to that specific category.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.