-
@foreach($errors->all() as $error)
- {{ $error }} @endforeach
# Guide complet : Laravel 12 CRUD ToDoList from scratch  *Aperçu de l'interface finale :*  **Public cible :** Très débutant (style stagiaire) **Objectif :** Projet CRUD ToDoList avec 2 tables en relation (categories 1—N tasks) + pages Blade simples en HTML/CSS sans framework. --- ## Table des matières 1. [Prérequis et liens officiels](#1-prérequis-et-liens-officiels) 2. [Créer le dossier et le projet Laravel 12](#2-créer-le-dossier-et-le-projet-laravel-12) 3. [Ouvrir le projet dans VS Code](#3-ouvrir-le-projet-dans-vs-code) 4. [Configurer la base de données](#4-configurer-la-base-de-données) 5. [Schéma de la base (MLD/MCD)](#5-schéma-de-la-base-mldmcd) 6. [Migrations](#6-migrations) 7. [Models avec relations](#7-models-avec-relations) 8. [Controllers CRUD](#8-controllers-crud) 9. [Routes](#9-routes) 10. [Vues Blade – Layout](#10-vues-blade--layout) 11. [Vues Blade – Categories](#11-vues-blade--categories) 12. [Vues Blade – Tasks](#12-vues-blade--tasks) 13. [Résumé des commandes](#13-résumé-des-commandes) 14. [Checklist de debug](#14-checklist-de-debug) --- ## 1. Prérequis et liens officiels ### Requirements | Logiciel | Version minimale | Lien officiel | |----------|------------------|---------------| | **PHP** | 8.2+ | [php.net](https://php.net/) | | **Composer** | 2.x | [getcomposer.org](https://getcomposer.org/) | | **MySQL** | 5.7+ ou 8.x | Via XAMPP | | **VS Code** | Dernière stable | [code.visualstudio.com](https://code.visualstudio.com/) | | **XAMPP** | 8.x (PHP + MySQL) | [apachefriends.org](https://www.apachefriends.org/) | ### Extensions PHP requises - **Vérifier :** `php -v` et `composer -v`  ### Liens utiles > **Piège fréquent :** Si `php` ou `composer` n’est pas reconnu dans le terminal, ajoutez-les au PATH système (répertoire `php` et `composer` dans XAMPP). --- **NEXT :** Passer à l’étape 2 – Créer le dossier et le projet Laravel 12. --- ## 2. Créer le dossier et le projet Laravel 12 ### Étapes 1. Ouvrir **CMD** dans un dossier (ex. `C:\Users\Admin\Desktop\laravel`). 2. Démarrer **XAMPP** (Apache + MySQL) et créer la base `todolist_db` dans phpMyAdmin. 3. Exécuter : ```bash composer create-project laravel/laravel todolist cd todolist php artisan serve ``` **Explication :** Le point `.` crée le projet dans le dossier courant. `^12.0` impose Laravel 12. > **Piège fréquent :** Ne pas oublier le point final. Sinon Composer crée un sous-dossier `laravel`. 4. Base `todolist_db` dans phpMyAdmin. - Aller sur `http://localhost/phpmyadmin` - Créer une base nommée `todolist_db` (interclassement : `utf8mb4_unicode_ci`). 6. Lancer le serveur Laravel : ```bash php artisan serve ``` L’application sera accessible sur `http://localhost:8000`. > **Piège fréquent :** Vérifier que le port 8000 n’est pas déjà utilisé. Sinon : `php artisan serve --port=8080`. --- **NEXT :** Ouvrir le projet dans VS Code avant de modifier `.env`. --- ## 3. Ouvrir le projet dans VS Code Dans le dossier du projet (ex. `todolist-app`), exécuter : ```bash code . ``` **Explication :** Ouvre VS Code avec le projet comme workspace. Pensez à installer l’extension [Laravel](https://marketplace.visualstudio.com/items?itemName=laravel.vscode-laravel) pour VS Code. > **Piège fréquent :** Si `code` n’est pas reconnu, ajoutez VS Code au PATH ou ouvrez le dossier manuellement (Fichier → Ouvrir un dossier). --- **NEXT :** Configurer la base de données dans `.env`. --- ## 4. Configurer la base (.env) Modifier `.env` : `DB_DATABASE=todolist_db`, `DB_USERNAME=root`, `DB_PASSWORD=` Puis : ``` php artisan config:clear ``` --- **NEXT :** Créer les migrations et le schéma de base. --- ## 5. Schéma de la base (MLD/MCD) ### MCD (Modèle Conceptuel de Données) ``` ┌─────────────────────┐ ┌─────────────────────┐ │ CATEGORY │ │ TASK │ ├─────────────────────┤ ├─────────────────────┤ │ id (PK) │ 1 N │ id (PK) │ │ name │─────────│ category_id (FK) │ │ created_at │ │ title │ │ updated_at │ │ done (boolean) │ └─────────────────────┘ │ due_date (nullable) │ │ created_at │ │ updated_at │ └─────────────────────┘ ``` **Relation :** 1 Category → N Tasks (clé étrangère `category_id` avec `onDelete('cascade')`). --- **NEXT :** Créer les migrations. --- ## 6. Migrations **C'est quoi une migration ?** Un fichier PHP qui décrit la structure d'une table (colonnes, types). Laravel l'exécute pour créer les tables dans MySQL. **Comment ça marche dans le projet ?** Les migrations sont dans `database/migrations/`. Chaque fichier = 1 table. La commande `php artisan migrate` exécute tout et crée les tables. --- ### Créer les migrations ```bash php artisan make:migration create_categories_table php artisan make:migration create_tasks_table ``` ### Migration `categories` Ouvrir `database/migrations/xxxx_create_categories_table.php` et remplacer la méthode `up()` par : ```php public function up(): void { Schema::create('categories', function (Blueprint $table) { $table->id(); $table->string('name'); $table->timestamps(); }); } ``` ### Migration `tasks` Ouvrir `database/migrations/xxxx_create_tasks_table.php` et remplacer la méthode `up()` par : ```php public function up(): void { Schema::create('tasks', function (Blueprint $table) { $table->id(); $table->foreignId('category_id')->constrained('categories')->onDelete('cascade'); $table->string('title'); $table->boolean('done')->default(false); $table->date('due_date')->nullable(); $table->timestamps(); }); } ``` **Explication :** `constrained()->onDelete('cascade')` supprime les tâches d’une liste quand celle-ci est supprimée. > **Piège fréquent :** Créer la migration `tasks` APRÈS `categories`, sinon erreur de clé étrangère. ### Exécuter les migrations ```bash php artisan migrate ``` Vérifier dans phpMyAdmin : tables `categories` et `tasks`. --- **NEXT :** Créer les models avec les relations. --- ## 7. Models avec relations ### Créer les models ```bash php artisan make:model Category php artisan make:model Task ``` ### Model `Category` Fichier `app/Models/Category.php` : ```php hasMany(Task::class); } } ``` ### Model `Task` Fichier `app/Models/Task.php` : ```php 'boolean', 'due_date' => 'date', ]; public function category(): BelongsTo { return $this->belongsTo(Category::class); } } ``` **Explication :** `$fillable` autorise l’assignation de masse. `$casts` convertit `done` en booléen et `due_date` en date. --- **NEXT :** Créer les controllers CRUD. --- ## 8. Controllers CRUD ### Créer les controllers (ressources) ```bash php artisan make:controller CategoryController --resource php artisan make:controller TaskController --resource ``` **Pourquoi `--resource` ?** Génère automatiquement les 7 méthodes CRUD : index, create, store, show, edit, update, destroy. **Commande alternative (sans --resource) :** ```bash # php artisan make:controller CategoryController → controller vide, à remplir à la main ``` ### `CategoryController` Fichier `app/Http/Controllers/CategoryController.php` : ```php orderBy('created_at', 'desc')->get(); return view('categories.index', compact('categories')); } public function create() { return view('categories.create'); } public function store(Request $request) { $request->validate([ 'name' => 'required|string|max:255', ]); Category::create($request->only('name')); return redirect()->route('categories.index')->with('success', 'Catégorie créée !'); } public function show(Category $category) { $category->load('tasks'); return view('categories.show', compact('category')); } public function edit(Category $category) { return view('categories.edit', compact('category')); } public function update(Request $request, Category $category) { $request->validate([ 'name' => 'required|string|max:255', ]); $category->update($request->only('name')); return redirect()->route('categories.index')->with('success', 'Catégorie modifiée !'); } public function destroy(Category $category) { $category->delete(); return redirect()->route('categories.index')->with('success', 'Catégorie supprimée !'); } } ``` ### `TaskController` Fichier `app/Http/Controllers/TaskController.php` : ```php orderBy('created_at', 'desc') ->paginate(10); return view('tasks.index', compact('tasks')); } public function create(Request $request) { $categories = Category::orderBy('name')->get(); $categoryId = $request->query('category'); return view('tasks.create', compact('categories', 'categoryId')); } public function store(Request $request) { $request->validate([ 'category_id' => 'required|exists:categories,id', 'title' => 'required|string|max:255', 'done' => 'boolean', 'due_date' => 'nullable|date', ]); $data = $request->only('category_id', 'title', 'due_date'); $data['done'] = $request->boolean('done'); Task::create($data); return redirect()->route('tasks.index')->with('success', 'Tâche créée !'); } public function show(Task $task) { $task->load('category'); return view('tasks.show', compact('task')); } public function edit(Task $task) { $categories = Category::orderBy('name')->get(); return view('tasks.edit', compact('task', 'categories')); } public function update(Request $request, Task $task) { $request->validate([ 'category_id' => 'required|exists:categories,id', 'title' => 'required|string|max:255', 'done' => 'boolean', 'due_date' => 'nullable|date', ]); $data = $request->only('category_id', 'title', 'due_date'); $data['done'] = $request->boolean('done'); $task->update($data); return redirect()->route('tasks.index')->with('success', 'Tâche modifiée !'); } public function destroy(Task $task) { $task->delete(); return redirect()->route('tasks.index')->with('success', 'Tâche supprimée !'); } } ``` > **Piège fréquent :** Pour les checkboxes, utiliser `$request->boolean('done')` car une checkbox non cochée n’est pas envoyée dans la requête. --- **NEXT :** Déclarer les routes. --- ## 9. Routes Ouvrir `routes/web.php` et remplacer tout le contenu par : ```php route('categories.index'); }); Route::resource('categories', CategoryController::class); Route::resource('tasks', TaskController::class); ``` **Vérifier les routes :** ```bash php artisan route:list ``` --- **NEXT :** Créer les vues Blade. --- ## 10. Vues Blade – Layout ### Récapitulatif du projet (Model, Migration, Controller, Vue) | Élément | Où ? | C'est quoi ? | |--------|------|--------------| | **Model** | `app/Models/` | Représente une table. `$fillable` = colonnes modifiables. Relations : `hasMany`, `belongsTo`. | | **Migration** | `database/migrations/` | Fichier PHP qui crée/modifie les tables. `Schema::create()` définit les colonnes. | | **Controller** | `app/Http/Controllers/` | Reçoit la requête, lit/écrit via le Model, retourne une Vue. Méthodes : index, create, store, show, edit, update, destroy. | | **Vue Blade** | `resources/views/` | Template HTML avec `{{ }}` et `@if`. Hérite du layout via `@extends`. | **Flux :** Route → Controller → Model (DB) → Vue → HTML **Contenu des Migrations (résumé) :** `categories` = id, name, timestamps. `tasks` = id, category_id (FK), title, done, due_date, timestamps. **Contenu des Models (résumé) :** Category = $fillable ['name'], tasks() hasMany. Task = $fillable ['category_id','title','done','due_date'], $casts, category() belongsTo. **Contenu des Controllers (résumé) :** index→get+view, create→view, store→validate+create+redirect, show→load+view, edit→view, update→validate+update+redirect, destroy→delete+redirect. --- ### Créer le dossier et le layout ```bash mkdir resources\views\categories mkdir resources\views\tasks ``` ### Layout principal Fichier `resources/views/layouts/app.blade.php` : ```html
Aucune catégorie. Créez-en une !
@endforelseAucune tâche.
@endforelseAucune tâche.
@endforelseListe : {{ $task->category->name }}
Statut : {{ $task->done ? '✓ Fait' : 'Non fait' }}
@if($task->due_date)Échéance : {{ $task->due_date->format('d/m/Y') }}
@endif