Laravel – Eloquent

Auteur(s) de l'article

À la suite de mon article sur Livewire, j'ai décidé d'attaquer un plan plus important du Framework, à savoir l'ORM Eloquent.

Préambule

Histoire d'être tous à la même page, je trouve utile de résumer afin de commencer sur de bonnes bases.
Laravel, comme la plupart des framework utilise un ORM (object-relational mapper).
Facilement comparable à Doctrine utilisé par Symfony, ces deux se distinguent de-part leurs fondement.
L'un est un Active Record tandis que l'autre est un Data Mapper.
Comparons les deux. Un modèle Eloquent est une représentation de la base de donnée et comprends également les opérations (save, update, delete, ...) dans cette dernière.
Un exemple d'insertion avec Eloquent.
Tandis que sur Doctrine, les choses sont un peu différentes.
Ce dernier s'appuie sur un EM (Entity Manager) pour executer ses actions et ses models sont une représentation de leurs tables respectives en base de données.
Un exemple avec Doctrine.

Eloquent dites-vous ?

Pour ne pas s'éparpiller sur ce sujet aussi vaste que Eloquent, le plus simple serait de s'attacher premièrement aux bases et par la suite (peut-être un second article ?) de détailler divers uses-cases et patterns.

Les "nice to have"

Carbon!

Laravel arrive avec Carbon qui est une API simple d'utilisation qui implémente et étends DateTime de PHP !

Ce qui permet à nos éléments stockés en dateTimeTz par exemple, d'être directement transformés en CarbonInterface.

Vous pouvez choisir de votre côté comment votre champ doit être transformé en re-déclarant votre propriété du modèle :
$casts = ['votre_champ' => 'bool'];

Si vous souhaitez en savoir plus sur Carbon, voici le lien de sa documentation.

Les protections

De part son architecture, il arrive qu'on passe directement la $request pour en créer un record en DB. Eloquent arrive avec une solution appelée la "Mass Assignement Protection" et nous permets de spécifier les champs que l'on veut pouvoir remplir sans avoir à tout ré-écrire (parfois un gain de temps considérable).

Nous pouvons donc déclarer les champs directement dans la propriété du modèle $fillable = ['field', ...];.

Par défaut, tous les champs sont $guarded = ['*']; donc il est important de déclarer les champs à cet effet.
Le but étant d'éviter que certains champs soient accessibles trop facilement comme 'password' ou 'roles' dans l'idée.

Les scopes

Si dans votre usage vous remarquez que vous utilisez souvent les mêmes "filtres", il serait peut-être temps de les localiser dans votre modèle.
Laravel appelle ça un "Local Scope". Pour en créer un, c'est tout simple, il suffit d'écrire la méthode avec comme préfix scope et il sera automatiquement utilisable.

Pour l'utiliser:
User::popular()->orderBy('xyz')->get();

L'appel du scope ne doit pas inclure le préfix.

Un scope peut être dynamique et comporter un ou plusieurs paramètres.
Un exemple de scope local.

La magie de Laravel ✨

Laravel garde en tête sa simplicité d'utilisation et propose des solutions out-of-the-box sans pour autant nous avertir.

Un exemple plus concret est l'utilisation des méthodes dites magiques comme l'exemple plus haut avec un scope déclaré par exemple, mais également des méthodes plus directes à notre model.

Je m'explique, si nous avons un champ vote sur notre table, on peux très bien utiliser une méthode (invisible par notre IDE) Model::whereVote('xzy');
Laravel va passer par la méthode magique de PHP __call ou __callStatic et fournir des outils à notre disposition.

D'ailleurs, pour les utilisateurs de PHPStorm, je vous recommande vivement ce plugin.

Tinker !

Laravel Tinker est un petit outil très facile à installer comme-ceci.
Il permet d'intéragir avec la base de donnée en simple ligne de commandes.
Un exemple de commande pour intéragir avec l'application.

Les relations

Un sujet vaste et assez complexe pour en faire simplement un paragraphe.
Eloquent propose plusieurs niveau de relations entre OneToOne jusqu'à ManyToMany en passant par les jointures à travers d'autres tables.
La force de ces méhtodes résident dans le fait que les relations vont être "devinées" en fonction du nom de la clé primaire de la table (en général id) et du nom de la class qui la définit.
Dans l'exemple plus haut j'ai un modèle Flight qui référence un autre modèle FlightTime.

La table flights_times a une colonne appellée flight_id, comme défini dans le fichier de migration.
Il me sufit donc de créer une méthode:
/**
     * @return \Illuminate\Database\Eloquent\Relations\HasOne
     */
    public function times(): HasOne
    {
        return $this->hasOne(FlightTime::class);
    }
pour que la relation se fasse automatiquement !
L'appel à la méthode de relation (avec la parenthèse) permet d'obtenir le QueryBuilder tandis que l'appel sans les parenthèses permet d'avoir le modèle directement résolu !

Conclusion

Comme Doctrine, Eloquent reste à ce jour une référence.

Directement intégré à l'écosysème de Laravel, ce dernier se distingue par sa simplicité, de ses méthodes magiques et de la rapidité à écrire une requête en base de données via très peu de lignes de code.
Le choix sera fait par le choix du framework, prennez juste en compte que Doctrine et Eloquent sont très différents ainsi que la courbe d'apprentissage n'en sera pas la même !
(Doctrine tends à être plus complexe)
Dans mon cas, j'apprécie beaucoup Doctrine pour les propriétés visibles sur l'entité, les getters et setters, on sait ce qui s'y trouve.

Eloquent fonctionne differement, les propriétés de notre modèle ne reflète pas forcément les champs, donc beaucoup d'attentes sur les méthodes magiques, ce qui peut rendre la chose plus difficile si on connaît mal le projet. Néamoins le gain de temps reste indégniable.
Merci de m'avoir lu !