Vanilla Blog — Part 2 | Controllers

Mehdi JaiMehdi Jai
3 min read

Other Parts:


In the previous article, we covered handling simple routes with basic functionality, specifically for GET requests. Now, it's time to enhance our router to handle multiple HTTP methods. Before diving into that, let's first explore the concept of controllers. Controllers act as an intermediary between the routes and the views, encapsulating the logic and handling the requests. They provide a structured way to organize and manage the functionality of your application.

Controller:

A controller is a class that takes care of handling the request and performs necessary operations or processes before rendering the page if required. In certain cases, specific routes may require additional tasks such as fetching data from a database, uploading images, or performing other operations. The controller encapsulates this functionality, allowing for a modular and organized approach to handling different routes and their associated tasks.

Let's create our first controller:

The controller is very simple, it creates a variable and passes it to the view.

// app/controllers/HomeController.php
<?php

class HomeController
{
    protected $data = [];

    public function __construct(array $data)
    {
        $this->data = $data;
    }

    public function index()
    {
        $name = "My Name";
        view("home", [
            ...compact(
                "name",
            ),
            ...$this->data
        ]);
    }
}

We have now to update the Router to require the router instead of the view.

First, let's add the render method to handle controller importing.

// app/Core/Router.php

/**
 * @param $controller Get the controller file name
 * @param $closure Which method in controller to run
 * @param $data Data to pass to controller (optional).
 */
private function render(string $controller, string $closure, array $data = [])
{
    if (count($this->data) > 0) {
        $data = $this->data;
    }

    require base_path("app/controllers/{$controller}Controller.php");

    $instance = new $controller($data);
    call_user_func(array($instance, $closure));
}

Now, the whole class will become:

// app/Core/Router.php

<?php

class Router
{
    private static $instance = null;
    private array $routes = [];

    private array $data = [];

    public static function getInstance()
    {
        if (self::$instance == null) {
            self::$instance = new Router();
        }
        return self::$instance;
    }
    // we will pass controller and closure instead of view name
    public function add(string $uri, string $controller, string $closure = "index")
    {
        $this->routes[] = compact("uri", "controller", "closure");

        return $this;
    }

    public function route()
    {
        foreach ($this->routes as $route) {
            if ($this->matchRoute($route['uri'])) {

                // execute render method instead of view
                $this->render($route['controller'], $route['closure']);
                exit();
            }
        }

        abort(404);
    }

    private function matchRoute(string $routeUri): bool
    {
        $server_uri = preg_replace("/(^\/)|(\/$)/", "", parse_url($_SERVER['REQUEST_URI'])['path']);
        parse_str($_SERVER['QUERY_STRING'], $queries);
        $this->data['queries'] = $queries;

        if (!empty($server_uri)) {
            $routeUri = preg_replace("/(^\/)|(\/$)/", "", $routeUri);
            $reqUri = preg_replace("/(^\/)|(\/$)/", "", $server_uri);
        } else {
            $reqUri = "/";
        }

        return $reqUri == $routeUri;
    }

    /**
     * @param $controller Get the controller file name
     * @param $closure Which method in controller to run
     * @param $data Data to pass to controller (optional).
     */

    private function render(string $controller, string $closure, array $data = [])
    {
        if (count($this->data) > 0) {
            $data = $this->data;
        }

        require base_path("app/controllers/{$controller}.php");

        $instance = new $controller($data);
        call_user_func(array($instance, $closure));
    }
}

Update web.php file:

// routes/web.php

<?php

$router = Router::getInstance();

$router->add("/", "HomeController");
$router->add("/about", "AboutController");
$router->add("/contact", "ContactController");

$router->route();

Let's add $name variable to home view:

// views/home.view.php

<main>
    <h1>Home</h1>
    <p>Hello, <?= $name ?></p>
</main>

The result:

Conclusion:

This is essentially the controller. We will later add a base Controller class to avoid duplicating the __construct method.

See you in the next one!

0
Subscribe to my newsletter

Read articles from Mehdi Jai directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Mehdi Jai
Mehdi Jai

Result-oriented Lead Full-Stack Web Developer & UI/UX designer with 5+ years of experience in designing, developing and deploying web applications.