Challenge 2 — Solution
Task: Identify the MVC violation in this controller method, and rewrite
it correctly: public function show() { $stmt = $pdo->query("SELECT *
FROM posts"); echo "
"; foreach ($stmt as $row) { echo "-
{$row['title']}
"; } echo "
"; } — explain in a comment what's
wrong and how your rewrite fixes it.
// What's wrong with the original code:
// This "controller" method is doing the job of all three MVC roles at
// once, inside a single method: it runs a raw SQL query directly
// ($pdo->query(...) - the Model's job), and it builds and echoes raw
// HTML directly ($stmt as $row) { echo "..." - the View's job).
// A genuine controller should only coordinate between a Model and a
// View - it should never contain SQL, and it should never echo HTML
// directly. There's also a second, separate problem: {$row['title']}
// is echoed completely unescaped, an XSS vulnerability exactly like
// the one Intermediate Chapter 8 already warned against.
---- src/Models/Post.php (the Model, already exists per the chapter) ----
pdo->query("SELECT * FROM posts")->fetchAll();
}
}
---- src/Controllers/PostController.php (corrected) ----
all();
require __DIR__ . '/../../views/post_list.php';
}
}
---- views/post_list.php (the View, holds the actual output) ----
- = htmlspecialchars($post['title']) ?>
Notes:
- The rewritten show() method contains no SQL and no HTML at all - it
only creates the Model, asks it for data, and requires the View,
exactly matching the chapter's own "thin controller" example.
- All the SQL now lives inside Post::all() (the Model), and all the
HTML now lives inside post_list.php (the View) - each piece is back
in its own correct role.
- The rewrite also fixes the unescaped-output problem the original
code had, by wrapping $post['title'] in htmlspecialchars() inside
the view - a fix that naturally falls out of moving the output logic
into a proper view file that follows this course's own established
escaping convention.