Commentaires
Nous avons presque terminé notre blog. Il ne nous reste plus qu'à modifier l'accueil pour afficher les posts et leurs commentaires.
Pour commencer, je te propose de designer le layout. Il y en a déjà un de proposé (guest.blade.php) qui utilise les view composers, mais nous allons utiliser ici l'héritage (ce qui est possible en Laravel 8 et ce qui était tout le temps fait dans les versions précédentes) que tu pourras rencontrer dans plusieurs autres projets Laravel je n'en doute pas.

Arthur, l'apprenti développeurOk ça me va !

Le layout et la page d'accueil


Commençons par créer notre fichier de layout, c'est à dire par créer le design qui va être commun à toutes les pages de la partie accessible à tous. On va l'appeler "front.blade.php". Je te préviens d'avance, ce ne sera pas très "beau" ni très "responsive". Je fais au plus simple, n'hésite pas à modifier par la suite !

Arthur, l'apprenti développeurJe comprends, le principal c'est la partie Laravel.
Oui, merci pour ta compréhension :p. Bon, alors pour faire notre layout il nous faut savoir quelles parties seront différentes entre chaque page. En général, on aura :

  • Le titre de la page

  • Sa meta description

  • Peut être du style particulier

  • Le contenu principal

  • Peut être des scripts particuliers


On doit donc faire en sorte que dans chaque vue utilisant notre layout on puisse renseigner ces différents éléments. Pour ça, on va utiliser dans le layout la fonction @yield de Blade. Cette fonction définit un "nom de variable" et un contenu par défaut facultatif. Ce nom de variable peut alors être réutilisé dans les vues pour renseigner un contenu qui lui est propre !
Je te propose donc le code suivant pour la layout :

{"language":"text/html","content":"<!DOCTYPE html>\n<html lang=\"{{ str_replace('_', '-', app()->getLocale()) }}\">\n <head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n <meta name=\"csrf-token\" content=\"{{ csrf_token() }}\">\n\n <title>@yield('title', 'Blog')</title>\n\n <meta name=\"description\" content=\"@yield('meta', 'Articles lunaires et articles heureux sur ce blog merveilleux')\">\n\n <!-- Fonts -->\n <link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css2?family=Nunito:wght@400;600;700&display=swap\">\n\n <!-- Styles -->\n <link rel=\"stylesheet\" href=\"{{ asset('css/app.css') }}\">\n @yield('scripts')\n\n <!-- Scripts -->\n <script src=\"{{ asset('js/app.js') }}\" defer></script>\n @yield('style')\n </head>\n <body class=\"font-sans antialiased\">\n <div class=\"min-h-screen bg-gray-100\">\n <header class=\"fixed top-0 left-0 w-full space-x-1 sm:h-12 bg-white flex items-center sm:space-x-5 sm:pl-8 flex-wrap\">\n <span class=\"text-blue-800 inline-block mr-8\">Mon Super Blog</span>\n <a href=\"/\">Accueil</a>\n <a href=\"{{ route('posts.index') }}\">Les posts</a>\n @auth\n <a href=\"{{ route('users.edit', Auth::user()) }}\">Profil</a>\n <form action=\"{{ route('logout') }}\" method=\"POST\">\n @csrf\n <button type=\"submit\" href=\"{{ route('logout') }}\">Déconnexion</button>\n </form>\n @if(Auth::user()->admin)\n <a href=\"{{ route('dashboard') }}\">Dashboard</a>\n @endif\n @else\n <a href=\"{{ route('login') }}\">Connexion</a>\n @endauth\n </header>\n\n\n <!-- Page Content -->\n <main class=\"mt-14 mx-2 sm:mx-8\">\n @yield('content')\n </main>\n </div>\n </body>\n</html>","filename":"resources/views/posts/show.blade.php"}


Tu comprends ce que j'ai fait ?

Arthur, l'apprenti développeurOui j'ai compris, le yield fonctionne un peu comme une variable dont le contenu proviendrait d'un ob_get_clean() en PHP classique.
C'est tout à fait ça ! @auth est une condition qui vérifie quant à elle si on est authentifié. Ensuite, j'utilise Auth, je t'en avais un peu parlé. On peut utiliser cette classe pour accéder aux données de l'utilisateur en faisant Auth::user().

Arthur, l'apprenti développeurOk ! Par contre pourquoi tu utilises un formulaire et pas un lien pour la déconnexion ?
Il suffit de regarder la méthode supportée par la route qui gère le logout. Si tu fais php artisan route:list, tu remarqueras que Laravel Breeze qui gère l'authentification ne supporte que le POST pour le logout. On doit donc utiliser un formulaire et le token CSRF ;)

On va maintenant modifier l'accueil de notre site du coup. Il s'agit pour cela de modifier la vue "welcome.blade.php" .
Pour utiliser notre layout, il va falloir indiquer qu'on va en hériter. Et pour ça, c'est comme en PHP classique, on va utiliser extends. Enfin, une fonction de Blade qui s'appelle extends :p. Et on va aussi utiliser la fonction @section pour renseigner les variables qu'on a définies avec @yield dans le layout parent.

{"language":"text/html","content":"@extends('layouts.front')\n@section('title', 'Accueil du blog')\n@section('content')\n <h1 class=\"text-3xl text-blue-700 my-8\">Bienvenue sur mon super blog</h1>\n <p>Retrouvez des articles de qualité sur des sujets variés qui embelliront vos journées !</p>\n <h2 class=\"text-2xl my-6\">Le dernier article</h2>\n\n@endsection","filename":"resources/views/welcome.blade.php"}

Arthur, l'apprenti développeurC'est clair pour moi. Par contre comment on va faire pour afficher le dernier post ?
Excellente question. On rappelle que cette tâche, de récupérer le dernier post et de le passer à la vue, est dédiée plutôt à la partie Controller du modèle MVC. Mais cette vue est appelée directement dans les routes. Cela a un certain avantage : c'est rapide et Laravel va mettre moins de temps à afficher la page. C'est donc une technique conseillée quand on a besoin que d'afficher la vue et de ne faire aucun traitement (page statique, mentions légales...). Mais nous là on va devoir créer un nouveau controller que je vais appeler "FrontController". Bilan :

{"language":"application/x-httpd-php","content":"<?php\n\nuse Illuminate\\Support\\Facades\\Route;\n\nuse App\\Http\\Controllers\\{CommentsController, PostsController, UsersController, FrontController};\n\n// ...\n\nRoute::get('/', [FrontController::class, 'welcome']);\n\n// ...","filename":"routes/web.php"}

{"language":"application/x-httpd-php","content":"<?php\n\nnamespace App\\Http\\Controllers;\n\nuse Illuminate\\Http\\Request;\nuse App\\Models\\Post;\n\nclass FrontController extends Controller\n{\n public function welcome() \n {\n \t$post = Post::latest('id')->first();\n \treturn view('welcome', compact('post'));\n }\n}","filename":"FrontController.php"}

Arthur, l'apprenti développeurEncore de nouvelles méthodes offertes par Eloquent latest() et first() ?
Tout à fait. Latest va récupérer les éléments dont la valeur de la colonne passée en argument est la plus récente. Ici, on récupère donc les derniers éléments insérés en fonction de l'ID. Et combien de derniers éléments ? Hé bien, un seul, grâce à first(). En bref, on récupère donc le dernier élément enregistré.
Du coup on peut terminer notre vue :

{"language":"text/html","content":"@extends('layouts.front')\n@section('title', 'Accueil du blog')\n@section('content')\n <h1 class=\"text-3xl text-blue-700 my-8\">Bienvenue sur mon super blog</h1>\n <p>Retrouvez des articles de qualité sur des sujets variés qui embelliront vos journées !</p>\n <h2 class=\"text-2xl my-6\">Le dernier article</h2>\n <div class=\"bg-white shadow w-full sm:w-1/2\">\n <h3 class=\"text-xl text-blue-500\">{{ $post->title }}</h3>\n <p>{{ Str::limit($post->content, 10) }}</p>\n </div>\n@endsection","filename":"welcome.blade.php"}

Arthur, l'apprenti développeurStr::limit() ? Quoi qu'est-ce donc ?
Je t'ai souvent parlé des helpeurs de Laravel. Il en offre aussi pour la manipulation des chaines de caractères ! https://laravel.com/docs/8.x/helpers. Cette fonction permet de remplacer les caractères qui dépassent notre limite par des "points" (...) (mais on peut aussi remplacer par ce qu'on veut). Dans notre cas j'ai fixé une limite à 10 caractères juste pour que tu voies ce que ça donne, mais évidemment on pourrait plutôt mettre 100 ou 150 ;)

Arthur, l'apprenti développeurOula en effet il y a plein de helpeurs sur la page que tu m'as donnée ! Plein de fonctions intéressantes, je vais regarder ça plus tard !
Oui, avant de faire une fonction hyper compliquée pour agir sur des chaines de caractères par exemple, n'hésite pas à regarder du côté des helpeurs ;)

Arthur, l'apprenti développeurÇa marche. Par contre je crois que tu as oublié de faire un lien pour lire l'article. Je fais ça, ça marche ?
{"language":"text/html","content":"@extends('layouts.front')\n@section('title', 'Accueil du blog')\n@section('content')\n <h1 class=\"text-3xl text-blue-700 my-8\">Bienvenue sur mon super blog</h1>\n <p>Retrouvez des articles de qualité sur des sujets variés qui embelliront vos journées !</p>\n <h2 class=\"text-2xl my-6\">Le dernier article</h2>\n <div class=\"bg-white shadow w-full sm:w-1/2\">\n <h3 class=\"text-xl text-blue-500\">{{ $post->title }}</h3>\n <p>{{ Str::limit($post->content, 10) }}</p>\n <a href=\"{{ route('posts.show', $post) }}\">Lire l'article</a>\n </div>\n@endsection","filename":"welcome.blade.php"}

Oui c'est parfait félicitations ! D'ailleurs, on va passer à la vue pour afficher les posts du coup, et en afficher aussi un en particulier.

Afficher tous les posts


Je ne crois pas qu'il y ait de difficultés à ce sujet.

{"language":"text/html","content":"@extends('layouts.front')\n@section('title', 'La liste des posts de mon super blog')\n@section('content')\n <h1 class=\"text-3xl text-blue-700 my-8\">Les articles</h1>\n <p>Tous les articles sont ici, profitez-en ;)</p>\n @foreach($posts as $post)\n\t <div class=\"bg-white shadow w-full sm:w-1/2\">\n\t <h3 class=\"text-xl text-blue-500\">{{ $post->title }}</h3>\n\t <p>{{ Str::limit($post->content, 10) }}</p>\n\t <a href=\"{{ route('posts.show', $post) }}\">Lire l'article</a>\n\t </div>\n\t@endforeach\n@endsection","filename":"resources/views/posts/index.blade.php"}

Afficher un seul post


Là on doit faire plus de choses :
- Afficher le post
- Afficher les commentaires
- Afficher le formulaire pour laisser un commentaire.
Le code est grand mais il n'y a rien que tu connaisses pas déjà si ce n'est la fonction @forelse qui est l'équivalent d'un @if(maCollectionN'estPasVide) @foreach(maCollection...) @endforeach @endif.
{"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 <p>{{ $comment->content }}</p>\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"}


Arthur, l'apprenti développeurLong code mais je crois avoir tout compris. Si j'ai des questions je reviens vers toi !

L'édition du profil


Le code est assez simple encore une fois, je ne pense pas qu'il posera de difficultés.
{"language":"text/html","content":"@extends('layouts.front')\n@section('title', 'Modification de mon profil')\n@section('content')\n<div class=\"bg-white overflow-hidden shadow-sm sm:rounded-lg\">\n <h3 class=\"text-3xl mx-4 my-4\">Modification de mon profil</h3>\n @if(session('success'))\n <div class=\"text-xl text-green-400\">\n {{ session('success') }}\n </div>\n @endif\n <form action=\"{{ route('users.update', $user) }}\" method=\"POST\" class=\"mx-auto\">\n @csrf\n @method('PUT')\n <div class=\"px-4 py-5 bg-white sm:p-6\">\n <div class=\"py-2\">\n <label for=\"name\" class=\"block text-sm font-medium text-gray-700\">Prénom</label>\n <input type=\"text\" name=\"name\" id=\"name\" autocomplete=\"given-name\" class=\"mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm sm:text-sm border-gray-300 rounded-md\" value=\"{{ old('name', $user->name) }}\">\n @error('name')\n <span class=\"text-red-600\">{{ $message }}</span>\n @enderror\n </div>\n <div class=\"py-2\">\n <label for=\"email\" class=\"block text-sm font-medium text-gray-700\">Email</label>\n <input type=\"text\" name=\"email\" id=\"email\" autocomplete=\"given-name\" class=\"mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm sm:text-sm border-gray-300 rounded-md\" value=\"{{ old('email', $user->email) }}\">\n @error('email')\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=\"Modifier\">\n </div>\n </div>\n </form>\n</div>\n@endsection","filename":"resources/views/users/edit.blade.php"}

Arthur, l'apprenti développeurJe comprends tout, enfin je crois... Si j'ai des questions je viens te chercher :p
Parfait ! Et bien voilà, on a fini le code des vues... Et on a aussi fini notre projet. Voyons dans la partie d'après quelques façons de l'améliorer et comment poursuivre ton apprentissage.

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