À table ! C'est Sushi ce midi
Caleb Porzio, fervent contributeur open source, entretient depuis 2020 une petite librairie nommée Sushi.
Cette librairie permet d'utiliser des modèles Eloquent avec comme source de données un tableau (array) à la place d'une base de données.
Ça vous met l’eau à la bouche ? On passe à table !
Sushi s'installe dans votre projet via composer composer require calebporzio/sushi et est fortement dépendant de l'extension pdo-sqlite, veillez à ce qu'elle soit bien installée.
Installation
L'aperçu le plus rapide et le plus parlant que je puisse vous donner est l'exemple de la documentation du package :
1<?php 2 3namespace App\Models; 4 5use Illuminate\Database\Eloquent\Model; 6 7class State extends Model 8{ 9 use \Sushi\Sushi;10 11 protected $rows = [12 [13 'abbr' => 'NY',14 'name' => 'New York',15 ],16 [17 'abbr' => 'CA',18 'name' => 'California',19 ],20 ];21}
Sushi modifie votre modèle via un trait \Sushi\Sushi, le rendant immédiatement indépendant de votre base de données et la remplace par l'utilisation de l'array $rows.
L'utilisation reste ensuite standard :
1$stateName = State::where('abbr', '=', 'NY')->first()->name;2// ou encore3$stateName = State::whereAbbr('NY')->first()->name;
Il en va de même pour la validation des données :
1$data = request()->validate([2 'state' => ['required', 'exists:App\Models\State,abbr'],3]);
Si cela a réveillé en vous de vieux souvenirs, par exemple, la façon dont vous aviez géré des tables de pays, de départements ou encore de fuseaux horaires (timezones), des listes qui ne changent que dans des cas rares ou particuliers, alors Sushi pourrait vous faire gagner du temps la prochaine fois.
Dynamiser ses données
La variable $rows peut être remplacée par la méthode getRows(); cette approche vous permettra de créer votre source de données dynamiquement :
1<?php 2 3namespace App\Models; 4 5use Illuminate\Database\Eloquent\Model; 6 7class CustomData extends Model 8{ 9 use \Sushi\Sushi;10 11 public function getRows()12 {13 $data = [];14 $myFile = storage_path('my_file.csv');15 $handle = fopen($myFile,'r');16 17 while (($currentLine = fgetcsv($handle)) !== FALSE ) {18 $data[] = $currentLine;19 }20 21 fclose($handle);22 23 return $data;24 }25}
Votre méthode getRows() peut aller récupérer les valeurs d'un fichier CSV et les rendre ainsi disponibles via Eloquent.
En termes de performance, l'approche avec la propriété $rows gérera directement pour vous les aspects de cache.
Sushi dispose d'un mécanisme par défaut qui mettra l'array dans une base de donnée SQLite.
Pour l'approche avec la méthode getRows(), vous devrez utiliser les méthodes sushiShouldCache() et sushiCacheReferencePath() pour configurer le cache.
sushiShouldCache() retourne un boolean qui indique simplement si oui ou non, le cache doit être utilisé.
sushiCacheReferencePath(), elle, retourne le chemin d'un fichier sur lequel la librairie testera la date de dernière modification du fichier.
Basiquement, si vous utilisez un fichier CSV pour constituer vos données, si le fichier est modifié ou remplacé, la librairie se chargera de mettre à jour le cache.
1<?php 2 3namespace App\Models; 4 5use Illuminate\Database\Eloquent\Model; 6 7class CustomData extends Model 8{ 9 use \Sushi\Sushi;10 11 public function getRows()12 {13 $data = [];14 $myFile = storage_path('my_file.csv');15 16 // process CSV17 18 return $data;19 }20 21 public function sushiShouldCache()22 {23 return config('app.debug') === false;24 }25 26 public function sushiCacheReferencePath()27 {28 return storage_path('my_file.csv');29 }30}
En plus de cela, il est possible de choisir l'emplacement de votre base SQLite via la méthode sushiCachePath(). Attention toutefois, si vous n'utilisez pas le cache, la base SQLite sera stockée en mémoire (RAM).
Bien que je n'aie pas encore essayé, il me semble possible de remplacer SQLite par un autre système de base de données. Pour les plus aventureux d'entre vous, vous pourriez tester de surcharger la méthode
setSqliteConnection(). Je serais ravi d'avoir vos retours sur ce sujet. Cette base de données SQLite pourrait gêner si votre application fonctionne sur plusieurs serveurs en parallèles.
L'approche avec la méthode getRows() vous obligera également à mettre en place un schéma de base de données.
Dans le cas où votre fichier serait vide, ce schéma permettra de créer la base de données SQLite sans erreur.
1<?php 2 3namespace App\Models; 4 5use Illuminate\Database\Eloquent\Model; 6 7class CustomData extends Model 8{ 9 use \Sushi\Sushi;10 11 protected $schema = [12 'id' => 'integer',13 'name' => 'string',14 'symbol' => 'string',15 'precision' => 'float',16 'created_at' => 'datetime',17 'updated_at' => 'datetime',18 ];19 20 public function getRows()21 {22 return [];23 }24}
Comme vous l'avez certainement constaté, le schéma précise aussi le typage des données de ce modèle et cela que vous utilisiez $rows ou getRows().
Pour davantage faciliter les opérations sur vos données, vous pouvez également utiliser la méthode afterMigrate().
Cela peut vous permettre d'ajouter des index ou d'exécuter toutes autres instructions sur vos colonnes.
1<?php 2 3namespace App\Models; 4 5use Illuminate\Database\Eloquent\Model; 6 7class CustomData extends Model 8{ 9 use \Sushi\Sushi;10 11 protected $schema = [12 'id' => 'integer',13 'name' => 'string',14 'symbol' => 'string',15 'precision' => 'float',16 'created_at' => 'datetime',17 'updated_at' => 'datetime',18 ];19 20 protected function afterMigrate(Blueprint $table)21 {22 $table->index('name');23 }24}
Comme dit plus haut, Sushi modifie votre modèle pour utiliser un array comme source de données, mais l'utilisation reste ensuite standard et cohérente avec les fonctionnalités d'Eloquent.
Il est donc tout à fait possible de créer des relations entre les modèles Eloquent standards et nos modèles surchargés avec Sushi.
Néanmoins, pour que votre solution soit pérenne, rappelez-vous que vos identifiants ne doivent pas changer pour identifier une même ligne ou encore ne pas être supprimé sauvagement.
Le typage de votre identifiant n'est pas forcément un entier (integer), et si ce n'en est pas un, il faudra le préciser dans le schéma du modèle ainsi que dans les propriétés $incrementing, $keyType et $primaryKey (je vous renvoie à la documentation des clefs primaires).
1<?php 2 3namespace App\Models; 4 5use Ramsey\Uuid\Uuid; 6use Illuminate\Database\Eloquent\Model; 7 8class CustomData extends Model 9{10 use \Sushi\Sushi;11 12 public $incrementing = false; 13 protected $keyType = 'string'; 14 protected $primaryKey = 'id'; 15 16 protected $schema = [17 'id' => 'string', 18 // ...19 ];20 21 public function getRows()22 {23 // ...24 25 while (($currentLine = fgetcsv($handle)) !== FALSE ) {26 27 $uniqId = Str::slug($currentLine[0] . $currentLine[1]); 28 29 $data[] = array_merge(30 [31 'id' => Uuid::uuid5(Uuid::NAMESPACE_DNS, $uniqId)->toString(), 32 ],33 $currentLine34 );35 }36 37 // ...38 }39}
Dans l'exemple ci-dessus, $currentLine[0] et $currentLine[1] sont deux identifiants disponibles dans le fichier CSV qui identifie de manière certaine une ligne du CSV.
Nous pourrions simplement les concaténer pour créer un identifiant unique, cependant si la longueur de ces chaines est variable, nous pourrions rencontrer des soucis lors de l'insertion des données.
Il est alors préférable de faire un hash de ces identifiants concaténés pour ne pas rencontrer ce genre de souci.
Fini de tailler la bavette, maintenant c’est vous le cuistot : à vos fourneaux !
Source : https://usesushi.dev/
A lire
Autres articles de la même catégorie
Utiliser un serveur MCP dans votre Laravel
Le MCP, standard de communication entre les assistants IA et vos données, va nous permettre d'ajouter aux réponses de nos agents IA des données fraîches et pertinentes.
Antoine Benevaut
Type-Safe de A à Z
Unifiez les types entre le backend et le frontend pour réduire les bugs et améliorer la cohérence de votre code
Rémy Guillermic
La validation d'emails
Décortiquons ensemble la validation des adresses email avec Laravel !
Rémy Guillermic