Commentaires
Bravo ! Bravo ! Et encore bravo ! Tu es arrivé au bout de ce cours pratique qui t'a permis de voir les bases de Laravel. J'ai toujours trouvé qu'on apprenait mieux en pratiquant qu'en mangeant de la théorie à ne plus pouvoir en avaler !

Arthur, l'apprenti développeurJe suis d'accord. Je pense avoir vraiment pris en compétences sur ce cours grâce à la pratique. Il y a certains points qu'il faudra approfondir ou revoir, mais je crois avoir cerné le concept du framework et ses bases. Hâte de progresser dessus encore et encore, il me semble qu'il y a plein de choses intéressantes à faire avec ce framework !
Oula oui ! Tu peux tout faire... Des systèmes de paiement, des API, des sites e-commerce, générer des factures, faire du temps réel, envoyer des emails de masse, gérer un stock, faire un système de gestion complet.... Pour ma part, j'ai déjà fait un site de gestion de revenus, de staff, des sites vitrines, des sites e-commerces multilingues, un essai de site avec une messagerie en temps réel (pas très concluant...)...

Arthur, l'apprenti développeurAh oui, même pour des sites vitrines tu utilises Laravel ? Ce n'est pas "trop" ?
En fait, ça dépend. Si c'est une simple page HTML/CSS, c'est sûr que c'est trop. Mais s'il y a un formulaire de contact, ou que l'on sait pas trop si le site peut évoluer vers un site multilingue, ou n'importe quoi qui pourrait donc nécessiter Laravel, il ne faut pas hésiter à l'utiliser. Même si c'est très minimaliste et qu'on affiche des vues en passant par les routes directement. C'est souvent plus simple de partir sur du Laravel dès le début que de l'intégrer après !

Arthur, l'apprenti développeurOui je comprends. Cet aperçu de Laravel m'a beaucoup plu. J'aimerais bien arriver à faire des sites e-commerce avec...
Il va falloir encore beaucoup d'apprentissage ! Sur les collections, l'utilisation de Laravel Cashier par exemple, sur l'authentification, les autorisations, les requests, les politiques de sécurité... Mais tu peux déjà faire beaucoup de choses avec ce qu'on a vu. D'ailleurs, améliorons rapidement notre projet.

Le retrait des routes inutilisées


Tu l'auras peut être remarqué, on a créé des routes qui ne sont pas utilisées. Notamment avec les ressources et les commentaires. Par exemple, on ne fait jamais appel à la route create. On va donc enlever tout ça en utilisant except avec les ressources :

{"language":"application/x-httpd-php","content":"<?php\n//..\nRoute::resource('comments', CommentsController::class)->except(['create']);\n//..\n","filename":"routes/web.php"}

Et évidemment, Il faut supprimer le code de la méthode create dans le contrôleur des commentaires.
À la place d'except, on aurait pu utiliser only :

{"language":"application/x-httpd-php","content":"<?php\n//..\nRoute::resource('comments', CommentsController::class)->only(['index', 'show', 'edit', 'update', 'store', 'destroy']);\n//..\n","filename":"routes/web.php"}


Le signalement des commentaires


On avait tout fait pour mais on l'a oublié durant nos vues !

Arthur, l'apprenti développeurOui j'osais pas te le dire...
Alors, voici ce que je te propose :
{"language":"text/html","content":"@extends('layouts.front')\n@section('title', 'Lire l\\'article '.$post->title)\n@section('content')\n\t<section class=\"mx-auto py-2 my-6 border-b border-gray-400\">\n\t <h1 class=\"text-3xl text-blue-700 my-8\">{{ $post->title}}</h1>\n\t <p>{{ $post->content }}</p>\n\t</section>\n\t<h2 class=\"text-2xl text-blue-500\">Les commentaires</h2>\n\t@forelse($post->comments as $comment)\n\t\t<section class=\"mx-auto py-2 my-6 border-b border-gray-400\">\n\t\t <h3 class=\"text-xl text-blue-400 my-8\">{{ $comment->title }} par {{ $comment->author }}</h3>\n\t\t @if($comment->reported)\n\t\t \t<p>Le message a été signalé</p>\n\t\t @else\n\t\t \t<p>{{ $comment->content }}</p>\n\t\t \t<a class=\"text-red-400\" href=\"{{ route('comments.report', $comment) }}\">Signaler le commentaire</a>\n\t\t @endif\n\t\t @if(Auth::check() and Auth::user()->admin)\n\t\t \t<a href=\"{{ route('comments.edit', $comment) }}\">Editer le commentaire</a>\n\t\t @endif\n\t\t</section>\n\t@empty\n\t\t<p>Soyez le premier à laisser un commentaire ! </p>\n\t@endforelse\n <form action=\"{{ route('comments.store') }}\" method=\"POST\" class=\"my-6 sm:w-1/2 mx-auto\">\n \t<h2>Laisser un commentaire</h2>\n \t @if(session('success'))\n <div class=\"text-xl text-green-400\">\n {{ session('success') }}\n </div>\n @endif\n @csrf\n <input type=\"hidden\" name=\"post_id\" value=\"{{ $post->id }}\">\n <div class=\"px-4 py-5 bg-white sm:p-6\">\n \t<div class=\"py-2\">\n <label for=\"author\" class=\"block text-sm font-medium text-gray-700\">Nom</label>\n <input type=\"text\" name=\"author\" id=\"author\" class=\"mt-1 focus:border-indigo-500 block w-full shadow-sm sm:text-sm border-gray-300 rounded-md\" @auth value=\"{{ old('author', Auth::user()->name) }}\" @else value=\"{{ old('author') }}\" @endauth>\n @error('author')\n <span class=\"text-red-600\">{{ $message }}</span>\n @enderror\n </div>\n <div class=\"py-2\">\n <label for=\"title\" class=\"block text-sm font-medium text-gray-700\">Titre du commentaire</label>\n <input type=\"text\" name=\"title\" id=\"title\" class=\"mt-1 focus:border-indigo-500 block w-full shadow-sm sm:text-sm border-gray-300 rounded-md\" value=\"{{ old('title') }}\">\n @error('title')\n <span class=\"text-red-600\">{{ $message }}</span>\n @enderror\n </div>\n <div class=\"py-2\">\n <label for=\"content\" class=\"block text-sm font-medium text-gray-700\">Contenu</label>\n <textarea id=\"content\" name=\"content\" rows=\"3\" class=\"shadow-sm focus:ring-indigo-500 focus:border-indigo-500 mt-1 block w-full sm:text-sm border-gray-300 rounded-md\">{{ old('content') }}</textarea>\n @error('content')\n <span class=\"text-red-600\">{{ $message }}</span>\n @enderror\n </div>\n <div class=\"py-2\">\n <input type=\"submit\" class=\"cursor-pointer inline-flex items-center w-1/4 py-4 border border-gray-400 shadow-sm text-base font-medium rounded-md text-gray-700 bg-white justify-center\" value=\"Créer\">\n </div>\n </div>\n </form>\n@endsection","filename":"resources/views/posts/show.blade.php"}


Et j'en profite pour modifier également le contrôleur, de telle sorte que lorsque l'administrateur va modifier un commentaire, s'il est signalé, alors il repasse en "non signalé".
{"language":"application/x-httpd-php","content":"<?php\n//..\npublic function update(Request $request, Comment $comment)\n{\n $validatedData = $request->validate([\n 'title'=>'required|min:2|max:255',\n 'author'=>'required|min:2|max:50',\n 'content'=>'required|min:5',\n ]);\n if($comment->reported) {\n $comment->update(array_merge($validatedData, ['reported' => 0]));\n } else {\n $comment->update($validatedData);\n }\n return redirect()->back()->with('success', 'Le commentaire a été modifié');\n}\n//..","filename":"CommentsController.php"}

L'eager loading


Je t'en parle depuis un petit moment, mais je pense important de te montrer ce que c'est.

Arthur, l'apprenti développeurJe suis tout ouïe !
Actuellement dans notre panneau d'administration, pour la liste des commentaires, on fait une boucle de la sorte :
{"language":"text/html","content":"@foreach($comments as $comment)\n {{ $comment->post->title }} \n@endforeach","filename":"show.blade.php"}

Comprenons ce qu'il se passe. Si on a 25 commentaires par exemple. Il va être fait une requête SQL pour récupérer tous les commentaires. Puis pour chaque commentaire, il va être fait une requête pour récupérer son post (et notamment le title du post). On imagine que ça devient problématique si on a 1000 commentaires, 100000 commentaires etc... Si on a x commentaires, cela représente x+1 requêtes. Beaucoup ! Et faire beaucoup de requêtes, même si elles sont petites, c'est moins bien que de faire pas beaucoup de requêtes mais plus grosses.
Heureusement Laravel a la solution : l'eager loading.

L'eager loading permet de transformer x+1 requêtes en... Devine ?

Arthur, l'apprenti développeurx requêtes ? Ca fait quand même beaucoup.
Non, en 2 requêtes. 2 requêtes seulement. Une requête pour récupérer les commentaires, et une requête pour récupérer les posts liés aux commentaires, d'un coup. On aura ce genre de requête :
{"language":"text/x-sql","content":"select * from comments\n\nselect * from posts where id in (1, 2, 3, 4, 5, ...)","filename":""}


Arthur, l'apprenti développeurOk c'est chouette ! Mais pourquoi ne pas utiliser les jointures ? On le ferait en 1 requête !
Excellente question. En fait, cela dépend des cas mais déjà parfois l'eager loading semble être plus efficace. Également, et c'est surtout pour ça, utiliser l'eager loading est super simple, super pratique donc super rapide. Pour des performances à peu près similaires à une jointure. Donc en général on utilise cette fonctionnalité. Cependant, tu peux utiliser les jointures dans Laravel grâce à une méthode nommée raw() sur l'interface DB. Si tu veux en savoir plus : [jointures avec Laravel].

Je disais que c'était simple d'utiliser l'eager loading. Il serait peut être temps de te le montrer !
Pour performer l'eager loading, on va utiliser "with" de Eloquent. On va donc modifier notre méthode index() de CommentsController de :

{"language":"application/x-httpd-php","content":"<?php\n//...\npublic function index()\n{\n return view('comments.index', ['comments'=>Comment::all()]);\n}","filename":"CommentsController.php"}

En :

{"language":"application/x-httpd-php","content":"<?php\n//...\npublic function index()\n{\n return view('comments.index', ['comments'=>Comment::with('post')->get()]);\n}","filename":"CommentsController.php"}

Arthur, l'apprenti développeurPourquoi ne réutilises-tu pas all() comme d'habitude ? Après le with() ?
Tout simplement parce que ça ne marche pas. On ne peut jamais mettre la méthode all() autre que seule. Si on "chaine" des méthodes, on utilisera get() pour récupérer tous les enregistrements qui correspondent à nos critères. Ici, le seul critère, c'est le "with", mais on pourrait utiliser ce genre de requête :

{"language":"application/x-httpd-php","content":"<?php\n//...\npublic function methode()\n{\n $comments = Comment::where('id', '>', 12)->where('author', 'Antoine')->orderBy('id', 'desc')->get();\n}","filename":"unExemple.php"}

Arthur, l'apprenti développeurEloquent a encore plein de fonctions à découvrir à ce que je vois !
Oh que oui ! Si tu veux en savoir +, c'est par ici : https://laravel.com/docs/8.x/eloquent-relationships

Le mot de la fin


Ce cours a été un long moment de rédaction et de relecture. J'espère qu'il t'aura plu et qu'il te donnera envie de continuer d'utiliser Laravel et de progresser. En septembre 2021 sortira le version 9 de Laravel, qui sera une version LTS (Long Term Support), c'est à dire une version qui sera maintenue à jour plus longtemps que les autres. Je modifierai alors le cours actuel pour qu'il soit compatible avec Laravel 9.

Je reste disponible pour répondre à tes questions et te souhaite une bonne continuation avec Laravel, le meilleur framework PHP du monde :p

Arthur, l'apprenti développeurMerci pour tout, je vais continuer d'apprendre et j'espère qu'on se reverra vite !

J'ai terminé cette partie
Demander de l'assistance