true, 'message' => 'API Voitures opérationnelle', 'database' => DB_NAME, 'endpoints' => [ 'Voitures' => [ 'GET /cars' => 'Liste toutes les voitures', 'GET /cars/{id}' => 'Détails d\'une voiture', 'GET /cars/{id}/etat' => 'État complet (voiture, dernier km, entretiens, notes)', 'POST /cars' => 'Créer une voiture (marque, modele, annee, dateAchat, VIN)', 'PUT /cars/{id}' => 'Modifier les paramètres d\'une voiture', 'DELETE /cars/{id}' => 'Supprimer une voiture (et ses données associées)' ], 'Kilométrage' => [ 'GET /cars/{id}/kilometrage' => 'Historique des relevés kilométriques', 'POST /cars/{id}/kilometrage' => 'Ajouter un relevé kilométrique (valeur, date_releve)', 'DELETE /kilometrage/{id}' => 'Supprimer un relevé kilométrique' ], 'Entretiens (Maintenance)' => [ 'GET /cars/{id}/maintenance' => 'Historique des entretiens d\'une voiture', 'POST /cars/{id}/maintenance' => 'Ajouter un entretien (type_entretien, date_evenement, kilometrage, description, prix)', 'PUT /maintenance/{id}' => 'Modifier un entretien', 'DELETE /maintenance/{id}' => 'Supprimer un entretien' ], 'Notes' => [ 'GET /cars/{id}/notes' => 'Notes associées à une voiture', 'POST /cars/{id}/notes' => 'Ajouter une note (titre, contenu)', 'PUT /notes/{id}' => 'Modifier une note', 'DELETE /notes/{id}' => 'Supprimer une note' ] ] ]); } $resource = strtolower($segments[0]); // ========================================================================= // RESSOURCE: CARS (/cars ...) // ========================================================================= if ($resource === 'cars' || $resource === 'voitures') { $carId = isset($segments[1]) && is_numeric($segments[1]) ? (int)$segments[1] : null; $subResource = $segments[2] ?? null; // --- SOUS-RESSOURCES DE CARS --- // 1. /cars/{id}/etat (État complet via la procédure sp_getEtatVoiture) if ($carId && $subResource === 'etat' && $method === 'GET') { try { $stmt = $db->prepare('CALL sp_getEtatVoiture(:id)'); $stmt->execute([':id' => $carId]); $car = $stmt->fetch(); if (!$car) { sendJson(404, ['success' => false, 'error' => "Voiture #{$carId} introuvable"]); } $maintenances = []; if ($stmt->nextRowset()) { $maintenances = $stmt->fetchAll(); } $notes = []; if ($stmt->nextRowset()) { $notes = $stmt->fetchAll(); } $stmt->closeCursor(); sendJson(200, [ 'success' => true, 'data' => [ 'voiture' => $car, 'maintenances' => $maintenances, 'notes' => $notes ] ]); } catch (Exception $e) { // Fallback si la procédure stockée a un souci $stmtCar = $db->prepare(' SELECT c.*, (SELECT valeur FROM kilometrage WHERE car_id = c.id ORDER BY date_releve DESC, id DESC LIMIT 1) AS dernier_km FROM cars c WHERE c.id = ? '); $stmtCar->execute([$carId]); $car = $stmtCar->fetch(); if (!$car) { sendJson(404, ['success' => false, 'error' => "Voiture #{$carId} introuvable"]); } $stmtMaint = $db->prepare('SELECT * FROM maintenance_logs WHERE car_id = ? ORDER BY date_evenement DESC'); $stmtMaint->execute([$carId]); $stmtNotes = $db->prepare('SELECT * FROM notes WHERE car_id = ? ORDER BY date_creation DESC'); $stmtNotes->execute([$carId]); sendJson(200, [ 'success' => true, 'data' => [ 'voiture' => $car, 'maintenances' => $stmtMaint->fetchAll(), 'notes' => $stmtNotes->fetchAll() ] ]); } } // 2. /cars/{id}/kilometrage if ($carId && $subResource === 'kilometrage') { if ($method === 'GET') { $limit = isset($_GET['limit']) ? (int)$_GET['limit'] : 50; try { $stmt = $db->prepare('CALL sp_getKilometrages(:id, :limit)'); $stmt->execute([':id' => $carId, ':limit' => $limit]); $data = $stmt->fetchAll(); $stmt->closeCursor(); } catch (Exception $e) { $stmt = $db->prepare('SELECT * FROM kilometrage WHERE car_id = ? ORDER BY date_releve DESC, id DESC LIMIT ?'); $stmt->bindValue(1, $carId, PDO::PARAM_INT); $stmt->bindValue(2, $limit, PDO::PARAM_INT); $stmt->execute(); $data = $stmt->fetchAll(); } sendJson(200, ['success' => true, 'data' => $data]); } if ($method === 'POST') { $body = getBody(); if (!isset($body['valeur'])) { sendJson(400, ['success' => false, 'error' => 'Le champ valeur (kilométrage) est obligatoire']); } $dateReleve = $body['date_releve'] ?? date('Y-m-d'); $valeur = (int)$body['valeur']; $stmt = $db->prepare('INSERT INTO kilometrage (car_id, date_releve, valeur) VALUES (?, ?, ?)'); $stmt->execute([$carId, $dateReleve, $valeur]); $newId = (int)$db->lastInsertId(); sendJson(201, [ 'success' => true, 'message' => 'Relevé kilométrique enregistré', 'data' => [ 'id' => $newId, 'car_id' => $carId, 'date_releve' => $dateReleve, 'valeur' => $valeur ] ]); } sendJson(405, ['success' => false, 'error' => 'Méthode non autorisée pour cette sous-ressource']); } // 3. /cars/{id}/maintenance if ($carId && ($subResource === 'maintenance' || $subResource === 'entretiens')) { if ($method === 'GET') { $stmt = $db->prepare('SELECT * FROM maintenance_logs WHERE car_id = ? ORDER BY date_evenement DESC'); $stmt->execute([$carId]); sendJson(200, ['success' => true, 'data' => $stmt->fetchAll()]); } if ($method === 'POST') { $body = getBody(); if (empty($body['type_entretien']) || empty($body['date_evenement']) || !isset($body['kilometrage'])) { sendJson(400, [ 'success' => false, 'error' => 'Les champs type_entretien, date_evenement et kilometrage sont obligatoires' ]); } $stmt = $db->prepare(' INSERT INTO maintenance_logs (car_id, type_entretien, date_evenement, kilometrage, description, prix) VALUES (?, ?, ?, ?, ?, ?) '); $stmt->execute([ $carId, trim((string)$body['type_entretien']), (string)$body['date_evenement'], (int)$body['kilometrage'], isset($body['description']) ? trim((string)$body['description']) : null, isset($body['prix']) ? (float)$body['prix'] : null ]); $newId = (int)$db->lastInsertId(); $stmtGet = $db->prepare('SELECT * FROM maintenance_logs WHERE id = ?'); $stmtGet->execute([$newId]); sendJson(201, [ 'success' => true, 'message' => 'Entretien enregistré avec succès', 'data' => $stmtGet->fetch() ]); } sendJson(405, ['success' => false, 'error' => 'Méthode non autorisée pour cette sous-ressource']); } // 4. /cars/{id}/notes if ($carId && $subResource === 'notes') { if ($method === 'GET') { $stmt = $db->prepare('SELECT * FROM notes WHERE car_id = ? ORDER BY date_creation DESC'); $stmt->execute([$carId]); sendJson(200, ['success' => true, 'data' => $stmt->fetchAll()]); } if ($method === 'POST') { $body = getBody(); if (empty($body['contenu'])) { sendJson(400, ['success' => false, 'error' => 'Le champ contenu est obligatoire']); } $titre = isset($body['titre']) ? trim((string)$body['titre']) : null; $contenu = trim((string)$body['contenu']); $stmt = $db->prepare('INSERT INTO notes (car_id, titre, contenu) VALUES (?, ?, ?)'); $stmt->execute([$carId, $titre, $contenu]); $newId = (int)$db->lastInsertId(); $stmtGet = $db->prepare('SELECT * FROM notes WHERE id = ?'); $stmtGet->execute([$newId]); sendJson(201, [ 'success' => true, 'message' => 'Note enregistrée avec succès', 'data' => $stmtGet->fetch() ]); } sendJson(405, ['success' => false, 'error' => 'Méthode non autorisée pour cette sous-ressource']); } // --- OPÉRATIONS PRINCIPALES SUR LES VOITURES (/cars ou /cars/{id}) --- // GET /cars : Liste toutes les voitures if ($carId === null && $method === 'GET') { $sql = 'SELECT c.*, (SELECT valeur FROM kilometrage WHERE car_id = c.id ORDER BY date_releve DESC, id DESC LIMIT 1) AS dernier_km FROM cars c ORDER BY c.id ASC'; $stmt = $db->query($sql); sendJson(200, ['success' => true, 'data' => $stmt->fetchAll()]); } // GET /cars/{id} : Consulter une voiture if ($carId !== null && $subResource === null && $method === 'GET') { $stmt = $db->prepare(' SELECT c.*, (SELECT valeur FROM kilometrage WHERE car_id = c.id ORDER BY date_releve DESC, id DESC LIMIT 1) AS dernier_km FROM cars c WHERE c.id = ? '); $stmt->execute([$carId]); $car = $stmt->fetch(); if (!$car) { sendJson(404, ['success' => false, 'error' => "Voiture #{$carId} introuvable"]); } sendJson(200, ['success' => true, 'data' => $car]); } // POST /cars : Créer une voiture if ($carId === null && $method === 'POST') { $body = getBody(); // Validation des champs obligatoires $required = ['marque', 'modele', 'annee', 'dateAchat']; foreach ($required as $field) { if (empty($body[$field])) { sendJson(400, ['success' => false, 'error' => "Le champ '$field' est obligatoire"]); } } $marque = trim((string)$body['marque']); $modele = trim((string)$body['modele']); $annee = (int)$body['annee']; $dateAchat = (string)$body['dateAchat']; $vin = !empty($body['VIN']) ? trim((string)$body['VIN']) : null; // Utilisation de la procédure stockée sp_insertVoiture try { $stmt = $db->prepare('CALL sp_insertVoiture(:marque, :modele, :annee, :dateAchat, :vin)'); $stmt->execute([ ':marque' => $marque, ':modele' => $modele, ':annee' => $annee, ':dateAchat' => $dateAchat, ':vin' => $vin ]); $res = $stmt->fetch(); $stmt->closeCursor(); $newId = isset($res['new_id']) ? (int)$res['new_id'] : (int)$db->lastInsertId(); } catch (Exception $e) { // Fallback direct INSERT si la procédure a une incompatibilité $stmt = $db->prepare('INSERT INTO cars (marque, modele, annee, dateAchat, VIN) VALUES (?, ?, ?, ?, ?)'); $stmt->execute([$marque, $modele, $annee, $dateAchat, $vin]); $newId = (int)$db->lastInsertId(); } // Si un kilométrage initial est fourni if (!empty($body['kilometrage_initial'])) { $kmStmt = $db->prepare('INSERT INTO kilometrage (car_id, date_releve, valeur) VALUES (?, ?, ?)'); $kmStmt->execute([$newId, $dateAchat, (int)$body['kilometrage_initial']]); } // Récupère l'enregistrement créé $stmtGet = $db->prepare(' SELECT c.*, (SELECT valeur FROM kilometrage WHERE car_id = c.id ORDER BY date_releve DESC, id DESC LIMIT 1) AS dernier_km FROM cars c WHERE c.id = ? '); $stmtGet->execute([$newId]); sendJson(201, [ 'success' => true, 'message' => 'Voiture créée avec succès', 'data' => $stmtGet->fetch() ]); } // PUT / PATCH /cars/{id} : Mettre à jour les paramètres d'une voiture if ($carId !== null && $subResource === null && ($method === 'PUT' || $method === 'PATCH')) { // Vérifier si la voiture existe $check = $db->prepare('SELECT id FROM cars WHERE id = ?'); $check->execute([$carId]); if (!$check->fetch()) { sendJson(404, ['success' => false, 'error' => "Voiture #{$carId} introuvable"]); } $body = getBody(); $allowedFields = ['marque', 'modele', 'annee', 'dateAchat', 'VIN']; $updates = []; $params = []; foreach ($allowedFields as $field) { if (array_key_exists($field, $body)) { $updates[] = "`$field` = ?"; $params[] = $body[$field]; } } if (empty($updates)) { sendJson(400, ['success' => false, 'error' => 'Aucun paramètre valide à mettre à jour']); } $params[] = $carId; $sql = 'UPDATE cars SET ' . implode(', ', $updates) . ' WHERE id = ?'; $stmt = $db->prepare($sql); $stmt->execute($params); // Récupère la voiture mise à jour $stmtGet = $db->prepare(' SELECT c.*, (SELECT valeur FROM kilometrage WHERE car_id = c.id ORDER BY date_releve DESC, id DESC LIMIT 1) AS dernier_km FROM cars c WHERE c.id = ? '); $stmtGet->execute([$carId]); sendJson(200, [ 'success' => true, 'message' => 'Paramètres de la voiture mis à jour avec succès', 'data' => $stmtGet->fetch() ]); } // DELETE /cars/{id} : Supprimer une voiture if ($carId !== null && $subResource === null && $method === 'DELETE') { // Vérifier existence $check = $db->prepare('SELECT id FROM cars WHERE id = ?'); $check->execute([$carId]); if (!$check->fetch()) { sendJson(404, ['success' => false, 'error' => "Voiture #{$carId} introuvable"]); } // Exécution de la procédure stockée sp_deleteVoiture try { $stmt = $db->prepare('CALL sp_deleteVoiture(:id)'); $stmt->execute([':id' => $carId]); $stmt->closeCursor(); } catch (Exception $e) { $stmt = $db->prepare('DELETE FROM cars WHERE id = ?'); $stmt->execute([$carId]); } sendJson(200, [ 'success' => true, 'message' => "Voiture #{$carId} ainsi que toutes ses données associées ont été supprimées avec succès" ]); } sendJson(405, ['success' => false, 'error' => "Méthode HTTP non autorisée pour cette URL"]); } // ========================================================================= // RESSOURCE: KILOMETRAGE DIRECT (/kilometrage/{id}) // ========================================================================= if ($resource === 'kilometrage') { $kmId = isset($segments[1]) && is_numeric($segments[1]) ? (int)$segments[1] : null; if ($kmId && $method === 'DELETE') { $stmt = $db->prepare('DELETE FROM kilometrage WHERE id = ?'); $stmt->execute([$kmId]); if ($stmt->rowCount() === 0) { sendJson(404, ['success' => false, 'error' => "Relevé kilométrique #{$kmId} introuvable"]); } sendJson(200, ['success' => true, 'message' => "Relevé kilométrique #{$kmId} supprimé"]); } sendJson(405, ['success' => false, 'error' => "Méthode non autorisée sur /kilometrage"]); } // ========================================================================= // RESSOURCE: MAINTENANCE DIRECT (/maintenance/{id}) // ========================================================================= if ($resource === 'maintenance' || $resource === 'entretiens') { $maintId = isset($segments[1]) && is_numeric($segments[1]) ? (int)$segments[1] : null; if (!$maintId) { sendJson(400, ['success' => false, 'error' => 'Identifiant d\'entretien requis']); } if ($method === 'GET') { $stmt = $db->prepare('SELECT * FROM maintenance_logs WHERE id = ?'); $stmt->execute([$maintId]); $item = $stmt->fetch(); if (!$item) { sendJson(404, ['success' => false, 'error' => "Entretien #{$maintId} introuvable"]); } sendJson(200, ['success' => true, 'data' => $item]); } if ($method === 'PUT' || $method === 'PATCH') { $body = getBody(); $allowed = ['type_entretien', 'date_evenement', 'kilometrage', 'description', 'prix']; $updates = []; $params = []; foreach ($allowed as $f) { if (array_key_exists($f, $body)) { $updates[] = "`$f` = ?"; $params[] = $body[$f]; } } if (empty($updates)) { sendJson(400, ['success' => false, 'error' => 'Aucun paramètre à modifier']); } $params[] = $maintId; $stmt = $db->prepare('UPDATE maintenance_logs SET ' . implode(', ', $updates) . ' WHERE id = ?'); $stmt->execute($params); $stmtGet = $db->prepare('SELECT * FROM maintenance_logs WHERE id = ?'); $stmtGet->execute([$maintId]); sendJson(200, [ 'success' => true, 'message' => 'Entretien mis à jour avec succès', 'data' => $stmtGet->fetch() ]); } if ($method === 'DELETE') { $stmt = $db->prepare('DELETE FROM maintenance_logs WHERE id = ?'); $stmt->execute([$maintId]); if ($stmt->rowCount() === 0) { sendJson(404, ['success' => false, 'error' => "Entretien #{$maintId} introuvable"]); } sendJson(200, ['success' => true, 'message' => "Entretien #{$maintId} supprimé avec succès"]); } sendJson(405, ['success' => false, 'error' => "Méthode non autorisée sur /maintenance/{id}"]); } // ========================================================================= // RESSOURCE: NOTES DIRECT (/notes/{id}) // ========================================================================= if ($resource === 'notes') { $noteId = isset($segments[1]) && is_numeric($segments[1]) ? (int)$segments[1] : null; if (!$noteId) { sendJson(400, ['success' => false, 'error' => 'Identifiant de note requis']); } if ($method === 'GET') { $stmt = $db->prepare('SELECT * FROM notes WHERE id = ?'); $stmt->execute([$noteId]); $item = $stmt->fetch(); if (!$item) { sendJson(404, ['success' => false, 'error' => "Note #{$noteId} introuvable"]); } sendJson(200, ['success' => true, 'data' => $item]); } if ($method === 'PUT' || $method === 'PATCH') { $body = getBody(); $allowed = ['titre', 'contenu']; $updates = []; $params = []; foreach ($allowed as $f) { if (array_key_exists($f, $body)) { $updates[] = "`$f` = ?"; $params[] = $body[$f]; } } if (empty($updates)) { sendJson(400, ['success' => false, 'error' => 'Aucun paramètre à modifier']); } $params[] = $noteId; $stmt = $db->prepare('UPDATE notes SET ' . implode(', ', $updates) . ' WHERE id = ?'); $stmt->execute($params); $stmtGet = $db->prepare('SELECT * FROM notes WHERE id = ?'); $stmtGet->execute([$noteId]); sendJson(200, [ 'success' => true, 'message' => 'Note mise à jour avec succès', 'data' => $stmtGet->fetch() ]); } if ($method === 'DELETE') { $stmt = $db->prepare('DELETE FROM notes WHERE id = ?'); $stmt->execute([$noteId]); if ($stmt->rowCount() === 0) { sendJson(404, ['success' => false, 'error' => "Note #{$noteId} introuvable"]); } sendJson(200, ['success' => true, 'message' => "Note #{$noteId} supprimée avec succès"]); } sendJson(405, ['success' => false, 'error' => "Méthode non autorisée sur /notes/{id}"]); } // Ressource inconnue sendJson(404, [ 'success' => false, 'error' => "Ressource '/$resource' non reconnue. Consultez la racine '/' pour la documentation des routes." ]); } catch (PDOException $e) { sendJson(500, [ 'success' => false, 'error' => 'Erreur SQL: ' . $e->getMessage() ]); } catch (Throwable $e) { sendJson(500, [ 'success' => false, 'error' => 'Erreur serveur: ' . $e->getMessage() ]); }