Modifier et supprimer : en autonomie
Temps 1 · séance 3 · ~60 min + maison
- Écrire seul la page de modification d'un projet, à partir de la page de création.
- Réutiliser la même Form Request pour créer et pour modifier.
- Supprimer un projet après une confirmation dans une boîte de dialogue.
- Trouver la cause d'un envoi qui échoue, dans l'onglet Réseau ou dans le journal de Laravel.
1. Ce que vous construisez
Votre back-office sait lister et créer. Il lui manque la modification et la suppression, les deux dernières lettres du CRUD de votre cahier des charges. Vous les écrivez seul, avec ce que les chapitres 1.10 à 1.12 vous ont montré. Ce chapitre ne donne pas le code : il donne ce qui doit marcher, où chercher, et les pièges.
Les critères
Côté serveur, dans Admin\ProjectController :
-
edit(Project $project): Responserend la pageadmin/projects/Editavec deux props : le projet et la liste des étiquettes. - Le projet envoyé est un tableau construit, avec
id,title,summary,content,demo_url,repo_urlet les numéros de ses étiquettes. Jamais le modèle entier. -
update(ProjectRequest $request, Project $project): RedirectResponseenregistre les champs validés, synchronise les étiquettes, prépare la notification « Projet modifié. » et redirige vers la liste. -
destroy(Project $project): RedirectResponsesupprime le projet, prépare la notification « Projet supprimé. » et redirige vers la liste. -
ProjectRequestreste la seule Form Request du projet : les règles ne sont écrites qu'une fois.
Côté navigateur :
- La page
admin/projects/Edit.vueaffiche le formulaire du chapitre 1.12, prérempli avec les valeurs du projet, cases à cocher comprises. - Le formulaire s'envoie avec
<Form v-bind="ProjectController.update.form(project)">. - Les erreurs s'affichent sous les champs, comme à la création, et le bouton « Enregistrer » se désactive pendant l'envoi.
- Dans la liste, chaque ligne a un bouton « Modifier », qui est un lien vers la page de modification, et un bouton « Supprimer ».
- « Supprimer » ouvre une boîte de dialogue qui nomme le projet. La boîte contient un
<Form v-bind="ProjectController.destroy.form(project)">, un bouton « Annuler » et un bouton de confirmation. - Après l'enregistrement ou la suppression, vous revenez à la liste avec une notification, et la boîte est fermée.
Vérifiez dans le navigateur
- Le fil d'Ariane de la page de modification affiche « Projets › » suivi du titre du projet.
- Un titre vidé affiche « Le champ titre est obligatoire. », et les autres champs gardent leur valeur.
- Un titre modifié apparaît dans la liste et sur la fiche, mais l'adresse
/projets/…du projet ne change pas. - Une étiquette décochée disparaît du projet, ce que vous vérifiez sur sa fiche publique ou avec Tinker.
- « Annuler » ferme la boîte sans rien supprimer. La confirmation retire la ligne de la liste.


2. Ce que vous avez déjà
Presque tout existe déjà dans votre projet, ou dans le kit.
| Pour | Partez de |
|---|---|
| la page de modification | votre Create.vue du chapitre 1.12 : même formulaire, autre fonction de Wayfinder, valeurs de départ |
| des champs préremplis | la page de profil du kit, resources/js/pages/settings/Profile.vue, qui préremplit le nom et l'adresse e-mail |
| une suppression confirmée | resources/js/components/DeleteUser.vue, la suppression du compte dans le kit : un Dialog qui contient un <Form> |
| la logique du serveur | votre store() du chapitre 1.12, et le chapitre 16 de 5XCOS, « Formulaires : modifier et supprimer » |
| le bouton-lien « Modifier » | le bouton « Nouveau projet » du chapitre 1.11, avec as-child |
Les ressources
| Ressource | Ce que vous y cherchez |
|---|---|
| Inertia, page « Forms » | la section « Form Component » : les « Slot Props » comme errors et processing, et les « Events » comme success |
| shadcn-vue, page « Dialog » | les morceaux de la boîte (DialogTrigger, DialogContent, DialogClose…) et l'exemple « Dialog with form » |
DeleteUser.vue du kit |
un Dialog, un <Form v-bind="….destroy.form()"> dedans, un bouton d'annulation dans DialogClose |
| 5XCOS, chapitre 16 | edit(), update(), destroy(), la Form Request partagée et le champ _method en Blade |
Ouvrez aussi le fichier que Wayfinder a généré pour votre contrôleur, resources/js/actions/App/Http/Controllers/Admin/ProjectController.ts. Les fonctions update et destroy y sont depuis le chapitre 1.10 : Wayfinder suit les routes déclarées par Route::resource(), même avant que les méthodes existent dans le contrôleur.
3. Les pièges
Le champ _method
En 5XCOS, un formulaire de modification contenait @method('PUT'), qui écrivait un champ caché _method. Le navigateur n'envoie un formulaire qu'en GET ou en POST, et Laravel lit _method pour savoir quelle route chercher.
Avec Wayfinder, vous n'écrivez rien de tout cela. ProjectController.update.form(project) renvoie une adresse qui contient déjà la méthode, et la méthode post. Pour le projet numéro 2, le navigateur reçoit cette balise :
<form action="/admin/projets/2?_method=PUT" method="post">
Laravel lit _method dans l'adresse aussi bien que dans un champ caché. destroy.form(project) fait la même chose avec _method=DELETE. Si vous écrivez l'adresse à la main, avec method="post" et sans _method, la route POST admin/projets/{project} n'existe pas, et Laravel répond par le code 405, comme au chapitre 16 de 5XCOS.
La route avec paramètre
update.form(project) construit l'adresse avec le numéro du projet. Wayfinder le cherche dans la clé id de l'objet reçu, comme le dit la signature générée :
update.url = (args: { project: number | { id: number } } | [project: number | { id: number } ] | number | { id: number }, options?: RouteQueryOptions) => {
Si le tableau envoyé par edit() n'a pas de clé id, la fonction ne trouve aucun numéro, et la page ne s'affiche pas. La console du navigateur montre alors une TypeError. Mettez id dans les props de edit(), même si la page ne l'affiche pas.
Les valeurs de départ
Un champ non contrôlé reçoit sa valeur de départ par la prop :default-value, comme dans la page de profil du kit. Pour les cases à cocher, la valeur de départ est true ou false : la case est cochée si le numéro de l'étiquette figure dans les étiquettes du projet.
<Checkbox
name="tags[]"
:value="String(tag.id)"
:default-value="project.tags.includes(tag.id)"
/>
Côté serveur, $project->tags()->pluck('tags.id') donne la liste des numéros d'étiquettes du projet.
Le fil d'Ariane qui dépend d'une prop
Dans Create.vue, le fil d'Ariane est un objet fixe, écrit dans defineOptions. Dans Edit.vue, il contient le titre du projet, qui est une prop. Or defineOptions ne voit pas les props de la page. Inertia accepte une fonction à la place de l'objet : il l'appelle avec les props de la page, et elle renvoie les options du layout.
defineOptions({
layout: (pageProps) => ({
breadcrumbs: [
{ title: 'Projets', href: index() },
{ title: pageProps.project.title, href: edit(pageProps.project) },
],
}),
});
Le Dialog qui ne se ferme pas
Placez la boîte de dialogue dans la ligne du tableau, une boîte par projet, comme dans DeleteUser.vue. Après la suppression, la ligne disparaît de la liste, et sa boîte disparaît avec elle.
Une seule boîte placée hors du tableau, ouverte par une variable, se comporte autrement. Après la suppression, la ligne disparaît et la notification s'affiche, mais la boîte reste ouverte avec « Supprimer « … » ? ». La redirection ramène sur la même page, Inertia garde le composant et son état, et la variable n'est jamais remise à zéro. Si vous choisissez cette version, remettez la variable à zéro dans l'événement @success du <Form>.
Le bouton « Annuler » se place dans un DialogClose as-child. Ce composant lui donne type="button" : un clic ferme la boîte sans envoyer le <Form> qui l'entoure.
Le slug ne change pas
La Form Request du chapitre 1.12 ne calcule le slug qu'à la création, quand la requête est un POST. À la modification, elle ne le touche pas, et sa règle prohibited refuse même un slug qu'une requête fabriquée enverrait. Un projet garde donc son adresse publique toute sa vie : si vous corrigez le titre du quiz, la liste et la fiche affichent le nouveau titre, et /projets/quiz-des-capitales répond toujours. Un lien partagé sur LinkedIn ne casse pas.
4. Quand ça casse
Le formulaire ne s'enregistre pas, et rien ne s'affiche
Un envoi refusé répond par une redirection, 303 ici parce que la méthode est PUT, puis la page reçoit les messages dans errors, comme au chapitre 1.12. Si aucun message n'apparaît, l'erreur porte sur un champ qui n'a pas d'InputError sous lui. Ouvrez l'onglet Réseau, cliquez sur la dernière requête vers la page du formulaire, et lisez la prop errors dans sa réponse. Le nom du champ refusé et son message s'y trouvent. Ajoutez un InputError pour ce champ, ou corrigez la valeur envoyée.
Une fenêtre avec une page d'erreur au-dessus de la vôtre
Une erreur dans votre code PHP produit une erreur 500. En local, Inertia affiche la page d'erreur de Laravel dans une fenêtre, par-dessus votre page. Lisez le message en haut, puis le nom du fichier et le numéro de ligne : ils désignent la ligne qui a échoué, souvent une faute de frappe dans le contrôleur ou une colonne absente de #[Fillable].
Le même message est écrit dans le journal de Laravel. Sur macOS et Linux, composer run dev lance un processus logs qui l'affiche dans le terminal. Sous Windows, ce processus n'existe pas : ouvrez storage/logs/laravel.log et lisez les dernières lignes.
Une erreur 404, ou un projet vide
La liaison de modèle cherche le projet par le paramètre {project} de la route. Un numéro absent de la base donne une 404 : vérifiez l'adresse dans la barre du navigateur. Un argument mal nommé, comme Project $projet, ne donne pas d'erreur : Laravel vous passe un projet vide, sans id, et la page reste blanche. L'argument de edit(), update() et destroy() doit s'appeler $project.
Une page blanche
Une page blanche vient le plus souvent d'une erreur JavaScript. Ouvrez la console avec F12 : le message nomme le fichier et la ligne. Une TypeError sur update.form() ou edit() signale un projet sans id, vu à la section 3.
Avant de demander de l'aide, ayez sous les yeux le message exact, le fichier, et la requête dans l'onglet Réseau.
5. Pratique : modifier, puis supprimer (60 min)
Faites les deux parties dans l'ordre.
- Modifier, environ 35 minutes.
edit()etupdate(), puisEdit.vue, puis le bouton « Modifier » dans la liste. Vérifiez chaque critère avant de passer à la suite. - Supprimer, environ 25 minutes.
destroy(), puis le bouton « Supprimer » et sa boîte de dialogue dans la liste.
Modifiez un vrai projet : corrigez le résumé d'un de vos projets seedés, ou complétez les liens du projet créé au chapitre 1.12. Puis créez un projet d'essai et supprimez-le.
6. À la maison
- Terminez la modification et la suppression, et cochez chaque critère de la section 1.
- Terminez votre planche de direction artistique du chapitre 1.09, si ce n'est pas encore fait. Elle se remet en PDF avec le portfolio, dans le devoir Teams du temps 1. Vous l'appliquerez à la page publique au chapitre 1.18.
Ce que votre portfolio contient maintenant
Un back-office complet pour vos projets : lister, créer, modifier, supprimer. Chaque action passe par un <Form>, valide ses champs avec la même Form Request, et revient à la liste avec une notification. La suppression demande une confirmation.
À retenir
ProjectController.update.form(project)etdestroy.form(project)mettent la méthodePUTouDELETEdans l'adresse, avec?_method=, sans champ caché à écrire.- Wayfinder lit le numéro du projet dans sa clé
id, qui doit donc figurer dans les props de la page. :default-valuedonne sa valeur de départ à un champ non contrôlé, case à cocher comprise.- Une boîte de dialogue placée dans chaque ligne disparaît avec la ligne supprimée, sans code pour la fermer.
- Une erreur de validation se lit dans la prop
errorsde l'onglet Réseau, et une erreur 500 dans la page d'erreur de Laravel ou dansstorage/logs/laravel.log.