Controllers as classes

Exercise 7 of 7 in chapter 6 · 200 XP · 3 objectives checked by tests

Topics covered: ControllerCallable::classDynamic instantiation

public/index.php is overflowing: five routes, five anonymous functions, and more on the way. Madame Lou asks for one last tidy-up before going live: one controller per page, a class in src/Controleur/ (controller). The front controller will only match addresses to controllers.

Your mission

  1. In src/Controleur/, create the classes UneControleur (front page), ArticleControleur, RubriqueControleur (section), RechercheControleur (search) and AnnonceControleur (ad), in the Gazette\Controleur namespace. Create each file with the editor's “New file” button: one file per class, named after it (src/Controleur/UneControleur.php).
  2. Move the code of the anonymous functions into them: afficher() (show) for the first four (with array $parametres for the article and the section), proposer() (submit) for the ad. Don't forget the use statements.
  3. In public/index.php, each route points to its controller with an array: [UneControleur::class, 'afficher'].
  4. When a route is found, create the controller and call its method with the parameters of the address, still inside the try that catches ArticleIntrouvable.

What Symfony would do for you

You have just written, in miniature, what a framework does: a front controller (public/index.php), a router, controllers, templates, responses with their status code, exceptions, forms that are validated and escaped. Symfony adds routes as attributes (#[Route('/articles/{slug}')]), Twig, which escapes everything on its own, a form component, and the database. You now know what's under the hood, and the Symfony for PHP developers track is the natural next step.

Reminder

[UneControleur::class, 'afficher'] points to a method without calling it: the class and the name of the method. new $classe() creates an object of the class whose name is in $classe, and $objet->$methode() calls the method whose name is in $methode.

To get both pieces at once, [$classe, $methode] = $route[1]; puts the first element of the array in $classe and the second in $methode.

UneControleur::afficher() and RechercheControleur::afficher() don't need the parameters of the address. You can still call them with $route[2]: PHP quietly ignores extra arguments passed to a method written in PHP, which lets you call every controller the same way.