Commentaires

Introduction


Laravel possède son propre ORM (Object-relational mapping), qui permet de gérer sa base de données de manière "orientée objet". Chaque action réalisée sur la base de données (select, insert...) se fait via l'appel d'une méthode (ou plusieurs) que propose Eloquent, l'ORM de Laravel.
Pour que cela fonctionne sans embuche, il nous faut créer des modèles. Chaque table en base de données à son modèle correspondant (à quelques exceptions près, qu'il est inutile de citer dans ce tutoriel introductif). Ce modèle va permettre à Laravel de savoir comment gérer les données de la table.

Le modèle User


Depuis Laravel 8, les modèles sont stockées dans le dossier app -> Models.

Arthur, l'apprenti développeurJ'ai déjà un fichier, User.php
Tout à fait. Laravel propose de base un modèle pour les utilisateurs déjà tout prêt. Voyons son contenu et déchiffrons tout ça.

{"language":"application/x-httpd-php","content":"<?php\n\nnamespace App\\Models;\n\nuse Illuminate\\Contracts\\Auth\\MustVerifyEmail;\nuse Illuminate\\Database\\Eloquent\\Factories\\HasFactory;\nuse Illuminate\\Foundation\\Auth\\User as Authenticatable;\nuse Illuminate\\Notifications\\Notifiable;\n\nclass User extends Authenticatable\n{\n use HasFactory, Notifiable;\n\n /**\n * The attributes that are mass assignable.\n *\n * @var array\n */\n protected $fillable = [\n 'name',\n 'email',\n 'password',\n ];\n\n /**\n * The attributes that should be hidden for arrays.\n *\n * @var array\n */\n protected $hidden = [\n 'password',\n 'remember_token',\n ];\n\n /**\n * The attributes that should be cast to native types.\n *\n * @var array\n */\n protected $casts = [\n 'email_verified_at' => 'datetime',\n ];\n}\n","filename":"User.php"}

Arthur, l'apprenti développeurJ'ai déjà manipulé des modèles avec le schéma MVC, mais là ce fichier ne ressemble à rien de ce que j'ai pu manipuler auparavant...
Il est certain que Laravel diffère de la méthode "conventionnelle" pour ses modèles. Cependant, après quelques explications, tout te paraitra plus clair.

On va décrypter ce fichier de haut en bas.

{"language":"application/x-httpd-php","content":"<?php\n\nnamespace App\\Models;\n\nuse Illuminate\\Contracts\\Auth\\MustVerifyEmail;\nuse Illuminate\\Database\\Eloquent\\Factories\\HasFactory;\nuse Illuminate\\Foundation\\Auth\\User as Authenticatable;\nuse Illuminate\\Notifications\\Notifiable;","filename":"User.php"}

Dans un premier temps, on définit le namespace.
Ensuite, on va utiliser plusieurs classes prédéfinies par Laravel. Celles-ci servent à l'authentification, l'utilisation des factories ou les notifications (une fonctionnalité propre à Laravel).


{"language":"application/x-httpd-php","content":"<?php\n\n \t/**\n * The attributes that are mass assignable.\n *\n * @var array\n */\n protected $fillable = [\n 'name',\n 'email',\n 'password',\n ];","filename":"User.php"}

Nous voyons ensuite ce premier attribut protégé nommé fillable. Il est extrêmement important.
Cet attribut est lié à la sécurité de notre application. Dans ce tableau, nous indiquons les colonnes de la table qui peuvent être modifiées lors d'une insertion ou un update en base après soumission d'un formulaire.
Par exemple, si nous créons un nouvel utilisateur, il faut se poser la question "Quelles colonnes vais-je modifier ? "
La réponse, dans le cas de notre blog, est la suivante :
- Son nom
- Son email
- Son mot de passe
Mais par contre, on ne va jamais modifier le booléen "admin", le token d'authentification... Autrement dit, un utilisateur lambda ne doit pas pouvoir modifier les colonnes autres que name, email et password lorsqu'il soumet un formulaire. Cela permet de se protéger dans le cas où une personne malicieuse modifierait les informations soumises à un formulaire en rajoutant par exemple un champ "admin" qu'il passerait à true. Car dans Laravel, pour insérer des données dans une base de données, on peut simplement demander :
"Fais une insertion dans la table users avec tous les paramètres de la requête POST". Il faut donc pouvoir trier les paramètres souhaités et ceux non souhaités. Fillable sert à ça ;).

{"language":"application/x-httpd-php","content":"<?php \n\t/**\n * The attributes that should be hidden for arrays.\n *\n * @var array\n */\n protected $hidden = [\n 'password',\n 'remember_token',\n ];","filename":"User.php"}

À l'inverse de fillable qui sert lors de requêtes d'insertion ou d'update en base de données, $hidden va servir lors d'un select.
Ce sont ici les colonnes que l'on ne souhaite pas récupérer lorsqu'on fait un select * from users;

Nous voyons ensuite un attribut nommé $casts. Cet attribut sert lors de conversions de type, notamment pour les dates, de sorte que nous puissions utiliser Carbon (une classe de gestion des dates) par exemple.

Et voilà, le décryptage est fini.
Arthur, l'apprenti développeurMais du coup, a-t-on des éléments à modifier ? Je crois que tout est déjà correct ?
La réponse est oui, tout est déjà parfait comme ça !

Arthur, l'apprenti développeurJ'ai une autre question. Il y a en effet quelque chose que je ne comprends pas... Comment Laravel fait pour savoir à quelle table est lié notre modèle ?
En fait, il se base exclusivement sur le nom de la classe si l'attribut $table n'est pas défini.
Quand la classe a le nom User, il va considérer que la table associée est users. Si ce n'est pas le cas, il nous suffit alors de rajouter dans notre modèle protected $table = 'users';

Le modèle Post


Pour créer un modèle, une syntaxe doit être respectée : la définition du namespace, l'utilisation de classes natives de Laravel etc. Pour nous simplifier la vie, on peut donc utiliser une commande artisan !
{"language":"shell","content":"php artisan make:model Post","filename":"terminal"}


Si on consulte notre dossier Models, ce fichier est alors apparu :
{"language":"application/x-httpd-php","content":"<?php\n\nnamespace App\\Models;\n\nuse Illuminate\\Database\\Eloquent\\Factories\\HasFactory;\nuse Illuminate\\Database\\Eloquent\\Model;\n\nclass Post extends Model\n{\n use HasFactory;\n}\n","filename":"Post.php"}


En premier lieu, il nous faut renseigner l'attribut $fillable. Une idée ?

{"language":"application/x-httpd-php","content":"<?php\n\nnamespace App\\Models;\n\nuse Illuminate\\Database\\Eloquent\\Factories\\HasFactory;\nuse Illuminate\\Database\\Eloquent\\Model;\n\nclass Post extends Model\n{\n use HasFactory;\n\n protected $fillable = [\n \t'title',\n \t'content'\n ];\n\n}\n","filename":"Post.php"}


A-t-on besoin de $hidden ?
Arthur, l'apprenti développeurNon, toutes les données nous seront utiles quand nous ferons un select * from posts.
Exact !
En réalité, notre modèle est terminé. Enfin presque... Nous allons voir ici un outil super puissant offert par Laravel.
Les relations entre modèles.
Comment récupérer tous les posts d'un user ? Comment récupérer les données d'un utilisateur qui a créé un post ?
Les relations nous permettent de palier à ces problèmes. Il n'est pas question là de faire l'équivalent grossier de :
$post = select * from posts where id = 1;
$user = select * from user where id = $post->user_id

Non, il est question ici de se simplifier la vie en faisant $post->user, tout simplement.
Pour cela, nous devons clairement établir quelle type de relation il existe entre nos tables.
Si tu as fait un peu d'UML (ou de Merise), il s'agit en fait des cardinalités. Autrement dit :
Un post appartient-il à un utilisateur ? A plusieurs ?
Un utilisateur a-t-il un post ? Plusieurs ?
En fonction de ces réponses, nous avons le type de relation :)

Dans notre cas, un post appartient à un seul utilisateur.
Dans Laravel, cela se traduira par l'utilisation de belongsTo.

{"language":"application/x-httpd-php","content":"<?php\n\nnamespace App\\Models;\n\nuse Illuminate\\Database\\Eloquent\\Factories\\HasFactory;\nuse Illuminate\\Database\\Eloquent\\Model;\n\nclass Post extends Model\n{\n use HasFactory;\n\n protected $fillable = [\n \t'title',\n \t'content'\n ];\n\n\n public function user() \n {\n \treturn $this->belongsTo(User::class, 'user_id');\n }\n}\n","filename":"Post.php"}

Le premier paramètre de la méthode belongsTo est le chemin vers la classe associée à la relation, le second est le nom de la foreign_key dans notre table.

Arthur, l'apprenti développeurC'est super pratique ça ! Ne devrions-nous pas faire la même chose sur le modèle User ?
Excellente idée. As-tu une idée du type de relation que nous devrons utiliser ?

Arthur, l'apprenti développeurUn utilisateur peut créer plusieurs posts, donc je dirais une relation qui s'apparente à 1:n ?
Tout juste, encore une fois. Dans Laravel, cela se traduira par l'utilisation de hasMany

{"language":"application/x-httpd-php","content":"<?php\n\nnamespace App\\Models;\n\nuse Illuminate\\Contracts\\Auth\\MustVerifyEmail;\nuse Illuminate\\Database\\Eloquent\\Factories\\HasFactory;\nuse Illuminate\\Foundation\\Auth\\User as Authenticatable;\nuse App\\Enums\\UserRole;\n\nclass User extends Authenticatable\n{\n use HasFactory;\n\n /**\n * The attributes that are mass assignable.\n *\n * @var array<int, string>\n */\n protected $fillable = [\n 'name',\n 'email',\n 'password',\n ];\n\n /**\n * The attributes that should be hidden for serialization.\n *\n * @var array<int, string>\n */\n protected $hidden = [\n 'password',\n 'remember_token',\n ];\n\n /**\n * The attributes that should be cast.\n *\n * @var array<string, string>\n */\n protected $casts = [\n 'role'=>UserRole::class,\n ];\n\n public function posts()\n {\n return $this->hasMany(Post::class);\n }\n}\n","filename":"User.php"}

Arthur, l'apprenti développeurJe crois que tu as oublié de préciser le nom de la foreign_key en second paramètre.
C'est vrai ! Enfin, presque vrai... Encore une fois, Laravel nous simplifie la vie. En réalité, si les foreign Keys ont des noms "communs", c'est à dire le nom de la table suivi d'un underscore (_) suivi de "id", alors pas besoin de renseigner. Autrement dit, user_id étant un nom "commun" de foreign key, pas besoin de renseigner. Et ça ne marche pas que sur le modèle User, on aurait aussi pu s'en passer sur le modèle Post ! Pratique non ?

Le modèle Comment


Comme d'habitude, commençons par artisan :
{"language":"shell","content":"php artisan make:model Comment","filename":"artisan"}


Ensuite, on va remplir $fillable et on va faire le lien entre les posts et les commentaires. Un commentaire appartient à un post, c'est donc du belongsTo

{"language":"application/x-httpd-php","content":"<?php\n\nnamespace App\\Models;\n\nuse Illuminate\\Database\\Eloquent\\Factories\\HasFactory;\nuse Illuminate\\Database\\Eloquent\\Model;\n\nclass Comment extends Model\n{\n use HasFactory;\n\n protected $fillable = [\n \t'title',\n \t'author',\n \t'content',\n \t'reported'\n ];\n\n public function post() \n {\n \treturn $this->belongsTo(Post::class);\n }\n}\n","filename":"Comment.php"}


Et du coup, on rajoute dans le modèle Post la relation hasMany.

{"language":"application/x-httpd-php","content":"<?php\n\nnamespace App\\Models;\n\nuse Illuminate\\Database\\Eloquent\\Factories\\HasFactory;\nuse Illuminate\\Database\\Eloquent\\Model;\n\nclass Post extends Model\n{\n // ...\n\t\n\tpublic function comments()\n {\n \treturn $this->hasMany(Comment::class);\n }\n}\n","filename":"Post.php"}


On a terminé cette partie ! Encore bravo d'être arrivé jusqu'au bout. On a donc fini avec nos modèles.



Arthur, l'apprenti développeurOn passe à la suite ?
Oui et on va maintenant attaquer l'authentification. De ce fait, on pourra ensuite commencer nos vues, nos contrôleurs ! J'ai terminé cette partie
Demander de l'assistance