The Goal: Connecting All the MVC Pieces
This is the final and most rewarding lesson of this module. We will connect our Model, View, and Controller to dynamically display a single blog post from the database based on the slug in the URL. Here's the complete user flow:
- A user visits
/post/your-post-slug
. - Our Router matches the URL and calls the
show()
method on thePostController
, passing "your-post-slug" as an argument. - The Controller creates an instance of the
Post
Model. - The Controller asks the Model to find the post with that specific slug.
- The Model queries the database and returns the post data (or nothing if not found).
- The Controller receives the data from the Model and passes it to a View file.
- The View renders the HTML, displaying the post's title, content, and other details.
Step 1: Updating the Post Model
Our Post
model currently only has a method to fetch all posts. We need to add a new method to find a single post by its slug. We'll also join with the users
table to get the author's name.
Update your file: app/Models/Post.php
<?php
namespace App\Models;
use App\Core\Model;
class Post extends Model
{
/**
* Fetch all posts from the database.
*/
public function fetchAll()
{
$stmt = $this->pdo->query("SELECT title, content FROM posts ORDER BY created_at DESC LIMIT 5");
return $stmt->fetchAll(\PDO::FETCH_ASSOC);
}
/**
* Find a single post by its slug.
*
* @param string $slug The post slug.
* @return mixed The post data as an array, or false if not found.
*/
public function findBySlug($slug)
{
$sql = "SELECT p.*, u.username AS author_name
FROM posts p
JOIN users u ON p.user_id = u.id
WHERE p.slug = ?
LIMIT 1";
$stmt = $this->pdo->prepare($sql);
$stmt->execute([$slug]);
return $stmt->fetch(\PDO::FETCH_ASSOC);
}
}
Step 2: Creating the Single Post View
Next, let's create the HTML template for displaying a single post. For better organization, we'll create a new subdirectory inside views
.
Create a new folder: views/post/
Create a new file inside that folder: views/post/single.view.php
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- The title will be dynamic based on the post -->
<title><?php echo htmlspecialchars($post['title']); ?></title>
<style>
body { font-family: sans-serif; background-color: #f0f2f5; margin: 0; padding: 20px; color: #333; line-height: 1.6; }
.container { max-width: 800px; margin: 40px auto; background: white; padding: 40px; border-radius: 10px; box-shadow: 0 4px 20px rgba(0,0,0,0.1); }
h1 { font-size: 2.5rem; margin-bottom: 0; }
.meta { color: #888; margin-bottom: 20px; border-bottom: 1px solid #eee; padding-bottom: 20px; }
.content { margin-top: 20px; }
.content p { margin-bottom: 1em; }
.back-link { display: inline-block; margin-top: 30px; color: #007bff; text-decoration: none; }
</style>
</head>
<body>
<div class="container">
<h1><?php echo htmlspecialchars($post['title']); ?></h1>
<div class="meta">
By <strong><?php echo htmlspecialchars($post['author_name']); ?></strong> on <?php echo date('F d, Y', strtotime($post['created_at'])); ?>
</div>
<div class="content">
<?php echo $post['content']; // Assuming content is safe HTML from your editor ?>
</div>
<a href="/" class="back-link">← Back to all posts</a>
</div>
</body>
</html>
Step 3: Updating the PostController
Finally, let's update the PostController
to connect the Model and the View. It will use the findBySlug
method and render our new single.view.php
file.
Update your file: app/Controllers/PostController.php
<?php
namespace App\Controllers;
use App\Models\Post;
class PostController
{
public function show($slug)
{
// 1. Instantiate the model
$postModel = new Post();
// 2. Find the post by its slug
$post = $postModel->findBySlug($slug);
// 3. If no post is found, show a 404 error
if (!$post) {
http_response_code(404);
echo "404 - Post not found.";
return;
}
// 4. If found, render the view and pass the post data to it
// Note the dot notation 'post.single' which our helper translates to 'post/single.view.php'
view('post.single', [
'post' => $post
]);
}
}
Your Mission
- Update your
app/Models/Post.php
file with the newfindBySlug
method. - Create the new directory
views/post
. - Inside that new directory, create the file
single.view.php
. - Update your
app/Controllers/PostController.php
file. - Test it! Find a real post slug from your database and navigate to that URL in your browser (e.g.,
/post/your-actual-post-slug
). You should see your full blog post rendered beautifully by your very own framework!
Module Complete!
Congratulations! You have successfully built a working, extensible MVC framework from the ground up. You've connected all the pieces: a front controller, a dynamic router, controllers, models that talk to the database, and views that render dynamic data. This is a huge accomplishment and provides the foundation for building almost any kind of web application.