Back to Blog
Lesson 28 of the PHP: Modern PHP from the Ground Up course
PHPAugust 15, 20263 min read

Building the Controller Layer: Managing MVC Requests in PHP

Learn to build an MVC Controller in PHP. We'll show you how to centralize request management, coordinate your models, and cleanly pass data to your views.

PHPMVCControllersBackend DevelopmentOOP
A close-up of a stop button on a public bus, highlighting travel and safety features.

Previously in this course, we explored Integrating Routing Logic: A Custom PHP Router for Your MVC App to direct incoming traffic to specific files. In this lesson, we stop writing "script soup" and move that logic into a structured MVC Controller.

The MVC Controller First Principles

In a Model-View-Controller (MVC) architecture, the controller acts as the "middleman" or traffic cop. Its primary responsibilities are:

  1. Handling the Request: Accepting input (via $_GET or $_POST).
  2. Coordinating Logic: Communicating with your Model to fetch or persist data.
  3. Preparing the View: Formatting the data and passing it to the display layer.

By centralizing these tasks, you ensure that your routing logic doesn't become cluttered with database queries or HTML rendering.

Structuring Your First Controller

Instead of having a standalone file for every page, we create a class where methods represent specific user actions. Let's build a UserController to manage user profiles.

First, ensure your directory structure follows the plan we laid out in our Project Structure Strategy.

PHP
#6A9955">// src/Controllers/UserController.php

namespace App\Controllers;

use App\Models\User;

class UserController
{
    public function showProfile(int $id): void
    {
        #6A9955">// 1. Coordinate: Fetch data from the model
        $userModel = new User();
        $userData = $userModel->find($id);

        #6A9955">// 2. Prepare: "Pass" data to the view
        #6A9955">// For now, we simulate this by including the view file
        require_once __DIR__ . '/../Views/user_profile.php';
    }
}

Passing Data to the View

The controller's job is to ensure the view has exactly what it needs. A common pitfall is passing too much data or "fat" objects that expose internal database structures. Keep it lean by passing associative arrays.

If you are rendering a profile, your controller should look like this:

PHP
#6A9955">// In UserController.php

public function showProfile(int $id): void
{
    $userModel = new User();
    $user = $userModel->find($id);

    if (!$user) {
        #6A9955">// Handle missing users gracefully
        http_response_code(404);
        echo "User not found.";
        return;
    }

    #6A9955">// We make $user available to the included view file
    #6A9955">// The view will simply use the variable $user
    require_once __DIR__ . '/../Views/user_profile.php';
}

Hands-on Exercise: Create a PostController

To move our project forward, perform the following steps:

  1. Create src/Controllers/PostController.php.
  2. Define a method index() that fetches all posts using your existing Post model.
  3. Update your router to instantiate PostController and call index() when the user visits /posts.
  4. Ensure the index.php view file can access the $posts variable created in the controller.

Common Pitfalls

  • Doing Too Much: Never write SQL queries directly inside your controller. If you find yourself writing SELECT * FROM... inside a controller method, you've broken the MVC separation. Keep that in the Model.
  • Echoing in Controllers: Avoid echo statements directly in your controller. Controllers should coordinate data, not output HTML. The require statement is the exception as it hands off control to the View.
  • Hard-coded Dependencies: Notice we manually instantiated new User() inside the method. As your application grows, you will eventually want to use Dependency Injection to make your controllers testable.

FAQ: Controller Best Practices

Q: Should I have one huge Controller or many small ones? A: Many small ones. Group them by resource (e.g., UserController, PostController, CommentController). This keeps files readable and maintainable.

Q: How do I handle form submissions? A: Use separate methods. showForm() handles the GET request to display the form, and store() handles the POST request to save the data.

Recap

We have successfully shifted from procedural scripts to object-oriented controllers. By grouping related actions into classes, we ensure our code is modular and predictable. Your controller now handles the "what" (business logic orchestration) while the view handles the "how" (presentation).

Up next: Implementing the View Layer, where we formalize how to render our templates cleanly.

Similar Posts