Laravel - Trip Planner
Démarrer
Section titled “Démarrer”- Ce TP est le début du projet (noté)
- Votre repository git sera le rendu de votre projet.
- Vous allez vous inscrire sur Github Classroom (voir lien sur Moodle) et créer votre repository.
- Le nom de votre «team» doit être les noms des membres séparés par un tiret.
- Le repository est une installation Laravel vierge
(avec le sujet au format md en plus.sujet.md)
Activité pratique
Section titled “Activité pratique”Vous pouvez lire ce sujet directement dans VSCode en ouvrant ce fichier (ou sujet.md dans votre repo) et en cliquant droit «Open Preview»
Trip Planner
Section titled “Trip Planner”Le but de cette activité est de réaliser une application web de planification de voyages.
Vous allez créer :
- Une page d’accueil affichant un texte de bienvenue et les 3 derniers voyages
- Une page Voyages, affichant la liste des voyages + une barre de recherche
- Une page Détail d’un voyage, avec le coût total (calculé dynamiquement)
- Une page Contact avec un formulaire
- Toutes les pages utilisent le même header et footer (layout Blade)
Contraintes :
- Laravel 11.x min
- Base de données SQLite (pas de MySQL)
- Eloquent (pas de SQL brut sauf justification)
- Validation avec FormRequest
- Logique métier hors contrôleurs
Mise en place du projet
Section titled “Mise en place du projet”Deux «environnements» possibles: (vous pouvez utiliser les deux simultanément)
1 - Avec Github Codespace
Section titled “1 - Avec Github Codespace”- Soit dans le navigateur:
- utiliser le bouton Open in Github Codespaces
- Soit dans VSCode (après avoir créé le codespace)
- bouton vert
<> Code - onglet Codespaces
- -> on current branch -> les 3 petits points …
- Open in Visual Studio Code
- bouton vert
2 - En local
Section titled “2 - En local”-
Ouvrez un terminal et placez vous dans un répertoire où vous souhaitez récupérer le projet.
-
Cloner le projet (Changer VOTRE-TEAM)
Terminal window git clone git@github.com:MIASHS-UGA-PWS/projet-VOTRE-TEAM.git -
Vous aurez besoin d’installer Composer
Dans les deux cas
Section titled “Dans les deux cas”-
(La première fois) Installer les librairies + configuration de base
Terminal window composer run setup- Cette commande remplace les commandes suivantes:
on «créer» un.env(cp .env.example .env)on installe les dépendances du projet (composer install)on installe les dépendances front-end (npm install && npm run build)on genere une clé d’application (php artisan key:generate)on crée la base de donnée sqlite (touch database/database.sqlite)on lance les migrations (php artisan migrate)
- Cette commande remplace les commandes suivantes:
-
Lancer le serveur
Terminal window composer run dev
Si tout est ok, votre application est accessible ici: http://localhost:8000
Création de routes, contrôleurs et vues
Section titled “Création de routes, contrôleurs et vues”Ouvrez le répertoire du projet dans votre éditeur de code (Visual Studio Code, Sublime, PHPStorm, etc.) et prenez un moment pour observer la structure de fichiers de l’application Laravel.
Pour rappel:
- Les routes ( = comment traiter les URLs demandées par un visiteur du site ) sont gérées dans le fichier
/routes/web.php/ - Les contrôleurs seront placés dans le répertoire
/app/Http/Controllers/ - Les modèles sont placés où vous voulez, par défaut dans le répertoire
/app/Models/ - Les vues sont placées dans le répertoire
/resources/views/
Exercice 1 - Routes / contrôleurs de base
Section titled “Exercice 1 - Routes / contrôleurs de base”-
Dans
routes/web.php, remplacez la route d’accueil par un contrôleur:routes/web.php Route::get('/', function () {return view('welcome');});use App\Http\Controllers\HomeController;Route::get('/', [HomeController::class, 'index']);L’ancien code “disait”: Lorsqu’un visiteur du site arrive sur l’URL ”/” (Soit ici http://localhost:8000/ ), alors renvoi la vue
welcome(qui correspond au fichier/resources/views/welcome.blade.php/)Le nouveau code “dit”: Lorsqu’un visiteur du site arrive sur l’URL ”/”, appelle la fonction
indexdu contrôleur qui s’appelleHomeController. -
Créez le contrôleur:
Terminal window php artisan make:controller HomeController -
Dans
HomeController, faites pointer l’actionindexvers une vue.app/Http/Controllers/HomeController.php 6 collapsed lines<?phpnamespace App\Http\Controllers;use Illuminate\Http\Request;class HomeController extends Controller{public function index(){return view('home');}}
Exercice 2 - Layout global
Section titled “Exercice 2 - Layout global”- Créez un layout commun:
- Créez le fichier
/resources/views/layouts/main.blade.php/(il faut créer le répertoire “layouts”) - Puis placez le code d’exemple (lien ou code ci-dessous) suivant dans ce fichier: https://gist.github.com/flody/6cbe4e2d1e134df1e14be21ed403ac30
- Créez le fichier
<!DOCTYPE html><html lang="fr"> <head> <title>@yield('title', 'Trip Planner')</title> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <meta name="description" content="@yield('meta_description', 'Trip Planner - application de planification de voyages')">
{{-- Template pédagogique : styles via CDN (Bulma / Font Awesome) pour éviter de rajouter de la configuration front (npm, Vite, Tailwind…) et rester focalisé sur Laravel / Blade / Eloquent. --}} <link rel="stylesheet" href="https://rsms.me/inter/inter.css"> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@1.0.4/css/bulma.min.css"> <link rel="icon" type="image/svg+xml" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Cg fill='none' stroke-linecap='round' stroke-linejoin='round'%3E%3Cg stroke='%23fff' stroke-width='8'%3E%3Cpath d='M58 12 8 33l18 7 7 16 7-20 18-24z'/%3E%3Cpath d='M26 40 58 12'/%3E%3Cpath d='M8 33l18 7 7 16'/%3E%3C/g%3E%3Cg stroke='%23D33682' stroke-width='4'%3E%3Cpath d='M58 12 8 33l18 7 7 16 7-20 18-24z'/%3E%3Cpath d='M26 40 58 12'/%3E%3Cpath d='M8 33l18 7 7 16'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E" /> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css" integrity="sha512-DTOQO9RWCH3ppGqcWaEA1BIZOC6xxalwEsw9c2QQeAIftl+Vegovlnee1c9QX4TctnWMn13TZye+giMm8e2LwA==" crossorigin="anonymous" referrerpolicy="no-referrer" /> </head>
<body> <nav class="navbar py-4" role="navigation" aria-label="main navigation"> <div class="container"> <div class="navbar-brand"> <a class="navbar-item" href="/" title="Accueil"> <span class="icon"> <i class="fa-solid fa-map-location-dot" style="font-size:26px; width:26px"></i> </span> <span class="ml-2 has-text-weight-semibold">Trip Planner</span> </a>
<a class="navbar-burger" role="button" aria-label="menu" aria-expanded="false" data-target="navbarBasicExample"> <span aria-hidden="true"></span> <span aria-hidden="true"></span> <span aria-hidden="true"></span> </a> </div>
<div id="navbarBasicExample" class="navbar-menu"> <div class="navbar-start"> <a class="navbar-item" href="/">Home</a> <a class="navbar-item" href="/trips">Voyages</a> <a class="navbar-item" href="/contact">Contact</a> </div>
<div class="navbar-end"> {{-- Optionnel: barre de recherche sur la page /trips --}} <div class="navbar-item"> <form method="GET" action="/trips"> <div class="field has-addons"> <div class="control"> <input class="input" type="search" name="q" placeholder="Rechercher un voyage" value="{{ request('q') }}"> </div> <div class="control"> <button class="button" type="submit" aria-label="Rechercher"> <span class="icon"><i class="fa-solid fa-magnifying-glass"></i></span> </button> </div> </div> </form> </div>
{{-- Optionnel: liens auth (si Breeze installé) --}} {{-- @auth <div class="navbar-item">Bonjour {{ auth()->user()->name }}</div> <div class="navbar-item"> <form method="POST" action="{{ route('logout') }}"> @csrf <button class="button is-light" type="submit">Logout</button> </form> </div> @else <a class="navbar-item" href="{{ route('login') }}">Login</a> <a class="navbar-item" href="{{ route('register') }}">Register</a> @endauth --}} </div> </div> </div> </nav>
<section class="section"> <div class="container"> <div class="mb-6 columns is-multiline is-centered"> <div class="column is-9 has-text-centered"> <span class="has-text-grey-dark">@yield('subtitle', 'Planifiez vos voyages')</span> <h1 class="mt-2 mb-4 is-size-1 is-size-3-mobile has-text-weight-bold"> @yield('header', 'Trip Planner') </h1> <p class="subtitle has-text-grey"> @yield('description', 'Organisez destinations, activités, hébergements et budget en un seul endroit.') </p> </div> </div>
@if (session('status')) <div class="notification is-success is-light"> {{ session('status') }} </div> @endif
@yield('content')
{{-- Exemple de layout pour 3 voyages --}} {{--
<div class="columns is-multiline"> <div class="column is-4 mb-5"> <span><small class="has-text-grey-dark">12 mar 2026 — 18 mar 2026</small></span> <h2 class="mt-2 mb-2 is-size-3 is-size-4-mobile has-text-weight-bold">Lisbonne</h2> <p class="subtitle has-text-grey">6 jours / 2 personnes</p> <p class="subtitle has-text-grey">Budget estimé : 750€</p> <a href="#">Voir le détail</a> </div> <div class="column is-4 mb-5"> <span><small class="has-text-grey-dark">02 avr 2026 — 10 avr 2026</small></span> <h2 class="mt-2 mb-2 is-size-3 is-size-4-mobile has-text-weight-bold">Tokyo</h2> <p class="subtitle has-text-grey">9 jours / 1 personne</p> <p class="subtitle has-text-grey">Budget estimé : 2200€</p> <a href="#">Voir le détail</a> </div> <div class="column is-4 mb-5"> <span><small class="has-text-grey-dark">15 mai 2026 — 22 mai 2026</small></span> <h2 class="mt-2 mb-2 is-size-3 is-size-4-mobile has-text-weight-bold">Reykjavik</h2> <p class="subtitle has-text-grey">8 jours / 4 personnes</p> <p class="subtitle has-text-grey">Budget estimé : 3400€</p> <a href="#">Voir le détail</a> </div> </div>
--}} </div> </section>
<footer class="footer"> <div class="content has-text-centered"> <p> <strong>Trip Planner</strong> — Laravel — SQLite </p> </div> </footer>
<script> // Burger menu Bulma document.addEventListener('DOMContentLoaded', () => { const burgers = Array.prototype.slice.call(document.querySelectorAll('.navbar-burger'), 0); burgers.forEach((el) => { el.addEventListener('click', () => { const target = el.dataset.target; const $target = document.getElementById(target); el.classList.toggle('is-active'); $target.classList.toggle('is-active'); }); }); }); </script> </body></html>-
Puis adaptez votre vue d’accueil
ressources/views/home.blade.phppour utiliser:-
@extends('layouts/main') -
une section
content -
exemple:
resources/views/home.blade.php @extends('layouts/main')@section('content')<h1>Bienvenue sur le Trip Planner</h1><p>Planifiez vos voyages facilement!</p>@endsection
-
-
Vous pouvez vérifier que le layout est bien appliqué en rafraîchissant la page d’accueil.
Exercice:
- Ajoutez une page
/contactavec un formulaire (nom, email, message) et une page/trips(liste des voyages statique pour l’instant)- Créez un contrôleur
ContactControlleretTripController - Ajoutez les routes
- Créez une vue
resources/views/contact.blade.phpetresources/views/trips.blade.php - Utilisez le layout commun
- Affichez le formulaire dans la vue
contact.blade.php - Pour l’instant, le formulaire n’a pas besoin de fonctionner.
- Pour la page
/homeet/ou/trips, vous pouvez prendre dans le code du layout (/layouts/main.blade.php/) le code exemple en commentaire affichant 3 voyages et l’inclure dans le votre@section('content')de la page. (Vous pouvez ensuite supprimer ce code en commentaire du layout)
- Créez un contrôleur
Base de données (SQLite) et migrations
Section titled “Base de données (SQLite) et migrations”Vous allez maintenant stocker les voyages et leurs éléments dans SQLite.
Nous allons utiliser une base de données pour stocker les voyages, utilisateurs et demandes de contact.
Les tables seront créées à l’aide des migrations de Laravel: https://laravel.com/docs/master/migrations
Les migrations permettent d’effectuer des modifications successives et ordonnées sur une base de données, comme par exemple:
- Créer un base si elle n’existe pas
- Créer une nouvelle table avec certaines colonnes
- Modifier cette table
- Ajouter une autre table
- Effacer une table
- etc.
Il est aussi possible de revenir en arrière pour chaque étape ou de réinitialiser toute la base depuis le début.
Laravel est déjà configuré pour utiliser SQLite (fichier database/database.sqlite).
Vous pouvez modifiez le fichier .env pour changer la configuration de la base de données si besoin.
# DB_DATABASE=/ABSOLUTE_PATH/database/database.sqlite# DB_CONNECTION=mysql# DB_HOST=127.0.0.1# DB_PORT=3306# DB_DATABASE=larasite# DB_USERNAME=root# DB_PASSWORD=ic2aNormalement votre application peut maintenant établir une connexion avec la base de données.
Laravel est livré avec des migrations basiques pour créer une table utilisateurs.
Pour lancer les migrations de base, dans le terminal, lancez:
php artisan migrateSi vous n’avez pas d’erreur, les migrations sont lancées et les tables créées.
Vous pouvez vérifiez que c’est bien le cas avec les outils phpLiteAdmin ou SQLite studio ou VScode (voir indication ci-dessous)
Exercice - Créer les modèles et les migrations
Section titled “Exercice - Créer les modèles et les migrations”-
Le modèle et la migration pour user existe déjà. Vous pouvez aller voir dans
/app/Models/User.phpet/database/migrations/0001_01_01_000000_create_users_table.php -
Créez le modèle et la migration pour Trip:
Terminal window php artisan make:model Trip -m -
Ouvrez la migration créée dans
/database/migrations/et ajoutez les colonnes suivantes:user_id(clé étrangère vers users)title(string)description(text, nullable)starts_at(date)ends_at(date)people_count(integer)
Voici un exemple de code pour la migration:
database/migrations/xxxx_xx_xx_xxxxxx_create_trips_table.php 13 collapsed lines<?phpuse Illuminate\Database\Migrations\Migration;use Illuminate\Database\Schema\Blueprint;use Illuminate\Support\Facades\Schema;return new class extends Migration{/*** Run the migrations.*/public function up(): void{Schema::create('trips', function (Blueprint $table) {$table->id();$table->string('title');$table->text('description')->nullable();$table->date('starts_at');$table->date('ends_at');$table->unsignedInteger('people_count')->default(1);$table->foreignId('user_id')->constrained()->cascadeOnDelete();$table->timestamps();});}9 collapsed lines/*** Reverse the migrations.*/public function down(): void{Schema::dropIfExists('trips');}};Relancer une migration et vérifier vos tables
Exercice - Lier les modèles (Créer les relations Eloquent)
Section titled “Exercice - Lier les modèles (Créer les relations Eloquent)”Eloquent permet de mettre en relation des entités: https://laravel.com/docs/master/eloquent-relationships
Sur notre site, un trip appartient à un user et un user peut avoir zéro ou plusieurs trips.
Pour renseigner cette relation, ouvrez le modèle User.php et ajoutez la méthode suivante:
9 collapsed lines
<?php
namespace App\Models;
// use Illuminate\Contracts\Auth\MustVerifyEmail;use Illuminate\Database\Eloquent\Factories\HasFactory;use Illuminate\Foundation\Auth\User as Authenticatable;use Illuminate\Database\Eloquent\Relations\HasMany;use Illuminate\Notifications\Notifiable;
class User extends Authenticatable37 collapsed lines
{ /** @use HasFactory<\Database\Factories\UserFactory> */ use HasFactory, Notifiable;
/** * The attributes that are mass assignable. * * @var list<string> */ protected $fillable = [ 'name', 'email', 'password', ];
/** * The attributes that should be hidden for serialization. * * @var list<string> */ protected $hidden = [ 'password', 'remember_token', ];
/** * Get the attributes that should be cast. * * @return array<string, string> */ protected function casts(): array { return [ 'email_verified_at' => 'datetime', 'password' => 'hashed', ]; }
public function trips(): HasMany { return $this->hasMany(Trip::class); }}Et le modèle Trip.php:
<?phpnamespace App\Models;6 collapsed lines
use Illuminate\Database\Eloquent\Model;use Illuminate\Database\Eloquent\Factories\HasFactory;use Illuminate\Database\Eloquent\Relations\BelongsTo;use Illuminate\Database\Eloquent\Relations\HasMany;
class Trip extends Model{ use HasFactory;
public function user(): BelongsTo { return $this->belongsTo(User::class); }}A partir de maintenant, vous pouvez accéder au user d’un trip ou les trips d’un user facilement (dans un controller par exemple):
- $trip->user
- $user->trips
$trip = Trip::find(1); // récupère le trip avec l'id 1echo $trip->user->name; // récupère le nom de l'utilisateur du trip
$user = User::find(1); // récupère l'utilisateur avec l'id 1foreach ($user->trips as $trip) { echo $trip->title; // affiche le titre de chaque trip de l'utilisateur}Exercice - Ajout de données
Section titled “Exercice - Ajout de données”Laravel offre aussi un système pour “remplir” votre base de données avec de fausses données, pour pouvoir effectuer des tests plus facilement: https://laravel.com/docs/master/seeding
Utilisez la documentation pour créer un utilisateur par défaut. N’utilisez pas de DB::table('users')->insert mais utilisez des factories.
Vous pouvez réinitialiser votre base de données et lancer l’insertion de données avec la commande:
php artisan migrate:fresh --seedVérifier que l’utilisateur a bien été créé dans la base de données. Voir database/DatabaseSeeder.php
Créer une factory pour le modèle Trip et insérer 5 trips dans la base de données lors du seeding. https://laravel.com/docs/master/eloquent-factories
php artisan make:factory TripFactory --model=TripLa librairie faker vous permet de créer de la donnée. Voir la documentation ici: https://fakerphp.github.io/formatters/
Dans database/factories/TripFactory.php, vous devrez compléter la fonction definition() avec les colonnes de la table trip à remplir. Voici une définition possible:
15 collapsed lines
<?php
namespace Database\Factories;
use App\Models\User;use Illuminate\Database\Eloquent\Factories\Factory;
/** * @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Trip> */class TripFactory extends Factory{ /** * Define the model's default state. * * @return array<string, mixed> */ public function definition(): array { $start = $this->faker->dateTimeBetween('now', '+6 months'); $end = (clone $start); $end->modify('+' . $this->faker->numberBetween(2, 14) . ' days');
return [ 'title' => $this->faker->city(), 'description' => $this->faker->optional()->paragraph(), 'starts_at' => $start->format('Y-m-d'), 'ends_at' => $end->format('Y-m-d'), 'people_count' => $this->faker->numberBetween(1, 6), 'user_id' => User::factory(), ]; }}Ensuite, dans database/seeders/DatabaseSeeder.php, ajoutez la création de 5 trips:
11 collapsed lines
<?php namespace Database\Seeders; use Illuminate\Database\Seeder; use App\Models\Trip; class DatabaseSeeder extends Seeder { /** * Seed the application's database. * * @return void */ public function run(): void { // \App\Models\User::factory(10)->create();
Trip::factory(5)->create();
//// Ou //// // $user = \App\Models\User::factory()->create([ // 'name' => 'Test User', // 'email' => 'test@example.com', // ]);
// Trip::factory()->count(5)->for($user)->create();
//// Ou //// // \App\Models\User::factory(10)->hasTrips(3)->create(); } }Doc du seeder https://laravel.com/docs/master/seeding
-
En option:
Si vous souhaitez des données en français vous pouvez configurer la langue de faker. Dans le fichier
config/app.phpvous pouvez modifierfaker_localeenfr_FR.
Affichage des voyages
Section titled “Affichage des voyages”Maintenant que vous avez des voyages dans la base de données, vous pouvez les afficher dans la vue /trips.
-
Pour récupérer des données d’un modèle, vous pouvez utiliser les méthodes Eloquent.Par exemple, pour récupérer tous les voyages:
$trips = \App\Models\Trip::all(); -
Pour passer des données à une vue, vous pouvez utiliser la méthode
view()dans un contrôleur:return view('trips', ['trips' => $trips]); -
Dans la vue, vous pouvez utiliser Blade pour afficher les données:
@foreach ($trips as $trip)<h2>{{ $trip->title }}</h2><p>{{ $trip->description }}</p>@endforeach
Exercice - Afficher les voyages
Section titled “Exercice - Afficher les voyages”Utilisez ce code pour compléter l’affichage des voyages sur la page d’accueil:
- Seuls les 3 derniers voyages doivent être affichés (ordre décroissant par date de début)
- Pour l’affichage vous pouvez vous inspirer du code en commentaire dans le layout (
/resources/views/layouts/main.blade.php/) - Chaque titre de voyage doit être un lien vers la page de détail du voyage (
/trips/{id}/)
Exercice - Page détail d’un voyage
Section titled “Exercice - Page détail d’un voyage”Lorsqu’un utilisateur clique sur un voyage, il doit être redirigé vers une page de détail du voyage.
-
Créez une route
/trips/{id}dansroutes/web.phpuse App\Http\Controllers\TripController;Route::get('/trips/{id}', [TripController::class, 'show'])->name('trips.show'); -
Dans le contrôleur
TripController, créez la méthodeshowqui récupère le voyage par son ID et passe les données à la vuetrips/show.blade.phppublic function show($id){$trip = Trip::findOrFail($id);return view('trips.show', ['trip' => $trip]);} -
Créez la vue
resources/views/trips/show.blade.phppour afficher les détails du voyage (titre, description, dates, nombre de personnes) -
Affichez également le nom de l’utilisateur qui a créé le voyage (relation Eloquent)
-
Réutilisez le layout commun
Exercice - Formulaire de contact
Section titled “Exercice - Formulaire de contact”En utilisant le cours et la documentation Laravel, réalisez la page de contact (Formulaire + enregistrement en base de données) : https://laravel.com/docs/master/csrf et https://laravel.com/docs/master/validation
Vous devrez probablement ajouter des colonnes dans votre table contact (modifier la migration)
Vous devez implémenter la validation des données comme présentée dans le cours.
Exercice - Le CRUD des voyages
Section titled “Exercice - Le CRUD des voyages”Laravel fournit un moyen simple de créer des opérations CRUD (Create, Read, Update, Delete) pour un modèle en utilisant les ressources de contrôleur: https://laravel.com/docs/master/controllers#resource-controllers
Créer un contrôleur de ressource d’administration des voyages:
php artisan make:controller TripController --resourceL’url d’administration des voyages doit être /admin/trips
Les routes doivent être définies dans routes/web.php:
use App\Http\Controllers\Admin\TripController;Route::resource('/admin/trips', TripController::class);Vous pouvez aussi lié vos contrôleurs à vos models (voir cours Route model binding)
Exercice supplémentaire
Section titled “Exercice supplémentaire”Modélisation minimale des autres entités
Section titled “Modélisation minimale des autres entités”Modélisez les autres entités (Destination, Activity, Accommodation, Transport) avec leurs relations Eloquent et migrations.
On part sur un modèle simple et réalisable:
- Un utilisateur possède des voyages
- Un voyage possède des destinations
- Une destination possède des activités
- Une destination possède des hébergements
- Un voyage possède des transports
Schéma (relations):
- User hasMany Trip
- Trip belongsTo User
- Trip hasMany Destination
- Destination belongsTo Trip
- Destination hasMany Activity
- Activity belongsTo Destination
- Destination hasMany Accommodation
- Accommodation belongsTo Destination
- Trip hasMany Transport
- Transport belongsTo Trip
Voici un schéma complet possible:
Exercice - Créer les migrations et modèles
Section titled “Exercice - Créer les migrations et modèles”Créez les modèles + migrations:
php artisan make:model Trip -mphp artisan make:model Destination -mphp artisan make:model Activity -mphp artisan make:model Accommodation -mphp artisan make:model Transport -mDans vos migrations, ajoutez:
-
trips
title(string)description(text nullable)starts_at(date)ends_at(date)people_count(integer)user_id(foreign key)
-
destinations
trip_id(foreign key)name(string)starts_at(date nullable)ends_at(date nullable)
-
activities
destination_id(foreign key)title(string)day(date nullable)duration_minutes(integer nullable)price_per_person(integer) / (ou decimal)
-
accommodations
destination_id(foreign key)title(string)nights(integer)price_per_night(integer) / (ou decimal)capacity(integer)
-
transports
trip_id(foreign key)type(string) (ex: train, avion, voiture)pricing_type(string) (ex: per_person / fixed)price(integer) / (ou decimal)
Lancez vos migrations:
Terminal window php artisan migrate
Eloquent: relations + affichage de données
Section titled “Eloquent: relations + affichage de données”Exercice - Relations Eloquent
Section titled “Exercice - Relations Eloquent”Dans les modèles, ajoutez les relations (hasMany/belongsTo).
Faites ensuite un premier affichage “réel”:
- Page
/trips: liste des voyages en base - Page
/trips/{id}: détail d’un voyage + destinations
Données factices: factories et seeders
Section titled “Données factices: factories et seeders”Objectif: un simple
php artisan migrate:fresh --seeddoit produire une application fonctionnelle et visitable.
Exercice - Factories
Section titled “Exercice - Factories”Créez des factories:
php artisan make:factory TripFactoryphp artisan make:factory DestinationFactoryphp artisan make:factory ActivityFactoryphp artisan make:factory AccommodationFactoryphp artisan make:factory TransportFactoryGénérez au moins:
- 2 users
- 5 trips
- pour chaque trip: 1-3 destinations
- pour chaque destination: 1-5 activities et 0-2 accommodations
- pour chaque trip: 0-2 transports
Fonctionnalité centrale: calcul du coût total (dynamique)
Section titled “Fonctionnalité centrale: calcul du coût total (dynamique)”Exercice - Calcul dynamique
Section titled “Exercice - Calcul dynamique”Dans le modèle Trip, implémentez une méthode
public function totalCost(): int|float
Règles de calcul:
- activités: somme(
activity.price_per_person*trip.people_count) - hébergements: somme(
accommodation.price_per_night*accommodation.nights) - transports:
- si
pricing_type=per_person=>price*people_count - si
pricing_type=fixed=>price
- si
Affichez ce total sur la page détail d’un voyage.
Interdit de stocker le total en base sans justification.
(Option) Contraintes métier “intéressantes”
Section titled “(Option) Contraintes métier “intéressantes””- Une activité ne peut pas être planifiée en dehors des dates du voyage.
people_countne doit pas dépasser la capacité d’un hébergement.
Vous pouvez implémenter ces règles via validation (FormRequest) et/ou logique métier.