PHP and MVC Architecture
The Intermediate capstone's blog put a fair amount of logic directly inside files like index.php and new_post.php. MVC (Model-View-Controller) separates an application into three distinct responsibilities, routed through a single entry point — the architecture underneath Laravel, Symfony, and most other PHP frameworks (previewed properly in Chapter 7).
The Three Roles
- Model — data and business logic; in this course, the
Postclass from the Intermediate capstone is already a Model - View — purely the HTML/output template; no business logic, no database calls
- Controller — receives the request, talks to the Model, picks a View, passes data to it; deliberately "thin"
The Front Controller — One Entry Point for Everything
Instead of one PHP file per page (as the Intermediate blog used), every request is routed through a single file, which decides what to do based on the URL.
A Minimal Router
new $controllerClass() demonstrates that a class name doesn't have to be written literally — it can be stored in a variable and instantiated dynamically, which is exactly what a router needs to do without an enormous hardcoded if/elseif chain for every possible URL.
The Front Controller Itself
PostController::class resolves to the string "App\Controllers\PostController" at compile time — safer and more refactor-friendly than writing that string by hand, since renaming the class or its namespace would automatically update everywhere ::class is used too.
A Thin Controller
The controller's job is deliberately small: get the data, hand it to a view. No SQL, no HTML — both belong elsewhere. This is exactly the "thin controller, fat model" guidance frameworks generally encourage.
A Pure View — No Logic, Just Output
Coding Challenges
Add a second route to the Router example: GET /about, mapped to a new AboutController with a show() method that requires a simple views/about.php containing static "About us" text (no model needed). Trace through, in writing, exactly what happens from the request hitting index.php to the final HTML being produced.
📄 View solutionIdentify the MVC violation in this controller method, and rewrite it correctly: public function show() { $stmt = $pdo->query("SELECT * FROM posts"); echo "<ul>"; foreach ($stmt as $row) { echo "<li>{$row['title']}</li>"; } echo "</ul>"; } — explain in a comment what's wrong and how your rewrite fixes it.
Extend the Router class to support route parameters, e.g. registering "/posts/{id}" and matching a real URL like "/posts/42" by extracting 42 and passing it as an argument to the controller method. Use a regular expression (Intermediate Chapter 9) to implement the matching.
📄 View solutionChapter 3 Quick Reference
- Model — data and business logic; View — pure output template; Controller — thin, coordinates the two
- Front controller — one entry point (public/index.php) routes every request
- Router — maps a URL to a [ControllerClass, 'method'] pair, dispatches dynamically
- new $className() — instantiating a class from a variable, essential for dynamic dispatch
- ClassName::class — gets the fully-qualified class name as a refactor-safe string
- "Thin controller, fat model" — keep business logic and SQL out of controllers and views entirely
- Next chapter: testing with PHPUnit — unit tests, mocking