diff --git a/mini-crm/mini-crm_TP9_TP10.zip b/mini-crm/mini-crm_TP9_TP10.zip new file mode 100644 index 0000000..4721125 Binary files /dev/null and b/mini-crm/mini-crm_TP9_TP10.zip differ diff --git a/mini-crm/mini-crm_TP9_TP10/README_TP9_TP10.md b/mini-crm/mini-crm_TP9_TP10/README_TP9_TP10.md new file mode 100644 index 0000000..eaada80 --- /dev/null +++ b/mini-crm/mini-crm_TP9_TP10/README_TP9_TP10.md @@ -0,0 +1,140 @@ +# Mini-CRM — TP9 + TP10 + +Ce ZIP contient les fichiers à ajouter/remplacer dans le projet `mini-crm` +du dépôt `yolou/BUT2`. + +## Ce que j'ai fait — TP9 + +- J'ai activé le routage API dans `bootstrap/app.php`. +- J'ai créé `routes/api.php`. +- J'ai ajouté `GET /api/test`. +- J'ai créé un contrôleur séparé `Api/ClientController`. +- J'ai ajouté `GET /api/clients` pour récupérer les clients. +- J'ai ajouté `GET /api/clients/{client}` avec le route model binding. +- J'ai ajouté `POST /api/clients` pour créer un client. +- J'ai validé `name`, `email`, `phone` et `city`. +- Comme la migration du projet impose un email unique, j'ai ajouté la règle + `unique:clients,email`. +- J'ai réalisé le défi autonome : + `GET /api/clients/search?email=...`. +- Si aucun email ne correspond, l'API renvoie 404. + +## Ce que j'ai fait — TP10 + +- J'ai créé `ClientResource` afin de maîtriser le JSON exposé. +- J'utilise la Resource pour la liste, le détail, la création et la modification. +- J'ai ajouté `PATCH /api/clients/{client}`. +- Pour PATCH, j'utilise `sometimes` afin de permettre une modification partielle. +- Pour l'email, j'ignore l'identifiant du client courant lors du contrôle d'unicité. +- J'ai ajouté `DELETE /api/clients/{client}`. +- Une suppression réussie renvoie HTTP 204 No Content. +- J'ai réalisé le défi de pagination avec `Client::paginate(10)`. + +## Installation dans ton projet + +Copie le contenu de ce dossier dans : + +`C:\Users\Junior\Desktop\laraveltp\mini_crm_aylane` + +en conservant l'arborescence. + +Les fichiers principaux sont : + +- `bootstrap/app.php` +- `routes/api.php` +- `app/Http/Controllers/Api/ClientController.php` +- `app/Http/Resources/ClientResource.php` + +Le modèle `app/Models/Client.php` de ton dépôt possède déjà les champs fillable +`name`, `email`, `phone`, `city`, donc il n'a pas besoin d'être remplacé. + +## Commandes à lancer + +```bash +php artisan optimize:clear +php artisan route:list --path=api +php artisan serve +``` + +> Le sujet propose `php artisan install:api`, notamment pour installer Sanctum. +> Ici Sanctum n'est pas nécessaire au fonctionnement demandé dans les TP9/TP10 : +> le routage API est déclaré directement dans `bootstrap/app.php`. +> Le TP10 demande justement de ne pas mettre en place un nouveau système +> d'authentification pour cette partie pédagogique. + +## Routes attendues + +| Méthode | URL | Rôle | +|---|---|---| +| GET | `/api/test` | Vérifier l'API | +| GET | `/api/clients` | Clients paginés (10/page) | +| GET | `/api/clients/search?email=...` | Recherche par email | +| GET | `/api/clients/{client}` | Détail d'un client | +| POST | `/api/clients` | Créer un client | +| PATCH | `/api/clients/{client}` | Modifier partiellement | +| DELETE | `/api/clients/{client}` | Supprimer | + +## Tests rapides + +### Créer + +```bash +curl -X POST http://127.0.0.1:8000/api/clients ^ + -H "Content-Type: application/json" ^ + -H "Accept: application/json" ^ + -d "{\"name\":\"Thomas Leroy\",\"email\":\"thomas@example.com\",\"city\":\"Paris\"}" +``` + +Résultat attendu : **201 Created**. + +### Modifier + +```bash +curl -X PATCH http://127.0.0.1:8000/api/clients/1 ^ + -H "Content-Type: application/json" ^ + -H "Accept: application/json" ^ + -d "{\"name\":\"Jean Dupont Modifié\"}" +``` + +Résultat attendu : **200 OK**. + +### Supprimer + +```bash +curl -X DELETE http://127.0.0.1:8000/api/clients/1 -H "Accept: application/json" +``` + +Résultat attendu : **204 No Content**. + +### Pagination + +Ouvrir : + +`http://127.0.0.1:8000/api/clients?page=1` + +puis : + +`http://127.0.0.1:8000/api/clients?page=2` + +## Réponses sécurité TP10 + +1. Dans un vrai CRM, les routes de consultation, création, modification et + suppression des clients devraient être protégées car elles manipulent des + données privées ou permettent de les modifier. +2. POST est sensible car un utilisateur non autorisé pourrait créer de fausses + données dans la base. +3. Cacher un bouton dans l'interface ne sécurise rien : quelqu'un peut appeler + directement l'URL de l'API. La vérification doit être faite côté serveur. +4. Une Policy doit être vérifiée dans le contrôleur avant l'opération sensible + (par exemple avant `update()` ou `delete()`), une fois l'utilisateur + authentifié. + +## Codes HTTP à connaître + +- 200 : requête réussie +- 201 : ressource créée +- 204 : suppression réussie sans contenu +- 404 : client inexistant +- 422 : données invalides +- 401 : utilisateur non authentifié +- 403 : utilisateur authentifié mais non autorisé diff --git a/mini-crm/mini-crm_TP9_TP10/TRACE_TP9_TP10.txt b/mini-crm/mini-crm_TP9_TP10/TRACE_TP9_TP10.txt new file mode 100644 index 0000000..cd53957 --- /dev/null +++ b/mini-crm/mini-crm_TP9_TP10/TRACE_TP9_TP10.txt @@ -0,0 +1,11 @@ +TP9 : +J'ai ajouté une API au Mini-CRM sans supprimer les routes web existantes. +J'ai créé les routes GET/POST, le contrôleur API, la validation et la recherche par email. + +TP10 : +J'ai structuré le JSON avec ClientResource, ajouté PATCH et DELETE, +géré l'unicité de l'email et ajouté la pagination à 10 clients par page. + +Important : +Les routes API sont publiques uniquement pour le cadre pédagogique du TP. +Dans un vrai CRM, elles devraient être protégées par authentification et autorisation. diff --git a/mini-crm/mini-crm_TP9_TP10/app/Http/Controllers/Api/ClientController.php b/mini-crm/mini-crm_TP9_TP10/app/Http/Controllers/Api/ClientController.php new file mode 100644 index 0000000..272914a --- /dev/null +++ b/mini-crm/mini-crm_TP9_TP10/app/Http/Controllers/Api/ClientController.php @@ -0,0 +1,108 @@ +validate([ + 'name' => ['required', 'string', 'max:255'], + 'email' => ['required', 'email', 'max:255', 'unique:clients,email'], + 'phone' => ['nullable', 'string', 'max:20'], + 'city' => ['nullable', 'string', 'max:100'], + ]); + + $client = Client::create($validated); + + // 201 = Created : la ressource vient d'être créée. + return (new ClientResource($client)) + ->response() + ->setStatusCode(201); + } + + /** + * TP9/TP10 - Grâce au route model binding, Laravel recherche + * automatiquement le Client correspondant à {client}. + * Si l'identifiant n'existe pas, Laravel renvoie une erreur 404. + */ + public function show(Client $client) + { + return new ClientResource($client); + } + + /** + * TP10 - PATCH permet de modifier seulement les champs envoyés. + * "sometimes" évite d'obliger l'utilisateur à renvoyer tout le client. + */ + public function update(Request $request, Client $client) + { + $validated = $request->validate([ + 'name' => ['sometimes', 'required', 'string', 'max:255'], + 'email' => [ + 'sometimes', + 'required', + 'email', + 'max:255', + Rule::unique('clients', 'email')->ignore($client->id), + ], + 'phone' => ['sometimes', 'nullable', 'string', 'max:20'], + 'city' => ['sometimes', 'nullable', 'string', 'max:100'], + ]); + + $client->update($validated); + + return new ClientResource($client); + } + + /** + * TP10 - Je supprime le client avec Eloquent. + * Une suppression réussie renvoie 204 No Content. + */ + public function destroy(Client $client) + { + $client->delete(); + + return response()->noContent(); + } + + /** + * TP9 - Défi autonome : rechercher un client grâce à son email. + */ + public function search(Request $request) + { + $validated = $request->validate([ + 'email' => ['required', 'email'], + ]); + + $client = Client::where('email', $validated['email'])->first(); + + if (! $client) { + return response()->json([ + 'message' => 'Aucun client ne correspond à cette adresse email.', + ], 404); + } + + return new ClientResource($client); + } +} diff --git a/mini-crm/mini-crm_TP9_TP10/app/Http/Resources/ClientResource.php b/mini-crm/mini-crm_TP9_TP10/app/Http/Resources/ClientResource.php new file mode 100644 index 0000000..4a79eb5 --- /dev/null +++ b/mini-crm/mini-crm_TP9_TP10/app/Http/Resources/ClientResource.php @@ -0,0 +1,24 @@ + $this->id, + 'name' => $this->name, + 'email' => $this->email, + 'phone' => $this->phone, + 'city' => $this->city, + ]; + } +} diff --git a/mini-crm/mini-crm_TP9_TP10/bootstrap/app.php b/mini-crm/mini-crm_TP9_TP10/bootstrap/app.php new file mode 100644 index 0000000..a31da00 --- /dev/null +++ b/mini-crm/mini-crm_TP9_TP10/bootstrap/app.php @@ -0,0 +1,28 @@ +withRouting( + web: __DIR__.'/../routes/web.php', + + // TP9 : j'ai ajouté le fichier api.php au routage Laravel. + // Laravel applique automatiquement le préfixe /api à ces routes. + api: __DIR__.'/../routes/api.php', + + commands: __DIR__.'/../routes/console.php', + health: '/up', + ) + ->withMiddleware(function (Middleware $middleware): void { + // + }) + ->withExceptions(function (Exceptions $exceptions): void { + // Je conserve le comportement déjà présent dans le projet : + // les erreurs de l'API doivent être retournées en JSON. + $exceptions->shouldRenderJsonWhen( + fn (Request $request) => $request->is('api/*') || $request->expectsJson(), + ); + })->create(); diff --git a/mini-crm/mini-crm_TP9_TP10/routes/api.php b/mini-crm/mini-crm_TP9_TP10/routes/api.php new file mode 100644 index 0000000..f83863f --- /dev/null +++ b/mini-crm/mini-crm_TP9_TP10/routes/api.php @@ -0,0 +1,31 @@ + 'Mon API fonctionne', + 'version' => 1, + ]; +}); + +// TP9 - défi : j'ai placé /search AVANT /{client} pour éviter que Laravel +// interprète le mot "search" comme l'identifiant d'un client. +Route::get('/clients/search', [ClientController::class, 'search']); + +// TP9 + TP10 : CRUD complet de l'API clients. +Route::get('/clients', [ClientController::class, 'index']); +Route::post('/clients', [ClientController::class, 'store']); +Route::get('/clients/{client}', [ClientController::class, 'show']); +Route::patch('/clients/{client}', [ClientController::class, 'update']); +Route::delete('/clients/{client}', [ClientController::class, 'destroy']);