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 :

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 :

À 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 :

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 :

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 :

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.

Antoine Benevaut avatar
Antoine Benevaut
PHP and Laravel Consultant

A lire

Autres articles de la même catégorie