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\">\n\t <h1 class=\"text-3xl text-blue-700 my-8 underline\">{{ $post->title}}</h1>\n\t <p>{{ $post->content }}</p>\n\t</section>\n\t<h2 class=\"text-xl text-blue-500 underline\">Les commentaires liés à ce post</h2>\n\t@forelse($post->comments as $comment)\n\t\t<section class=\"mx-auto py-2 my-6 border border-blue-100\">\n\t\t <h3 class=\"text-base text-blue-400\">{{ $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 class=\"text-sm\">{{ $comment->content }}</p>\n\t\t \t<a class=\"text-red-400 inline-block text-xs mt-2\" href=\"{{ route('comments.report', $comment) }}\">Signaler le commentaire</a>\n\t\t @endif\n\t\t @if(Auth::check() and Auth::user()->role === \\App\\Enums\\UserRole::Admin)\n\t\t \t<p class=\"mt-2\">\n <a class=\"underline text-blue-400 text-xs\" href=\"{{ route('comments.edit', $comment) }}\">Editer le commentaire</a>\n </p>\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 class=\"text-xl\">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/9.x/eloquent-relationships

Autoriser un administrateur à donner ce rôle à un autre utilisateur


Je pense que cette fonctionnalité est intéressante. Elle te permettra de voir les risques du mass assignment (tu sais, l'histoire avec notre $fillable !).
Si tu te souviens bien, dans l'entité User, on n'avait pas mis "role" dans fillable : on ne souhaitait pas que les utilisateurs puissent le modifier. Mais maintenant, si on veut que les administrateurs puissent modifier le rôle d'autres utilisateurs il faut le rajouter dans fillable :

{"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 'role',\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"}

Maintenant, modifions la vue edit.blade.php pour rajouter le "select" de modification du rôle. On ne va l'afficher que si nous sommes administrateur, évidemment. Aussi, nous allons profiter de notre enum UserRole pour afficher directement les options possibles ([lister les valeurs d'enum avec PHP8]).

{"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 @if(Auth::user()->role === \\App\\Enums\\UserRole::Admin)\n <div class=\"py-2\">\n <label for=\"role\" class=\"block text-sm font-medium text-gray-700\">Role</label>\n <select name=\"role\">\n @foreach(\\App\\Enums\\UserRole::cases() as $role)\n <option value=\"{{ $role->value}}\">{{ $role->name }}</option>\n @endforeach\n </select>\n </div>\n @endif\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"}

Et je vais donc également modifier la méthode "update" de mon UsersController pour que le rôle soit bien pris en compte lorsque l'administrateur édite un utilisateur. On va donc changer nos règles de validation en rajoutant "role" et en indiquant que sa valeur doit être une donnée de notre enum :

{"language":"application/x-httpd-php","content":"<?php\n\nnamespace App\\Http\\Controllers;\n\nuse Illuminate\\Http\\Request;\nuse App\\Models\\User;\nuse Illuminate\\Validation\\Rules\\Enum;\n\nclass UsersController extends Controller\n{\n\t// Les autres méthodes\n\n public function update(Request $request, User $user)\n {\n $this->isAbleToEdit($user);\n $validatedData = $request->validate([\n 'name' => ['required', 'min:2', 'max:50'],\n 'email' => 'required|email',\n 'role'=> [new Enum(\\App\\Enums\\UserRole::class)],\n ]);\n\n $user->update($validatedData);\n\n return redirect()->back()->with('success', 'Les informations ont bien été modifiées');\n }\n\n\n private function isAbleToEdit(User $user)\n {\n if (!($user->id === request()->user()->id or request()->user()->role === \\App\\Enums\\UserRole::Admin)) {\n abort(403);\n }\n }\n}","filename":"UsersController.php"}

Bon, alors, comme ça, tout marche bien. Es-tu d'accord ?

Arthur, l'apprenti développeurJe suis en train d'essayer, ça marche !
Parfait. Cependant, tu as ici une faille de sécurité... Une personne peut s'auto-donner le rôle d'administrateur à cause du mass assignment.... Voici la technique :

  • Je vais éditer mon profil. Un peu curieux et bidouilleur, je vais tenter d'ajouter un input en modifiant le HTML :

  • Je soumets le formulaire. Voici un screen d'un dd($validatedData) dans la méthode update() dans le UsersController après avoir soumis ce formulaire modifié à la main :

  • Et après avoir soumis le formulaire, tout s'est bien passé, je suis devenu admin : j'ai en effet accès au select dans la page d'édition du profil (visible uniquement des admin) :


Et voilà, tout simplement... Tu peux essayer de ton côté, c'est très rapide !

Arthur, l'apprenti développeurOula ça fait peur :o. Je vais essayer de ce pas. Mais du coup, comment je me protège de ça ? Je dois pas mettre "role" dans $fillable ?
Si, tu n'as pas le choix. En fait, il y a 2 solutions :

  • Soit on n'utilise pas la méthode "update" mais la méthode "save" en faisant attention à mettre des if() bien placés

  • Soit on garde update() mais on en fait 2 : un si on est user, un si on est admin


Voici les deux solutions à l'oeuvre :

{"language":"application/x-httpd-php","content":"<?php\n\nnamespace App\\Http\\Controllers;\n\nuse Illuminate\\Http\\Request;\nuse App\\Models\\User;\nuse Illuminate\\Validation\\Rules\\Enum;\n\nclass UsersController extends Controller\n{\n // Les autres méthodes\n\n public function update(Request $request, User $user)\n {\n $this->isAbleToEdit($user);\n $validatedData = $request->validate([\n 'name' => ['required', 'min:2', 'max:50'],\n 'email' => 'required|email',\n 'role'=> [new Enum(\\App\\Enums\\UserRole::class)],\n ]);\n\n \n $user->name = $validatedData['name'];\n $user->email = $validatedData['email'];\n if(request()->user()->role === \\App\\Enums\\UserRole::Admin) {\n $user->role = $validatedData['role'];\n }\n $user->save();\n\n return redirect()->back()->with('success', 'Les informations ont bien été modifiées');\n }\n\n private function isAbleToEdit(User $user)\n {\n if (!($user->id === request()->user()->id or request()->user()->role === \\App\\Enums\\UserRole::Admin)) {\n abort(403);\n }\n }\n}","filename":"UsersController.php"}

{"language":"application/x-httpd-php","content":"<?php\n\nnamespace App\\Http\\Controllers;\n\nuse Illuminate\\Http\\Request;\nuse App\\Models\\User;\nuse Illuminate\\Validation\\Rules\\Enum;\n\nclass UsersController extends Controller\n{\n // Les autres méthodes\n\n public function update(Request $request, User $user)\n {\n $this->isAbleToEdit($user);\n $validatedData = $request->validate([\n 'name' => ['required', 'min:2', 'max:50'],\n 'email' => 'required|email',\n 'role'=> [new Enum(\\App\\Enums\\UserRole::class)],\n ]);\n\n if(request()->user()->role === \\App\\Enums\\UserRole::Admin) {\n $user->update($validatedData);\n } else {\n $user->update([\n 'name'=>$validatedData['name'],\n 'email'=>$validatedData['email']\n ]);\n }\n\t}\n\n private function isAbleToEdit(User $user)\n {\n if (!($user->id === request()->user()->id or request()->user()->role === \\App\\Enums\\UserRole::Admin)) {\n abort(403);\n }\n }\n}","filename":"UsersController.php"}

Ce genre de faille de sécurité est très très courant. Il faut vraiment penser à tout ça lorsqu'on développe. Pour information, on pourrait avoir le même souci lorsqu'on crée un utilisateur. Mais heureusement, par défaut, Laravel évite ce problème. Regarde le contenu de RegisteredUserController :
{"language":"application/x-httpd-php","content":"<?php\n\nnamespace App\\Http\\Controllers\\Auth;\n\nuse App\\Http\\Controllers\\Controller;\nuse App\\Models\\User;\nuse App\\Providers\\RouteServiceProvider;\nuse Illuminate\\Auth\\Events\\Registered;\nuse Illuminate\\Http\\Request;\nuse Illuminate\\Support\\Facades\\Auth;\nuse Illuminate\\Support\\Facades\\Hash;\nuse Illuminate\\Validation\\Rules;\n\nclass RegisteredUserController extends Controller\n{\n /**\n * Display the registration view.\n *\n * @return \\Illuminate\\View\\View\n */\n public function create()\n {\n return view('auth.register');\n }\n\n /**\n * Handle an incoming registration request.\n *\n * @param \\Illuminate\\Http\\Request $request\n * @return \\Illuminate\\Http\\RedirectResponse\n *\n * @throws \\Illuminate\\Validation\\ValidationException\n */\n public function store(Request $request)\n {\n $request->validate([\n 'name' => ['required', 'string', 'max:255'],\n 'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],\n 'password' => ['required', 'confirmed', Rules\\Password::defaults()],\n ]);\n\n $user = User::create([\n 'name' => $request->name,\n 'email' => $request->email,\n 'password' => Hash::make($request->password),\n ]);\n\n event(new Registered($user));\n\n Auth::login($user);\n\n return redirect(RouteServiceProvider::HOME);\n }\n}\n","filename":"app/Http/Controllers/Auth/RegisteredUserController.php"}

Dans la méthode "store", le User::create() précise bien chacun des champs souhaités. Comme dans notre solution numéro 2 au dessus ! Donc c'est tout bon ;).

Arthur, l'apprenti développeurOk c'est compris. Fiou, il faudra que j'y pense quand je développerai mes sites après !
Ça oui ! Allez, dernière chose à voir : si tu as testé d'éditer ton profil en tant Administrateur, tu auras remarqué que dans le select, par défaut, c'est "User" qui s'affiche. On va donc utiliser @old. Mais avec les select, c'est un peu particulier. Et heureusement, avec Laravel 9, ça a été simplifié : on a désormais la directive @selected.

{"language":"application/x-httpd-php","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 @if(Auth::user()->role === \\App\\Enums\\UserRole::Admin)\n <div class=\"py-2\">\n <label for=\"role\" class=\"block text-sm font-medium text-gray-700\">Role</label>\n <select name=\"role\">\n @foreach(\\App\\Enums\\UserRole::cases() as $role)\n <option value=\"{{ $role->value}}\" @selected(old('role', Auth::user()->role) == $role)>{{ $role->name }}</option>\n @endforeach\n </select>\n </div>\n @endif\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"}

Et voilààààààà !

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. Il s'agit de l'amélioration du cours sur Laravel 8. Il y a eu des corrections et des ajouts pour coller au mieux à l'évolution du framework et également voir certaines des nouvelles fonctionnalités apportées par la version 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