Vanilla Blog — Part 2 | Controllers
Table of contents
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!
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.