Spatie a annoncé la fin du support de son paquet spatie/data-transfer-object
et celui-ci c’est vu être immédiatement archivé.
Ce paquet, nous l’avions découvert au sein de cet article. Il est temps de passer en revue l’alternative qu’il recommande qui n’est autre que leur nouveau paquet spatie/laravel-data
.
Introduction
Ce paquet a pour vocation d’être utilisé dans 3 contextes différents :
- À la place des
Resource
pour les retours d’API - À la place des
FormRequest
pour la validation des données - Pour les définitions en TypeScript
Nous allons, dans cet article, explorer les 2 premiers points.
Commençons par le commencement, c’est-à-dire l’implémentation d’une classe. Rien de bien compliqué, notre classe doit étendre de la classe Data
et définir les propriétés typées au sein du constructeur :
1namespace App\Data; 2 3use Spatie\LaravelData\Data; 4 5class StudentData extends Data 6{ 7 public function __construct( 8 public int $id, 9 public string $name,10 ) {11 }12}
On peut d’ores et déjà jouer avec nos classes soit en instanciant un objet avec des paramètres nommés ou bien via la méthode from
en lui passant en paramètre un tableau :
1use App\Data\StudentData;2 3$boris = new StudentData(id: 42, name: 'Boris');4$jane = StudentData::from(['id' => 99, 'name' => 'Jane']);
Le casting des propriétés
Une des fonctionnalités appréciées dans le paquet DTO était le casting des propriétés via les attributs, on retrouve ici la même logique que l’on va illustrer avec une nouvelle classe BirthdateData
qui a pour particularité d’avoir une propriété de type DateTime
:
1namespace App\Data; 2 3use DateTime; 4use Spatie\LaravelData\Attributes\WithCast; 5use Spatie\LaravelData\Casts\DateTimeInterfaceCast; 6use Spatie\LaravelData\Data; 7 8class BirthdateData extends Data 9{10 public function __construct(11 #[WithCast(DateTimeInterfaceCast::class, format: 'Y-m-d')]12 public DateTime $date,13 ) {14 }15}
Alors, pourquoi la présence de l’attribut WithCast
? Tout simplement pour que classe BirthdateData
puisse convertir, lors de sa construction, une string
représentant une date en objet DateTime
, ainsi le code suivant va fonctionner :
1use \App\Data\BirthdateData;2 3$bornAt = BirthdateData::from(['date' => '1970-01-01']);
On complexifie un peu le tout en ajoutant une propriété bornAt
à notre classe StudentData
qui sera du type précédemment définit, c’est-à-dire BirthdateData
:
1namespace App\Data; 2 3use Spatie\LaravelData\Data; 4 5class StudentData extends Data 6{ 7 public function __construct( 8 public int $id, 9 public string $name,10 public BirthdateData $bornAt,11 ) {12 }13}
Lors de la construction de notre objet depuis un tableau, il est tout à fait possible de passer notre date en string
car celle-ci va être automatiquement convertie en DateTime
:
1$jane = \App\Data\StudentData::from([2 'id' => 99,3 'name' => 'Jane',4 'bornAt' => [5 'date' => '1970-01-01'6 ],7]);
À noter que nous pouvons définir nos propres types.
Utile pour les retours d’API
Nous allons maintenant aborder les utilisations de nos objets en commençant par les retours des méthodes de nos contrôleurs à envisager dans le cadre du développement d’une API.
Comme indiqué précédemment nos nouveaux objets Data
peuvent être rendu en lieu et place des Resource
de Laravel.
Ainsi, à partir d’un modèle passé en paramètre d’une méthode de notre contrôleur, nous pouvons produire notre objet Data
et leur rendre tel quel.
Pour l’exemple, admettons un modèle Student
en paramètre de la méthode show
de notre contrôleur :
1namespace App\Http\Controllers; 2 3use App\Data\StudentData; 4use App\Models\Student; 5 6class StudentController extends Controller 7{ 8 public function show(Student $model) 9 {10 return StudentData::from($model);11 }12}
Ce code va nous retourner un json
semblable à ceci :
1{2 "id": 99,3 "name": "Jane",4 "bornAt": {5 "date": "1970-01-01T16:48:29+01:00"6 }7}
Utile pour la validation de données
Un autre aspect mis en avant avec ce paquet est la possibilité de déléguer la définition des règles de validation à notre objet Data
en ajoutant des attributs aux propriétés comme ceci :
1class BirthdateData extends Data 2{ 3 public function __construct( 4 #[Required, Date] 5 #[WithCast(DateTimeInterfaceCast::class, format: 'Y-m-d')] 6 public DateTime $date, 7 ) { 8 } 9}10 11class StudentData extends Data12{13 public function __construct(14 #[Nullable]15 public ?int $id,16 #[Required, StringType]17 public string $name,18 #[Required, ArrayType]19 public BirthdateData $bornAt,20 ) {21 }22}
On remarque que la validation d’objet Data
imbriqué est possible, il faut cependant que notre formulaire respecte la structure des objets :
1<form action="/data" method="post">2 <input type="text" name="name"/></br>3 <input type="text" name="bornAt[date]"/></br> <!-- ICI -->4 <button type="submit">Envoyer</button>5</form>
Voilà, nous pouvons dorénavant utiliser notre objet StudentData
au sein de notre contrôleur en lieu et place des FormRequest
:
1public function store(StudentData $data)2{3 dd($data->all());4}
Conclusion
Ce paquet apporte de la consistance dans la manipulation de nos données au sein d’un projet Laravel, là où on peut considérer la définition des règles de validation et des ressources trop volatile.
Il reste plein d’aspect à explorer dans la documentation du paquet.
Ceci étant dit, pour une utilisation dans un contexte autre que celui de Laravel, celui-ci ne semble pas approprié. Ainsi, il faut peut-être privilégier une autre librairie ou une implémentation personnelle du DTO.
A lire
Autres articles de la même catégorie
Laravel Arkitect
Appliquez des règles d'architectures dans vos projets grace à Arkitect !
Mathieu De Gracia
PAN : L'Analytics PHP qui respecte la vie privée
L'outil simple et respectueux de la vie privée pour un suivi d'analytics minimaliste et efficace !
Laravel Jutsu
Découverte de NativePHP
Transformez votre Laravel en application desktop !
Mathieu De Gracia