Pourquoi utiliser tout un Framework comme Laravel lorsqu'on a une tâche toute simple à effectuer ? On peut évidemment utiliser la version allégée Lumen. Mais on peut aussi tout simplement faire ses courses dans Packagist. Je vais montrer dans cet article à quel point Composer est simple et efficace et la richesse des composants qu'on a à notre disposition. Mais comme tout ça peut paraître très théorique ou alors perdu dans les méandres de Laravel par exemple, je vous propose de considérer un cas pratique qui va servir d'illustration, en l'occurrence on va construire un convertisseur Markdown en ligne.
Composer
Mais d'abord qu'est-ce que Composer ? C'est tout simplement un gestionnaire de dépendances pour PHP. On lui dit quels composants on désire et lui se fait un plaisir de les installer pour nous. En plus il met en place un chargement automatique des classes concernées. Et pour clôturer la chose il va aussi assurer les mises à jours.
Si vous utilisez Laravel vous connaissez déjà Composer et il est installé sur votre ordinateur. Si ce n'est pas le cas alors c'est le moment de le faire, que ce soit pour Linux ou pour Windows. Il existe aussi une version phar si vous ne voulez pas l'installer globalement.
Packagist
Allons faire notre marché sur Packagist. On va avoir besoin de :- un routeur
- un convertisseur Markdown
On voit que le plus utilisé est nikic/fast-route, mais on voit aussi que league/route est basé sur ce package. Comme j'aime bien les packages de league j'opte pour celui-ci.
Voyons maintenant pour Markdown :
Là sans hésiter je choisis michelf/php-markdown.
Installation
Maintenant que les packages sont choisis comment les installer ?Commençons par préparer un dossier sur un serveur pour accueillir notre application. Autant l'appeler markdown. Avec la console on se place dans ce dossier et on commence par entrer :
composer require league/route
On attend un petit peu et on obtient ce genre de chose :
Using version ^1.1 for league/route ./composer.json has been created Loading composer repositories with package information Updating dependencies (including require-dev) - Installing symfony/http-foundation (v2.7.0) Loading from cache - Installing nikic/fast-route (v0.5.0) Loading from cache - Installing league/container (1.3.2) Loading from cache - Installing league/route (1.1.0) Loading from cache Writing lock file Generating autoload files
Voyons le résultat :
On se retrouve avec pas mal de choses !
Quand on installe un composant il peut avoir des dépendances, alors Composer les installe également. Ici on voit qu'on a nikic/fast-route, mais ça on s'en doutait. Mais on a également symfony/http-foundation qui est un peu l'incontournable pour le http, et qui est d'ailleurs aussi utilisé par Laravel. On trouve aussi un autre composant de league : league/container.
On voit que les composants sont bien rangés dans des sous-dossiers de vendor :
On trouve aussi un fichier composer.json avec ce code :
{ "require": { "league/route": "^1.1" } }
C'est ici qu'on définit les composants qu'on veut, ici Composer a mis celui que nous avons désigné. On peut très bien maintenant ajouter des noms de composants ici et utiliser composer update pour les installer.
On a aussi un fichier composer.lock qui sert à la maintenance interne de Composer. C'est là qu'il référence tout ce qu'il a installé. On n'a normalement pas à changer ce code à moins de savoir très précisément ce qu'on fait !
On a enfin un dossier composer :
Ce dossier est destiné au chargement automatique des classes (mais aussi des fichiers éventuels). Je ne vais pas entrer dans leur description parce qu'ici aussi c'est Composer qui gère tout ça pour nous. Par exemple voilà le code pour le chargement automatique à la sauce PSR-4 :
<?php // autoload_psr4.php @generated by Composer $vendorDir = dirname(dirname(__FILE__)); $baseDir = dirname($vendorDir); return array( 'Symfony\\Component\\HttpFoundation\\' => array($vendorDir . '/symfony/http-foundation'), 'League\\Route\\' => array($vendorDir . '/league/route/src'), 'League\\Container\\' => array($vendorDir . '/league/container/src'), 'FastRoute\\' => array($vendorDir . '/nikic/fast-route/src'), );
On retrouve les composants avec leurs emplacements précisés ainsi que leurs espaces de noms. On n'aura ainsi pas à se soucier de savoir où sont placés nos classes, il suffira de préciser leur espace de nom !
On va aussi installer le convertisseur Markdown :
composer require michelf/php-markdown
On obtient ça :
Using version ^1.5 for michelf/php-markdown ./composer.json has been updated Loading composer repositories with package information Updating dependencies (including require-dev) - Installing michelf/php-markdown (1.5.0) Loading from cache Writing lock file Generating autoload files
On nous dit que composer.json a été mis à jour :
{ "require": { "league/route": "^1.1", "michelf/php-markdown": "^1.5" } }
Et évidemment on trouve un nouveau dossier pour ce composant :
On voit aussi que ce composant n'a aucune dépendance.
Des urls propres
Comme on va utiliser un fichier index.php pour récupérer toutes les urls on va avoir besoin d'un fichier .htaccess. Il faudra réécrire les urls, donc avoir le module mod_rewrite activé. Pour le code du fichier .htaccess je me suis contenté de reprendre celui de Laravel :
<IfModule mod_rewrite.c> <IfModule mod_negotiation.c> Options -MultiViews </IfModule> RewriteEngine On # Redirect Trailing Slashes... RewriteRule ^(.*)/$ /$1 [L,R=301] # Handle Front Controller... RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^ index.php [L] </IfModule>
L'application
Maintenant que l'intendance est en place il faut écrire le code de l'application. J'ai fait simple parce que le but est surtout de montrer comment Composer nous aide, pas pour aboutir à une application impeccable. Voici le code du fichier index.php :
<?php use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use League\Route\RouteCollection; use Michelf\Markdown; // Autoload require 'vendor/autoload.php'; // Création du routeur $router = new RouteCollection; // Création routes $router->addRoute('GET', '/convert', function (Request $request, Response $response) { $html = '<form action="convert" method="POST"> <textarea rows="4" cols="50" name="content"></textarea> <p><input type="submit" value="OK"></p></form>'; $response->setContent($html); $response->setStatusCode(200); return $response; }); $router->addRoute('POST', '/convert', function (Request $request, Response $response) { $content = $_POST['content']; $content = '<h1>Conversion obtenue :</h1>' . Markdown::defaultTransform($content); $response->setContent($content); $response->setStatusCode(200); return $response; }); // On traite la requête $dispatcher = $router->getDispatcher(); $request = Request::createFromGlobals(); $response = $dispatcher->dispatch($request->getMethod(), $request->getPathInfo()); // On renvoie la réponse $response->send();
Voyons un peu ça. Pour que le chargement automatique des classes fonctionne il faut charger le fichier vendor/autoload.php :
require 'vendor/autoload.php';
Du coup avec quelques use on peut référencer les classes :
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use League\Route\RouteCollection; use Michelf\Markdown;
Il est ensuite facile de créer le routeur :
$router = new RouteCollection;
On ajoute ensuite 2 routes :
$router->addRoute('GET', '/convert', function (Request $request, Response $response) { $html = '<form action="convert" method="POST"> <textarea rows="4" cols="50" name="content"></textarea> <p><input type="submit" value="OK"></p></form>'; $response->setContent($html); $response->setStatusCode(200); return $response; }); $router->addRoute('POST', '/convert', function (Request $request, Response $response) { $content = $_POST['content']; $content = '<h1>Conversion obtenue :</h1>' . Markdown::defaultTransform($content); $response->setContent($content); $response->setStatusCode(200); return $response; });
Pour le détail de cette syntaxe vous pouvez vous reporter à la documentation du composant.
On a ainsi 2 routes :
- convert avec le verbe GET pour aller chercher le formulaire
- convert avec le verbe POST pour soumettre le formulaire
$dispatcher = $router->getDispatcher(); $request = Request::createFromGlobals(); $response = $dispatcher->dispatch($request->getMethod(), $request->getPathInfo());On obtient ainsi la réponse qu'il n'y a plus qu'à envoyer :
$response->send();Le code est sommaire mais suffisant pour que l'application fonctionne.
Fonctionnement
Donc avec l'url convert on obtient le formulaire :Ce n'est pas du grand art mais on s'en contentera. On a une zone de texte et un bouton de soumission. On va entrer du code au format markdown. Par exemple :
# Titre Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. - Type some Markdown on the left - See HTML in the right - Magic Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum : Duis aute irure dolor in reprehenderit occaecat cupidatat non proident consectetur adipiscing elit sed do eiusmod tempor incididunt Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
A la soumission on obtient :
On obtient bien la conversion !
Un template
Poursuivons cet exemple en se disant qu'il serait mieux avec un système de template. Comme on aime bien Blade pourquoi ne pas l'utiliser ? On va utiliser le composant philo/laravel-blade :
composer require philo/laravel-blade
Pour le coup on va se retrouver avec pas mal de composants installés :
Mais on a un système de template performant à disposition !
Pour Blade on va prévoir deux nouveaux dossiers :
Le premier pour le cache de Blade et l'autre pour les vues.
On crée les deux vues avec toujours la même prestation minimale :
La première pour le formulaire (form.blade.php) :
<form action="convert" method="POST"> <textarea rows="4" cols="50" name="content"></textarea> <p><input type="submit" value="OK"></p> </form>
La seconde pour le résultat (result.blade.php) :
<h1>Conversion obtenue :</h1> {!! $html !!}
Et voilà le nouveau code de index.php :
<?php use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use League\Route\RouteCollection; use Michelf\Markdown; use Philo\Blade\Blade; // Autoload require 'vendor/autoload.php'; // Initialisation de Blade $views = __DIR__ . '/views'; $cache = __DIR__ . '/cache'; $blade = new Blade($views, $cache); // Création du routeur $router = new RouteCollection; // Création routes $router->addRoute('GET', '/convert', function (Request $request, Response $response) use($blade) { $response->setContent($blade->view()->make('form')->render()); $response->setStatusCode(200); return $response; }); $router->addRoute('POST', '/convert', function (Request $request, Response $response) use($blade) { $content = $_POST['content']; $content = Markdown::defaultTransform($content); $response->setContent($blade->view()->make('result')->with('html', $content)->render()); $response->setStatusCode(200); return $response; }); // On traite la requête $dispatcher = $router->getDispatcher(); $request = Request::createFromGlobals(); $response = $dispatcher->dispatch($request->getMethod(), $request->getPathInfo()); // On renvoie la réponse $response->send();
Comme nouveautés on a la référence du composant pour Blade :
use Philo\Blade\Blade;
L'initialisation de Blade :
$views = __DIR__ . '/views'; $cache = __DIR__ . '/cache'; $blade = new Blade($views, $cache);
L'utilisation de Blade pour générer les vues avec tranmission d'un paramètre dans le second cas :
$response->setContent($blade->view()->make('form')->render()); ... $response->setContent($blade->view()->make('result')->with('html', $content)->render());
L'application est ainsi un peu plus propre sans nous avoir demandé de gros efforts !
On pourrait encore améliorer le code mais je vais m'arrêter là sinon on va se retrouver avec tous les composants de Laravel !
Par bestmomo
Aucun commentaire