Skip to content

Laravel - Trip Planner

  • 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).

Vous pouvez lire ce sujet directement dans VSCode en ouvrant ce fichier (ou sujet.md dans votre repo) et en cliquant droit «Open Preview»

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

Deux «environnements» possibles: (vous pouvez utiliser les deux simultanément)

  • 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
  • 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

  • (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)
  • Lancer le serveur

    Terminal window
    composer run dev

Si tout est ok, votre application est accessible ici: http://localhost:8000

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”
  1. 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 index du contrôleur qui s’appelle HomeController.

  2. Créez le contrôleur:

    Terminal window
    php artisan make:controller HomeController
  3. Dans HomeController, faites pointer l’action index vers une vue.

    app/Http/Controllers/HomeController.php
    6 collapsed lines
    <?php
    namespace App\Http\Controllers;
    use Illuminate\Http\Request;
    class HomeController extends Controller
    {
    public function index()
    {
    return view('home');
    }
    }
<!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.php pour 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 /contact avec un formulaire (nom, email, message) et une page /trips (liste des voyages statique pour l’instant)
    • Créez un contrôleur ContactController et TripController
    • Ajoutez les routes
    • Créez une vue resources/views/contact.blade.php et resources/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 /home et/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)

Vous allez maintenant stocker les voyages et leurs éléments dans SQLite.

img

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.

.env
# 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=ic2a

Normalement 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:

Terminal window
php artisan migrate

Si 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”
  1. Le modèle et la migration pour user existe déjà. Vous pouvez aller voir dans /app/Models/User.php et /database/migrations/0001_01_01_000000_create_users_table.php

  2. Créez le modèle et la migration pour Trip:

    Terminal window
    php artisan make:model Trip -m
  3. 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
    <?php
    use 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:

app/Models/User.php
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 Authenticatable
37 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:

app/Models/Trip.php
<?php
namespace 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 1
echo $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 1
foreach ($user->trips as $trip) {
echo $trip->title; // affiche le titre de chaque trip de l'utilisateur
}

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:

Terminal window
php artisan migrate:fresh --seed

Vé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

Terminal window
php artisan make:factory TripFactory --model=Trip

La 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:

database/factories/TripFactory.php
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:

database/seeders/DatabaseSeeder.php
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

  1. En option:

    Si vous souhaitez des données en français vous pouvez configurer la langue de faker. Dans le fichier config/app.php vous pouvez modifier faker_locale en fr_FR.

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

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}/)

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} dans routes/web.php

    use App\Http\Controllers\TripController;
    Route::get('/trips/{id}', [TripController::class, 'show'])->name('trips.show');
  • Dans le contrôleur TripController, créez la méthode show qui récupère le voyage par son ID et passe les données à la vue trips/show.blade.php

    public function show($id)
    {
    $trip = Trip::findOrFail($id);
    return view('trips.show', ['trip' => $trip]);
    }
  • Créez la vue resources/views/trips/show.blade.php pour 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

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.

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:

Terminal window
php artisan make:controller TripController --resource

L’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)

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:

img

Exercice - Créer les migrations et modèles

Section titled “Exercice - Créer les migrations et modèles”

Créez les modèles + migrations:

Terminal window
php artisan make:model Trip -m
php artisan make:model Destination -m
php artisan make:model Activity -m
php artisan make:model Accommodation -m
php artisan make:model Transport -m

Dans vos migrations, ajoutez:

  1. trips

    • title (string)
    • description (text nullable)
    • starts_at (date)
    • ends_at (date)
    • people_count (integer)
    • user_id (foreign key)
  2. destinations

    • trip_id (foreign key)
    • name (string)
    • starts_at (date nullable)
    • ends_at (date nullable)
  3. activities

    • destination_id (foreign key)
    • title (string)
    • day (date nullable)
    • duration_minutes (integer nullable)
    • price_per_person (integer) / (ou decimal)
  4. accommodations

    • destination_id (foreign key)
    • title (string)
    • nights (integer)
    • price_per_night (integer) / (ou decimal)
    • capacity (integer)
  5. 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”

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

Objectif: un simple

Terminal window
php artisan migrate:fresh --seed

doit produire une application fonctionnelle et visitable.

Créez des factories:

Terminal window
php artisan make:factory TripFactory
php artisan make:factory DestinationFactory
php artisan make:factory ActivityFactory
php artisan make:factory AccommodationFactory
php artisan make:factory TransportFactory

Gé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)”

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

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_count ne 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.