TP4 : Système de Covoiturage Objectif : Concevoir et implémenter une API pour un service de
covoiturage. • Entités principales : o Conducteur (id, nom, email, téléphone, véhicule) o
Véhicule (id, marque, modèle, immatriculation, nombre de places) o Trajet (id, lieu de départ,
destination, date, heure, idConducteur) o Passager (id, nom, email, téléphone) o Réservation
(id, idTrajet, idPassager, statut : confirmée/annulée) • Relations : o Un Conducteur propose
plusieurs Trajets. o Un Passager peut réserver plusieurs Trajets. • Exigences : o CRUD pour
chaque entité. o Implémenter un filtre de recherche de trajet par date et lieu de départ. o Vérifier
la disponibilité des places avant chaque réservation. • Livrables : o Documentation complète
sous Word (MCD, MLD, workflow, explication de l’application). o Code source propre
respectant la structure du projet vue en classe. o Bonus pour ceux qui ajoutent un front-end
permettant la réservation en ligne.
ChatGPT a dit :
Mémoire mise à jour
Ça a l'air d'un projet intéressant ! Tu veux commencer par quoi ? La conception de la base de
données (MCD, MLD), l’implémentation de l’API, ou bien un plan global du projet ?
4o
Vous avez dit :
on suivra cet ordre
ChatGPT a dit :
Parfait ! On va donc commencer par la conception de la base de données.
1️⃣ Modèle Conceptuel de Données (MCD)
Le MCD représente graphiquement les entités, leurs attributs et leurs relations.
Les entités principales et leurs relations sont les suivantes :
• Conducteur (1,1) --- propose --- (0,n) Trajet
• Trajet (1,1) --- concerne --- (0,n) Réservation
• Passager (1,1) --- effectue --- (0,n) Réservation
• Véhicule (1,1) --- appartient --- (1,1) Conducteur
On peut modéliser cela sous forme de diagramme UML ou avec un outil comme DB Designer.
2️⃣ Modèle Logique de Données (MLD)
Le MLD traduit le MCD en relations SQL :
sql
CopierModifier
CREATE TABLE Conducteur (
id SERIAL PRIMARY KEY,
nom VARCHAR(100),
email VARCHAR(100) UNIQUE,
telephone VARCHAR(15) UNIQUE
);
CREATE TABLE Vehicule (
id SERIAL PRIMARY KEY,
marque VARCHAR(50),
modele VARCHAR(50),
immatriculation VARCHAR(20) UNIQUE,
nombre_places INT CHECK (nombre_places > 0),
idConducteur INT UNIQUE,
FOREIGN KEY (idConducteur) REFERENCES Conducteur(id) ON DELETE CASCADE
);
CREATE TABLE Trajet (
id SERIAL PRIMARY KEY,
lieu_depart VARCHAR(100),
destination VARCHAR(100),
date DATE,
heure TIME,
idConducteur INT,
FOREIGN KEY (idConducteur) REFERENCES Conducteur(id) ON DELETE CASCADE
);
CREATE TABLE Passager (
id SERIAL PRIMARY KEY,
nom VARCHAR(100),
email VARCHAR(100) UNIQUE,
telephone VARCHAR(15) UNIQUE
);
CREATE TABLE Reservation (
id SERIAL PRIMARY KEY,
idTrajet INT,
idPassager INT,
statut VARCHAR(20) CHECK (statut IN ('confirmée', 'annulée')),
FOREIGN KEY (idTrajet) REFERENCES Trajet(id) ON DELETE CASCADE,
FOREIGN KEY (idPassager) REFERENCES Passager(id) ON DELETE CASCADE
);
Dis-moi si tu veux modifier ou compléter quelque chose avant de passer à la partie API !
4o
Vous avez dit :
c est avec entity framework
ChatGPT a dit :
Mémoire mise à jour
D'accord ! Avec Entity Framework, on va créer les entités C# et configurer les relations via
Fluent API ou les Data Annotations.
1️⃣ Définition des Entités
csharp
CopierModifier
public class Conducteur
public int Id { get; set; }
public string Nom { get; set; }
public string Email { get; set; }
public string Telephone { get; set; }
// Relation One-to-One avec Véhicule
public Vehicule Vehicule { get; set; }
// Relation One-to-Many avec Trajet
public ICollection<Trajet> Trajets { get; set; }
public class Vehicule
public int Id { get; set; }
public string Marque { get; set; }
public string Modele { get; set; }
public string Immatriculation { get; set; }
public int NombrePlaces { get; set; }
public int ConducteurId { get; set; }
public Conducteur Conducteur { get; set; }
public class Trajet
public int Id { get; set; }
public string LieuDepart { get; set; }
public string Destination { get; set; }
public DateTime Date { get; set; }
public int ConducteurId { get; set; }
public Conducteur Conducteur { get; set; }
public ICollection<Reservation> Reservations { get; set; }
public class Passager
public int Id { get; set; }
public string Nom { get; set; }
public string Email { get; set; }
public string Telephone { get; set; }
public ICollection<Reservation> Reservations { get; set; }
public class Reservation
public int Id { get; set; }
public int TrajetId { get; set; }
public Trajet Trajet { get; set; }
public int PassagerId { get; set; }
public Passager Passager { get; set; }
public string Statut { get; set; } // "confirmée" ou "annulée"
2️⃣ Configuration via Fluent API (dans DbContext)
csharp
CopierModifier
protected override void OnModelCreating(ModelBuilder modelBuilder)
modelBuilder.Entity<Conducteur>()
.HasOne(c => c.Vehicule)
.WithOne(v => v.Conducteur)
.HasForeignKey<Vehicule>(v => v.ConducteurId);
modelBuilder.Entity<Conducteur>()
.HasMany(c => c.Trajets)
.WithOne(t => t.Conducteur)
.HasForeignKey(t => t.ConducteurId);
modelBuilder.Entity<Trajet>()
.HasMany(t => t.Reservations)
.WithOne(r => r.Trajet)
.HasForeignKey(r => r.TrajetId);
modelBuilder.Entity<Passager>()
.HasMany(p => p.Reservations)
.WithOne(r => r.Passager)
.HasForeignKey(r => r.PassagerId);
modelBuilder.Entity<Reservation>()
.Property(r => r.Statut)
.HasDefaultValue("confirmée");
3️⃣ Migration et Création de la Base
Dans la console du gestionnaire de package :
powershell
CopierModifier
Add-Migration InitialCreate
Update-Database
Tout est prêt pour commencer les opérations CRUD avec Entity Framework Core !
Tu veux qu'on enchaîne avec les contrôleurs et services pour l’API REST ?
4o
Vous avez dit :
ok etape suivante
ChatGPT a dit :
3️⃣ Implémentation de l'API avec les Contrôleurs
On va maintenant créer les contrôleurs pour chaque entité en utilisant ASP.NET Core et Entity
Framework Core.
Création des Services et du DbContext
D'abord, ajoutons le DbContext à notre application dans Program.cs :
csharp
CopierModifier
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
Et définissons notre ApplicationDbContext :
csharp
CopierModifier
public class ApplicationDbContext : DbContext
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) :
base(options) { }
public DbSet<Conducteur> Conducteurs { get; set; }
public DbSet<Vehicule> Vehicules { get; set; }
public DbSet<Trajet> Trajets { get; set; }
public DbSet<Passager> Passagers { get; set; }
public DbSet<Reservation> Reservations { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
// Configuration Fluent API déjà définie ici
CRUD des Conducteurs (Exemple d'un contrôleur)
On utilise un Controller API pour gérer les conducteurs (ConducteursController.cs) :
csharp
CopierModifier
[Route("api/[controller]")]
[ApiController]
public class ConducteursController : ControllerBase
private readonly ApplicationDbContext _context;
public ConducteursController(ApplicationDbContext context)
_context = context;
// GET: api/conducteurs
[HttpGet]
public async Task<ActionResult<IEnumerable<Conducteur>>> GetConducteurs()
return await _context.Conducteurs.ToListAsync();
// GET: api/conducteurs/5
[HttpGet("{id}")]
public async Task<ActionResult<Conducteur>> GetConducteur(int id)
var conducteur = await _context.Conducteurs.FindAsync(id);
if (conducteur == null)
return NotFound();
return conducteur;
// POST: api/conducteurs
[HttpPost]
public async Task<ActionResult<Conducteur>> PostConducteur(Conducteur conducteur)
_context.Conducteurs.Add(conducteur);
await _context.SaveChangesAsync();
return CreatedAtAction(nameof(GetConducteur), new { id = conducteur.Id }, conducteur);
// PUT: api/conducteurs/5
[HttpPut("{id}")]
public async Task<IActionResult> PutConducteur(int id, Conducteur conducteur)
if (id != conducteur.Id)
return BadRequest();
_context.Entry(conducteur).State = EntityState.Modified;
await _context.SaveChangesAsync();
return NoContent();
// DELETE: api/conducteurs/5
[HttpDelete("{id}")]
public async Task<IActionResult> DeleteConducteur(int id)
var conducteur = await _context.Conducteurs.FindAsync(id);
if (conducteur == null)
return NotFound();
_context.Conducteurs.Remove(conducteur);
await _context.SaveChangesAsync();
return NoContent();
Répéter la même structure pour les autres entités
Il faudra créer les contrôleurs pour Véhicule, Trajet, Passager et Réservation avec les mêmes
méthodes CRUD (GetAll, GetById, Post, Put, Delete).
Si tu veux, je peux générer ces fichiers pour toi !
Prochaine étape : Recherche de trajets et vérification des places
Une fois que les CRUD sont prêts, on ajoutera :
Un filtre de recherche pour les trajets (par date et lieu de départ).
La vérification des places avant une réservation.
Tu veux qu'on avance sur les autres contrôleurs maintenant ou qu'on passe directement aux
fonctionnalités avancées ?
4o
Vous avez dit :
etape suivante
ChatGPT a dit :
4️⃣ Recherche de Trajets & Vérification des Places
Maintenant que les contrôleurs CRUD sont en place, on va ajouter deux fonctionnalités
importantes :
Filtrer les trajets par date et lieu de départ
Vérifier la disponibilité des places avant une réservation
1️. Filtrage des Trajets par Date et Lieu de Départ
Dans TrajetsController.cs, on ajoute un endpoint de recherche :
csharp
CopierModifier
[HttpGet("search")]
public async Task<ActionResult<IEnumerable<Trajet>>> SearchTrajets(
[FromQuery] string? lieuDepart,
[FromQuery] DateTime? date)
var query = _context.Trajets.AsQueryable();
if (!string.IsNullOrEmpty(lieuDepart))
query = query.Where(t => t.LieuDepart.Contains(lieuDepart));
if (date.HasValue)
query = query.Where(t => t.Date.Date == date.Value.Date);
return await query.ToListAsync();
Explication :
• On récupère les paramètres lieuDepart et date en query string.
• On filtre dynamiquement la requête en fonction des paramètres fournis.
• On retourne la liste des trajets correspondants.
Exemple d’appel API :
sql
CopierModifier
GET /api/trajets/search?lieuDepart=Paris&date=2025-02-10
2️. Vérification des Places Disponibles Avant Réservation
Avant qu'un passager ne réserve un trajet, on doit vérifier si des places sont disponibles.
Dans ReservationsController.cs, on modifie la méthode PostReservation :
csharp
CopierModifier
[HttpPost]
public async Task<ActionResult<Reservation>> PostReservation(Reservation reservation)
var trajet = await _context.Trajets
.Include(t => t.Reservations)
.Include(t => t.Conducteur)
.ThenInclude(c => c.Vehicule)
.FirstOrDefaultAsync(t => t.Id == reservation.TrajetId);
if (trajet == null)
return NotFound("Trajet non trouvé.");
int placesRéservées = trajet.Reservations.Count;
int placesDisponibles = trajet.Conducteur.Vehicule.NombrePlaces;
if (placesRéservées >= placesDisponibles)
return BadRequest("Plus de places disponibles pour ce trajet.");
_context.Reservations.Add(reservation);
await _context.SaveChangesAsync();
return CreatedAtAction(nameof(GetReservation), new { id = reservation.Id }, reservation);
Explication :
• On récupère le trajet et son conducteur avec son véhicule.
• On compte le nombre de réservations existantes.
• On compare avec le nombre de places du véhicule.
• Si c’est complet, on retourne une erreur 400 Bad Request.
Exemple de réponse en cas de places indisponibles :
json
CopierModifier
"error": "Plus de places disponibles pour ce trajet."
Prochaine Étape : Documentation et Tests
Maintenant que l’API fonctionne, on peut :
Créer la documentation avec Swagger
Tester les endpoints avec Postman
(Bonus) Développer un Front-End pour réserver des trajets
Tu veux commencer par Swagger ou tu préfères tester avec Postman d'abord ?
4o
Vous avez dit :
etape suivante
ChatGPT a dit :
5️⃣ Documentation & Tests de l'API
Maintenant que notre API est fonctionnelle, on va :
Générer une documentation interactive avec Swagger
Tester nos endpoints avec Postman
(Optionnel) Ajouter des tests unitaires
1️. Génération de la Documentation avec Swagger
Installation de Swagger
Swagger est intégré à ASP.NET Core via Swashbuckle. Ajoute-le dans Program.cs :
csharp
CopierModifier
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
Dans Program.cs, active Swagger en mode développement :
csharp
CopierModifier
if (app.Environment.IsDevelopment())
app.UseSwagger();
app.UseSwaggerUI();
Exécute l’API et ouvre :
bash
CopierModifier
http://localhost:<port>/swagger
Tu verras une interface interactive pour tester les endpoints !
🛠 2️. Tester l’API avec Postman
Exemples de requêtes
• Créer un conducteur (POST /api/conducteurs)
json
CopierModifier
"nom": "Jean Dupont",
"email": "[email protected]",
"telephone": "0601020304"
• Créer un véhicule (POST /api/vehicules)
json
CopierModifier
"marque": "Peugeot",
"modele": "208",
"immatriculation": "AB-123-CD",
"nombrePlaces": 4,
"conducteurId": 1
}
• Créer un trajet (POST /api/trajets)
json
CopierModifier
"lieuDepart": "Paris",
"destination": "Lyon",
"date": "2025-02-15T08:00:00",
"conducteurId": 1
• Rechercher des trajets (GET /api/trajets/search?lieuDepart=Paris&date=2025-02-15)
• Créer une réservation (vérification des places incluses) (POST /api/reservations)
json
CopierModifier
"trajetId": 1,
"passagerId": 1,
"statut": "confirmée"
3️. Ajouter des Tests Unitaires (Bonus, si tu veux aller plus loin)
Si on veut tester l’API avec xUnit, on peut écrire des tests pour s’assurer que :
✔ Les trajets sont bien filtrés par lieu et date
✔ Une réservation ne peut pas dépasser le nombre de places disponibles
Exemple de test avec xUnit (TrajetTests.cs) :
csharp
CopierModifier
public class TrajetTests
private readonly ApplicationDbContext _context;
public TrajetTests()
{
var options = new DbContextOptionsBuilder<ApplicationDbContext>()
.UseInMemoryDatabase(databaseName: "TestDB")
.Options;
_context = new ApplicationDbContext(options);
[Fact]
public void FiltrerTrajets_ParLieuEtDate_RetourneResultats()
_context.Trajets.Add(new Trajet { LieuDepart = "Paris", Destination = "Lyon", Date =
DateTime.Today });
_context.Trajets.Add(new Trajet { LieuDepart = "Marseille", Destination = "Nice", Date =
DateTime.Today });
_context.SaveChanges();
var service = new TrajetsController(_context);
var result = service.SearchTrajets("Paris", DateTime.Today).Result.Value;
Assert.Single(result);
Prochaine Étape : Ajouter un Front-End ?
Si tu veux, on peut maintenant développer un front-end (React, Vue.js, ou Blazor) pour
permettre aux utilisateurs de :
🖥 Voir les trajets disponibles
Réserver un trajet
Gérer leur compte
Tu veux avancer là-dessus ou bien finaliser la documentation ?
4o
Vous avez dit :
suivant
ChatGPT a dit :
Mémoire mise à jour
6️⃣ Développement du Front-End
On va maintenant créer une interface utilisateur pour permettre aux utilisateurs de :
Rechercher des trajets
Réserver un trajet
Voir leurs réservations
Technologies suggérées :
• React.js (moderne et rapide)
• Tailwind CSS (pour un design épuré)
• Axios (pour communiquer avec l’API)
🏗 1️. Initialiser le Projet React
1⃣ Créer le projet React
sh
CopierModifier
npx create-react-app covoiturage-front
cd covoiturage-front
npm install axios react-router-dom tailwindcss
npx tailwindcss init -p
2⃣ Configurer Tailwind (dans tailwind.config.js)
js
CopierModifier
module.exports = {
content: ["./src/**/*.{js,jsx,ts,tsx}"],
theme: { extend: {} },
plugins: [],
};
3⃣ Ajouter Tailwind dans index.css
css
CopierModifier
@tailwind base;
@tailwind components;
@tailwind utilities;
2️. Création de la Page de Recherche de Trajets
Dans src/pages/SearchTrajets.jsx :
jsx
CopierModifier
import { useState, useEffect } from "react";
import axios from "axios";
export default function SearchTrajets() {
const [lieuDepart, setLieuDepart] = useState("");
const [date, setDate] = useState("");
const [trajets, setTrajets] = useState([]);
const searchTrajets = async () => {
try {
const response = await axios.get("http://localhost:5000/api/trajets/search", {
params: { lieuDepart, date },
});
setTrajets(response.data);
} catch (error) {
console.error("Erreur lors de la récupération des trajets", error);
};
return (
<div className="p-4">
<h1 className="text-2xl font-bold mb-4">Rechercher un Trajet</h1>
<input
type="text"
placeholder="Lieu de départ"
className="border p-2 mr-2"
value={lieuDepart}
onChange={(e) => setLieuDepart(e.target.value)}
/>
<input
type="date"
className="border p-2"
value={date}
onChange={(e) => setDate(e.target.value)}
/>
<button onClick={searchTrajets} className="bg-blue-500 text-white p-2 ml-2">
Rechercher
</button>
<ul className="mt-4">
{trajets.map((trajet) => (
<li key={trajet.id} className="border p-2 my-2">
{trajet.lieuDepart} → {trajet.destination} | {new Date(trajet.date).toLocaleDateString()}
<button className="bg-green-500 text-white p-1 ml-2">Réserver</button>
</li>
))}
</ul>
</div>
);
3️. Configurer le Router
Dans src/App.jsx :
jsx
CopierModifier
import { BrowserRouter as Router, Route, Routes } from "react-router-dom";
import SearchTrajets from "./pages/SearchTrajets";
export default function App() {
return (
<Router>
<Routes>
<Route path="/" element={<SearchTrajets />} />
</Routes>
</Router>
);
Lancer l’Application
sh
CopierModifier
npm start
Tu devrais voir la page de recherche de trajets !
On ajoute maintenant la gestion des réservations ?
4o
Vous avez dit :
oui
ChatGPT a dit :
🎟 7️⃣ Ajout de la Réservation de Trajets
On va maintenant permettre aux utilisateurs de réserver un trajet en cliquant sur un bouton.
1️. Ajouter la Réservation dans SearchTrajets.jsx
Modifions notre fichier SearchTrajets.jsx pour gérer les réservations :
jsx
CopierModifier
import { useState, useEffect } from "react";
import axios from "axios";
export default function SearchTrajets() {
const [lieuDepart, setLieuDepart] = useState("");
const [date, setDate] = useState("");
const [trajets, setTrajets] = useState([]);
const [passagerId] = useState(1); // Simulons un passager connecté (à améliorer avec
authentification)
const searchTrajets = async () => {
try {
const response = await axios.get("http://localhost:5000/api/trajets/search", {
params: { lieuDepart, date },
});
setTrajets(response.data);
} catch (error) {
console.error("Erreur lors de la récupération des trajets", error);
};
const reserverTrajet = async (trajetId) => {
try {
const response = await axios.post("http://localhost:5000/api/reservations", {
trajetId,
passagerId,
statut: "confirmée",
});
alert("Réservation réussie !");
} catch (error) {
alert("Erreur lors de la réservation : " + error.response.data);
}
};
return (
<div className="p-4">
<h1 className="text-2xl font-bold mb-4">Rechercher un Trajet</h1>
<input
type="text"
placeholder="Lieu de départ"
className="border p-2 mr-2"
value={lieuDepart}
onChange={(e) => setLieuDepart(e.target.value)}
/>
<input
type="date"
className="border p-2"
value={date}
onChange={(e) => setDate(e.target.value)}
/>
<button onClick={searchTrajets} className="bg-blue-500 text-white p-2 ml-2">
Rechercher
</button>
<ul className="mt-4">
{trajets.map((trajet) => (
<li key={trajet.id} className="border p-2 my-2">
{trajet.lieuDepart} → {trajet.destination} | {new Date(trajet.date).toLocaleDateString()}
<button
onClick={() => reserverTrajet(trajet.id)}
className="bg-green-500 text-white p-1 ml-2"
>
Réserver
</button>
</li>
))}
</ul>
</div>
);
2️. Ajouter une Page "Mes Réservations"
Dans src/pages/MesReservations.jsx :
jsx
CopierModifier
import { useState, useEffect } from "react";
import axios from "axios";
export default function MesReservations() {
const [reservations, setReservations] = useState([]);
const [passagerId] = useState(1); // Simulons un passager connecté
useEffect(() => {
const fetchReservations = async () => {
try {
const response = await
axios.get(`http://localhost:5000/api/reservations/passager/${passagerId}`);
setReservations(response.data);
} catch (error) {
console.error("Erreur lors de la récupération des réservations", error);
};
fetchReservations();
}, []);
return (
<div className="p-4">
<h1 className="text-2xl font-bold mb-4">Mes Réservations</h1>
<ul>
{reservations.map((reservation) => (
<li key={reservation.id} className="border p-2 my-2">
Trajet {reservation.trajet.lieuDepart} → {reservation.trajet.destination}
<span className="ml-2 font-bold">{reservation.statut}</span>
</li>
))}
</ul>
</div>
);
3️. Ajouter une Navigation Entre les Pages
Modifions App.jsx pour inclure la page Mes Réservations :
jsx
CopierModifier
import { BrowserRouter as Router, Route, Routes, Link } from "react-router-dom";
import SearchTrajets from "./pages/SearchTrajets";
import MesReservations from "./pages/MesReservations";
export default function App() {
return (
<Router>
<nav className="bg-gray-800 text-white p-4">
<Link to="/" className="mr-4">Recherche de Trajets</Link>
<Link to="/reservations">Mes Réservations</Link>
</nav>
<Routes>
<Route path="/" element={<SearchTrajets />} />
<Route path="/reservations" element={<MesReservations />} />
</Routes>
</Router>
);
Test du Front-End
Démarre l'application :
sh
CopierModifier
npm start
Fonctionnalités disponibles :
Rechercher un trajet
Réserver un trajet
Consulter ses réservations
Prochaine Étape : Ajouter l’Authentification ?
Tu veux qu’on ajoute un login/logout avec JWT pour gérer les utilisateurs et sécuriser l’accès
aux réservations ?