Commentaires
Commençons à rentrer dans les codes. L'une des premières étapes que je te propose est de s'occuper des modèles et de la base de données.
Grâce à Laravel, aucune raison cependant de mettre les mains dans phpmyadmin ou dans des lignes de code SQL pour créer et peupler ses tables : ce sont les rôles des migrations et des seeders !

Les migrations sont des représentations, des schémas des tables en base de données.
Les seeders nous permettent de peupler notre base de données avec des données fictives. Cela nous permet de nous éviter d'inventer des faux utilisateurs à la main, des faux messages...

Une fois les migrations créées, on n'aura plus qu'à utiliser artisan pour créer nos tables en base de données automatiquement.

Les migrations


Les migrations se trouvent dans database > migrations (qui se lit : dans le dossier migrations lui-même dans le dossier database).

Arthur, l'apprenti développeurOula, j'ai déjà des fichiers dans ce dossier, c'est normal ?
Tout à fait ! Laravel propose déjà 3 migrations qui correspondent à des tables dont on a très souvent besoin dans chaque application :


  • Les utilisateurs : Laravel peut créer pour nous une table "users" qui contiendra nos utilisateurs. Nous la modifierons pour parfaire nos besoins

  • Les mots de passe oubliés : Laravel peut créer pour nous une table "password_resets" qui enregistrera les demandes de mots de passe oubliés

  • Les jobs : Laravel peut créer pour nous une table "jobs". Celle-ci est plus compliquée à comprendre, nous ne l'utiliserons pas. Elle sert dans le cas où nous souhaitons réaliser de manière différée des opérations couteuses en temps. Super pratique dans le cas d'envoi d'emails de masse par exemple ou de traitement d'image : les jobs vont permettre de gérer ces tâches sans bloquer l'utilisateur


Avant de nous attaquer à la création de nos migrations, réfléchissons à nos besoins :
Nous allons réaliser un blog où les utilisateurs devront être inscrits par l'administrateur pour pouvoir poster des articles. Cependant, tout le monde pourra poster des commentaires et les administrateurs pourront les gérer.

Allez, imaginons les attributs de chaque entité que nous allons gérer :

Les utilisateurs :


  • id : l'identifiant unique de l'utilisateur

  • name : le nom et prénom de l'utilisateur

  • email : l'email de l'utilisateur

  • password : le mot de passe de l'utilisateur


Les posts :


  • id : l'identifiant unique du post

  • title : le titre du post

  • user_id : l'identifiant unique du user qui a posté l'article

  • content : le contenu du post

  • created_at : la date de création du post

  • updated_at : la date de modification du post


Les commentaires :


  • id : l'identifiant unique du commentaire

  • post_id : l'identifiant unique du post auquel appartient le commentaire

  • title : le titre du commentaire

  • author : l'auteur du commentaire (un pseudo)

  • content : le contenu du commentaire

  • reported : booléen pour savoir si le commentaire a été signalé

  • created_at : la date de création du commentaire

  • updated_at : la date de modification du commentaire


Arthur, l'apprenti développeurFastoche !
En effet, rien ne change par rapport à d'habitude. Petite précision cependant : étant donné que nous n'avons que 3 entités, on peut se permettre sans mal de faire un listing comme au dessus. Dans des projets plus avancés, il est essentiel de réaliser une analyse préalable grâce à des outils de modélisation comme UML pour concevoir les classes ou MERISE (Français !) pour concevoir des bases de données. Un cours sur le sujet est (ou sera bientôt !) disponible sur le site. Tu peux aussi t'aider d'outils comme Laravel Schema Designer qui te permettent de renseigner tes entités graphiquement puis de générer directement le code correspondant (attention, Laravel Schema Designer est un outil indépendant, je ne garantis rien !).

Arthur, l'apprenti développeurC'est noté, merci pour ces précisions ;)
Passons donc maintenant aux migrations directement. Nous allons débuter par la modification de la migrations associées aux users, qui actuellement, contient ce code :

{"language":"application/x-httpd-php","content":"<?php\n\nuse Illuminate\\Database\\Migrations\\Migration;\nuse Illuminate\\Database\\Schema\\Blueprint;\nuse Illuminate\\Support\\Facades\\Schema;\n\nclass CreateUsersTable extends Migration\n{\n /**\n * Run the migrations.\n *\n * @return void\n */\n public function up()\n {\n Schema::create('users', function (Blueprint $table) {\n $table->id();\n $table->string('name');\n $table->string('email')->unique();\n $table->timestamp('email_verified_at')->nullable();\n $table->string('password');\n $table->rememberToken();\n $table->timestamps();\n });\n }\n\n /**\n * Reverse the migrations.\n *\n * @return void\n */\n public function down()\n {\n Schema::dropIfExists('users');\n }\n}","filename":"2014_10_12_000000_create_users_table.php"}

On peut remarquer deux méthodes : up() et down(). La première sert à dire quoi faire pour créer la table et l'autre pour la détruire.
C'est à dire que c'est dans la méthode up() qu'on va indiquer les colonnes (ou champs) qu'on va créer dans notre table "users".

Arthur, l'apprenti développeurEt c'est quoi la classe Schema ?
C'est justement cette classe qui nous offre les méthodes permettant de créer les colonnes de notre table avec des fonctions comme string, boolean, enum, text... qui représentent les types en base de données.

Dans notre table users, on aura donc :
- Un champ id, de type bigInteger, auto-incrémenté. C'est une clé primaire
- Un champ name, varchar
- Un champ email, varchar, unique
- Un champ password, varchar


Arthur, l'apprenti développeurMais attends, il y a aussi d'autres lignes comme $table->rememberToken(); par exemple. À quoi servent-elles ?
Bien vu ! En fait, il s'agit de méthodes utiles pour l'authentification très souvent :
- rememberToken() crée un varchar nullable remember_token qui stockera un identifiant de connexion
- timestamp('email_verified_at') crée un champ de type timestamp qui stockera la date à laquelle l'adresse email a été vérifiée (ce n'est pas obligatoire, c'est juste par défaut disponible).

Il y a aussi la méthode timestamps(), qui crée automatiquement deux champs : created_at et updated_at.

Cette table correspond donc à ce que nous voulions, on peut simplement enlever le email_verified_at si on le souhaite.

Passons à la table posts. Pour cela, nous allons utiliser une nouvelle fois artisan pour que cet utilitaire crée le fichier de migration :

{"language":"shell","content":"php artisan make:migration create_posts_table","filename":"console"}

Si l'on retourne maintenant dans notre dossier des migrations, on a un nouveau fichier nommé date_create_posts_table.php qui contient déjà la base de notre fichier : les "use" utiles ainsi que la déclaration de la classe et les méthodes up et down.
Il ne nous reste plus qu'à compléter la méthode up() qui est l'équivalent grossier d'un CREATE TABLE AS en SQL.

{"language":"application/x-httpd-php","content":"<?php\n\nuse Illuminate\\Database\\Migrations\\Migration;\nuse Illuminate\\Database\\Schema\\Blueprint;\nuse Illuminate\\Support\\Facades\\Schema;\n\nclass CreatePostsTable extends Migration\n{\n /**\n * Run the migrations.\n *\n * @return void\n */\n public function up()\n {\n Schema::create('posts', function (Blueprint $table) {\n $table->id();\n $table->string('title');\n $table->text('content');\n $table->timestamps();\n });\n }\n\t...\n}\n","filename":"2020_10_07_092924_create_posts_table.php"}

Arthur, l'apprenti développeurMais attends, il ne manque pas user_id ?
En effet. Mais là, c'est un peu particulier, car c'est une clé étrangère !
Nous allons donc utiliser une nouvelle méthode : foreignId()

{"language":"application/x-httpd-php","content":"<?php\nSchema::create('posts', function (Blueprint $table) {\n $table->id();\n $table->string('title');\n $table->text('content');\n $table->foreignId('user_id')->constrained();\n $table->timestamps();\n });","filename":"2020_10_07_092924_create_posts_table.php"}


On continue avec la table comments :

{"language":"shell","content":"php artisan make:migration create_comments_table","filename":"console"}

{"language":"application/x-httpd-php","content":"<?php\n\nuse Illuminate\\Database\\Migrations\\Migration;\nuse Illuminate\\Database\\Schema\\Blueprint;\nuse Illuminate\\Support\\Facades\\Schema;\n\nclass CreateCommentsTable extends Migration\n{\n /**\n * Run the migrations.\n *\n * @return void\n */\n public function up()\n {\n Schema::create('comments', function (Blueprint $table) {\n $table->id();\n $table->string('title');\n $table->string('author');\n $table->text('content');\n $table->foreignId('post_id')->constrained();\n $table->boolean('reported')->default(false);\n $table->timestamps();\n });\n }\n\n /**\n * Reverse the migrations.\n *\n * @return void\n */\n public function down()\n {\n Schema::dropIfExists('comments');\n }\n}","filename":"2020_10_07_094212_create_comments_table.php"}

Et voilà ! Nos migrations sont créées ! Nous n'avons plus qu'à demander à artisan de créer les tables dans notre base de données. Pour ça, rien de plus simple :

{"language":"shell","content":"php artisan migrate","filename":"console"}

C'est gagné ! Tu peux aller directement regarder dans la base de données qui a été créé si tu es curieux ;)

Arthur, l'apprenti développeurMince, j'ai une erreur Illuminate\Database\QueryException SQLSTATE[HY000] [2002] Connection refused...
Cela signifie que les données de connexion que tu as renseignées dans le .env ne sont pas correctes, il faut que tu les vérifies ;).


Les factories et les seedings


Passons maintenant au peuplement de notre base de données. Comme expliqué au début, on peut générer des données fictives pour tester notre application sans s'embêter grâce aux factories et aux seedings.

Comme les migrations, nous allons aller dans le dossier database pour cette partie.
Puis, dans factories. Un premier fichier est déjà là pour les Users, regardons son contenu.

{"language":"application/x-httpd-php","content":"<?php\n\nnamespace Database\\Factories;\n\nuse App\\Models\\User;\nuse Illuminate\\Database\\Eloquent\\Factories\\Factory;\nuse Illuminate\\Support\\Str;\n\nclass UserFactory extends Factory\n{\n /**\n * The name of the factory's corresponding model.\n *\n * @var string\n */\n protected $model = User::class;\n\n /**\n * Define the model's default state.\n *\n * @return array\n */\n public function definition()\n {\n return [\n 'name' => $this->faker->name,\n 'email' => $this->faker->unique()->safeEmail,\n 'email_verified_at' => now(),\n 'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password\n 'remember_token' => Str::random(10),\n ];\n }\n}","filename":"UserFactory.php"}

Ce fichier, comme son nom l'indique, est un Factory. Il permet de création un utilisateur bidon en base de données grâce à Faker : https://github.com/fzaninotto/Faker (attention, depuis le 21 octobre 2020, le créateur a arrêté le son développement. Je ne sais pas actuellement ce qui va le remplacer dans Laravel. Cela n'empêche cependant pas son bon fonctionnement).

Étudions rapidement ce fichier.
D'abord, on voit qu'un attribut model est défini et pointe vers une classe User. Cette classe User est le modèle (l'entité) associée à ce Factory.

Arthur, l'apprenti développeurMais, on n'a toujours pas réalisé nos entités ?
En effet, nous les créerons juste après. Cependant, Laravel intègre par défaut une entité User prête à l'emploi.

Ensuite, on a une méthode définition qui permet d'indiquer quelles valeurs renseignées aux colonnes de notre table users. Faker permet de générer des string aléatoires, des nombres aléatoires...
En bref, ce Factory est déjà tout prêt ! Si vous voulez en savoir +, n'hésitez pas [Créer des factories].


Arthur, l'apprenti développeurMais, comment sait-on qu'un utilisateur est administrateur ?
Excellente question ! En effet, dans notre base de données, aucune colonne ne nous permet de savoir si quelqu'un est administrateur. C'est un oubli (volontaire) de ma part pour te montrer comment rajouter/modifier/supprimer une colonne quand on a déjà réalisé les migrations

Il faut donc créer une nouvelle migration. Ne surtout pas modifier l'ancienne car elle ne sera pas prise en compte si on fait php artisan:migrate, à moins de tout supprimer et de tout recommencer...
La solution : php artisan make:migration add_admin_column_to_users_table.
Son contenu sera le suivant :

{"language":"application/x-httpd-php","content":"<?php\n\nuse Illuminate\\Database\\Migrations\\Migration;\nuse Illuminate\\Database\\Schema\\Blueprint;\nuse Illuminate\\Support\\Facades\\Schema;\n\nclass AddAdminColumnToUsersTable extends Migration\n{\n /**\n * Run the migrations.\n *\n * @return void\n */\n public function up()\n {\n Schema::table('users', function (Blueprint $table) {\n $table->boolean('admin')->default(false);\n });\n }\n\n /**\n * Reverse the migrations.\n *\n * @return void\n */\n public function down()\n {\n }\n}\n","filename":"Migration addAdminColumnToUsersTable"}

Excellent. Et maintenant, il nous suffit de faire php artisan migrate une nouvelle fois et le tour est joué ;)

Alors maintenant nous pouvons modifier notre fichier UserFactory pour intégrer le fait que nous voulons un utilisateur administrateur.

{"language":"application/x-httpd-php","content":"<?php\n\nnamespace Database\\Factories;\n\nuse App\\Models\\User;\nuse Illuminate\\Database\\Eloquent\\Factories\\Factory;\nuse Illuminate\\Support\\Str;\n\nclass UserFactory extends Factory\n{\n /**\n * The name of the factory's corresponding model.\n *\n * @var string\n */\n protected $model = User::class;\n\n /**\n * Define the model's default state.\n *\n * @return array\n */\n public function definition()\n {\n return [\n 'name' => $this->faker->name,\n 'email' => $this->faker->unique()->safeEmail,\n 'admin'=>true,\n 'email_verified_at' => now(),\n 'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password\n 'remember_token' => Str::random(10),\n ];\n }\n}\n","filename":"UserFactory.php"}


Arthur, l'apprenti développeurAttends attends, c'est quoi tout ce truc pour le password ?
Comme tu dois le savoir, on est tenu en Europe (et on est tenu dans le monde entier pour des mesures de sécurité) de stocker les mots de passe de manière chiffrée ou hashée. Le password que nous avons renseigné là est le résultat du mot "password" hashé avec la méthode de PHP "password_hash" et l'algorithme BCRYPT. Du coup, quand on voudra essayer de se connecter, n'oublions pas que le mot de passe est tout simplement "password" ;).

Arthur, l'apprenti développeurOk je comprends mieux !
Attaquons-nous maintenant au seeding pour finir avec cette partie. Le seeding nous servira simplement à faire tourner notre UserFactory pour insérer en base de données.
Rendez-vous dans seeders pour trouver DatabaseSeeder et y insérer ce code, explicite :

{"language":"application/x-httpd-php","content":"<?php\n\nnamespace Database\\Seeders;\n\nuse Illuminate\\Database\\Seeder;\n\nclass DatabaseSeeder extends Seeder\n{\n /**\n * Seed the application's database.\n *\n * @return void\n */\n public function run()\n {\n \\App\\Models\\User::factory(1)->create();\n }\n}\n","filename":"DatabaseSeeder.php"}

Et enfin, dans notre terminal...

{"language":"shell","content":"php artisan db:seed","filename":""}

Et voilà ! Tu peux regarder le contenu de ta base de données pour vérifier qu'un utilisateur a bien été ajouté.

Cette partie était longue mais importante. Nous avons fait une belle introduction des possibilités offertes par Laravel pour interagir avec la base de données lors de sa création.



Attaquons-nous maintenant aux entités, une partie beaucoup plus courte. J'ai terminé cette partie
Demander de l'assistance