Vous souhaitez nous soutenir ? Devenez sponsor de l'association sur notre page Github

Utilisez Illuminate en dehors de Laravel

Publié le 4 mars 2024 par Antoine Benevaut
Couverture de l'article Utilisez Illuminate en dehors de Laravel

Vous les utilisez probablement de façon naturelle sans même vous en rendre compte, les bibliothèques "Illuminate" nous rendent bien des services.

Requêter la base de données, faire une nouvelle route, authentifier un utilisateur ... elles sont les composantes applicatives de notre Framework favori ... mais saviez-vous que vous pouvez les utiliser en dehors de Laravel ?

Petit rappel sur la base de code Laravel avant d'aller plus loin :

  • laravel/laravel est un starter kit pour se mettre rapidement au travail. Le Framework y est une dépendance qui est mise en service notamment via les fichiers index.php, artisan & bootstrap/app.php
  • laravel/framework contient l'Application et les services applicatifs, c'est la pierre angulaire qui ordonne et instruit les services sur leur façon de fonctionner.
  • illuminate/* sont les services de laravel/framework, distribué hors du Framework et de façon individuel.

Mais ... si le Framework contient les services applicatifs, pourquoi les rendre disponibles individuellement ?

Vous envisagiez peut-être de développer votre propre framework en tirant parti des fonctionnalités existantes, de créer un package Composer qui devra embarquer une dépendance forte avec une fonctionnalité existante, ou bien alors d'optimiser un service sans nécessiter toutes ces dépendances, les possibilités sont vastes et variées.

Je vous partage cette ressource de Matt Stauffer qui vous propose bon nombre d'exemples pour utiliser les composants Illuminate hors de Laravel.

Passons à l'action !

J'ai récemment été confronté à la refonte d'une API de génération de QR Code dont les enjeux ont été multiples et pour lesquels sans Illuminate, je ne suis pas sûr que cette refonte aurait pu être aussi aboutie.

L'API est composée d'un endpoint "get", paramétrable via plusieurs arguments.

Laravel 9.x sous le capot et intégrant le package simplesoftwareio/simple-qrcode pour la génération des QR Codes.

L'infrastructure, un serveur tout à fait classique avec des contraintes classiques : CPU, RAM et bande passante limitée ... le tout cumulé limitant le nombre de requêtes possible par minute.

Les objectifs de la refonte étaient multiples :

  • Être toujours disponible, s'abstraire des limitations serveurs en migrant sur une fonction Serverless
  • Avoir le minimum de packages composer et supprimer ceux inutilisés
  • Optimiser le poids du projet et ainsi optimiser le temps de déploiement
  • Si possible, pouvoir poursuivre l'utilisation du package simplesoftwareio/simple-qrcode

À l'exception du contexte serveur, pour lequel je ne rentrerais pas plus dans les détails, cette application pourrait se résumer aux actions suivantes :

  • Valider des paramètres, on sait le faire avec Laravel et Illuminate\Validation
  • Utiliser le service de QR Code, Laravel s'occupe de le charger pour nous

On s'aidera également de composer pour se faciliter la récupération des packages et librairies.

Allez ! Ça valide !

On commence par initialiser le projet avec composer.

1mkdir -p flash
2cd flash
3composer init

On va maintenant analyser la première dépendance que l'on va utiliser, Illuminate\Validation, en commençant par installer le package.

1composer require illuminate/validation

Les détails techniques du package sont disponibles sur packagist.org, on s'y intéressera un peu plus bas dans l'article.

Une fois installé, explorons le répertoire vendor/illuminate/validation.

Habituellement, la mécanique de Laravel (avec l'aide de composer) permet de charger automatiquement le "Service Provider" de la librairie, cela vous permet d'utiliser très facilement la "Facade" Validator.

Les deux classes suivantes sont habituellement chargées automatiquement :

  • vendor/illuminate/validation/ValidationServiceProvider.php
  • vendor/illuminate/support/Facades/Validator.php

Alors, tout ça est bien beau, mais nous, on est hors de Laravel, hors du Framework, il n'y a plus de chargement automatique des classes !

Le modèle de conception "Service Provider" permet de créer une instance d'une ou plusieurs classes et de la/les mettre à disposition sous forme d'un singleton. La Facade, quant à elle, facilite l'accès à cette instance.

Voici comment Laravel instancie le validator dans le framework :

1protected function registerValidationFactory()
2{
3 $this->app->singleton('validator', function ($app) {
4 $validator = new Factory($app['translator'], $app);
5 
6 // The validation presence verifier is responsible for determining the existence of
7 // values in a given data collection which is typically a relational database or
8 // other persistent data stores. It is used to check for "uniqueness" as well.
9 if (isset($app['db'], $app['validation.presence'])) {
10 $validator->setPresenceVerifier($app['validation.presence']);
11 }
12 
13 return $validator;
14 });
15}

Portons notre attention à cette ligne qui crée l'instance et qui est ensuite partagée à la Facade.

1$validator = new Factory($app['translator'], $app);

Nous allons donc créer une instance du Validator de la même façon en faisant très attention aux paramètres.

1// le constructeur de la class `Illuminate\Validation\Factory`
2public function __construct(
3 Translator $translator,
4 Container $container = null
5)

On voit ici que seul le premier paramètre est obligatoire et doit être une instance de la classe Illuminate\Translation\Translator.

Sur packagist.org, sans surprise, on peut voir que le package illuminate/translation est une dépendance du package illuminate/validation.

Commençons à écrire nos premières lignes de code en ajoutant un fichier index.php à la racine du projet, on lie ensuite l'autoloader de composer :

1<?php declare(strict_types=1);
2 
3require __DIR__ . '/vendor/autoload.php';

Le Serverless impose de servir une fonction, la forme du index.php ne sera pas la même que sur Laravel.

1<?php declare(strict_types=1);
2 
3require __DIR__ . '/vendor/autoload.php';
4 
5return function ($event = []) {
6 
7};

On peut alors coder la validation des arguments de cette fonction.

1<?php declare(strict_types=1);
2 
3require __DIR__ . '/vendor/autoload.php';
4 
5use Illuminate\Translation\{ArrayLoader, Translator};
6use Illuminate\Validation\Factory;
7 
8return function ($event = []) {
9 
10 /*
11 * Pour instancier l'objet des traductions,
12 * il faut générer un tableau de traduction
13 */
14 $validationTranslations = require __DIR__ . '/vendor/illuminate/translation/lang/en/validation.php';
15 $translations = (new ArrayLoader())
16 ->addMessages('en', 'validation', $validationTranslations);
17 
18 // On crée l'instance, l'objet du Translator
19 $translator = new Translator($translations, 'en');
20 
21 // On peut maintenant créer l'objet du Validator
22 $validator = new Factory($translator);
23 
24 /*
25 * C'est parti ! On peut valider un array, ici `$event`,
26 * le tableau qui contient les arguments de la fonction Serverless
27 */
28 $validator
29 ->make($event, [
30 'correction' => 'required|string|in:L,M,Q,H',
31 'format' => 'required|string|in:png,svg,eps',
32 'size' => 'required|integer',
33 'text' => 'required|string',
34 'image' => 'url',
35 ])
36 ->validate();
37 
38 unset($validator);
39 
40 // ici, on va générer le QR Code
41 
42 return // et là, on va retourner le QR Code;
43};

Top ! On vient d'utiliser illuminate\validation hors de Laravel !

Ça flashe pour moi, la méthode générale

Ok, on vient d'intégrer illuminate en dehors de Laravel, maintenant comment ça fonctionne pour un package Laravel Open Source ? Eh bien, de la même façon :

  • On trouve la classe qui est instanciée par le "Service Provider"
  • On s'approprie les paramètres et on liste les dépendances de la classe
  • On ajoute les dépendances à composer

Avant de lire le code qui suit, essayez de faire l'exercice avec le package simplesoftwareio/simple-qrcode et si vous avez besoin de support n'hésitez à nous rejoindre sur discord

Voilà ce que ça donne :

1<?php declare(strict_types=1);
2 
3require __DIR__ . '/vendor/autoload.php';
4 
5use Illuminate\Translation\{ArrayLoader, Translator};
6use Illuminate\Validation\Factory;
7use SimpleSoftwareIO\QrCode\Generator;
8 
9return function ($event) {
10 
11 // Ici, il y a le code du Validator
12 
13 /*
14 * Il n'y a pas de paramètre pour le générateur de QR Code
15 * On crée une nouvelle instance, et on l'utilise directement
16 */
17 $qrGenerator = new Generator();
18 
19 $qrCode = $qrGenerator
20 ->format($event['format'])
21 ->errorCorrection($event['correction'])
22 ->size($event['size']);
23 
24 unset($qrGenerator);
25 
26 if (isset($event['image'])) {
27 $qrCode->merge($event['image'], .2, true);
28 }
29 
30 return base64_encode($qrCode->generate($event['text'])->toHtml());
31};

La fonction Serverless est prête, nous allons l'exécuter pour nous assurer que tout fonctionne correctement.

Créez un fichier test.php et collez-y le script ci-dessous.

1<?php declare(strict_types=1);
2 
3$event = [
4 "correction" => "L",
5 "format" => "png",
6 "size" => 100,
7 "text" => "1234",
8];
9 
10$qrCodeFunction = require('./index.php');
11 
12$base64QrCode = call_user_func($qrCodeFunction, $event);
13 
14echo $base64QrCode;

Pour lancer l'exécution, dans un terminal, exécutez php test.php, votre QR Code apparaitra sous la forme d'une chaine de caractère encodée en base64.

Service Provider ou Instance de classe ?

Dans le chapitre précédent, le contexte de la refonte de l'API de QR Code a dicté le besoin d'investiguer dans les différentes librairies que nous avons utilisées, pour trouver les bonnes classes à instancier et à utiliser. On est revenu au b.a.-ba, nous avons fait du PHP.

Dans le projet de Matt Stauffer, il présente un contexte beaucoup plus orienté application. Rappelez-vous de mon commentaire plus haut

Le modèle de conception "Service Provider" permet de faire une instance d'une ou plusieurs classes et la/les mettre à disposition sous forme d'un singleton

Dans les projets plus conséquents avec plusieurs fichiers, des classes applicatives et/ou metiers, vous allez certainement avoir besoin d'appeler plusieurs fois une même classe pour l'utiliser à de multiple reprise au cours du cycle de vie d'une de vos requêtes.

Pour ce faire, nous allons utiliser illuminate/container, à l'instar de Laravel, nous allons créer une "Application".

Il n'est pas rare que les Frameworks aient un objet central qui orchestre l'ensemble de ses services et que cette Application soit nommée "Container".

Regardons le code du package illuminate/container, nous remarquons qu'il n'y a pas de "Service Provider" ni de mécanique associée à Laravel, ici, on va directement utiliser la classe Container.

À l'exemple des Services Providers, nous allons maintenant enregistrer des instances de classes dans le Container pour pouvoir les solliciter à notre guise.

1use Illuminate\Container\Container;
2use Illuminate\Translation\{ArrayLoader, Translator};
3use Illuminate\Validation\Factory;
4 
5$app = new Container();
6 
7// On assigne le nom `validator` à notre instance
8$app->singleton('validator', function () {
9 $validationTranslations = require __DIR__ . '/vendor/illuminate/translation/lang/en/validation.php';
10 $translations = (new ArrayLoader())->addMessages('en', 'validation', $validationTranslations);
11 
12 return new Factory(new Translator($translations, 'en'));
13});

Une instance doit être nommée pour qu'on puisse facilement la retrouver par la suite, dans notre cas, nous appellerons ce singleton : validator.

Grâce au Container, nous pouvons désormais facilement récupérer une instance du validator :

1// On utilise notre instance
2$app->get('validator'); // Illuminate\Validation\Factory

Notre tour d'horizon s'achève ici, je vous laisse maintenant explorer ces librairies pour Illuminez votre PHP.

Source : https://gist.github.com/abenevaut/49afd6ae4534395a80e68189a3c829ef
Antoine Benevaut avatar
Antoine Benevaut
PHP & Laravel Consultant - Lead Developer, Paris / Passionate / Cat lover / #laravel 😍

A lire

Autres articles de la même catégorie