BTS SIO SLAM — Promotion 2025–2026

Projet Lisa Horse

Application web de gestion pour une clinique vétérinaire spécialisée équine — PHP / MySQL / HTML5 / CSS3

PHP 8 (PDO) MySQL / PhpMyAdmin HTML5 CSS3 Kanban Trello Live Share
MA
Mohamed Amine
Dev · Documentation
LK
Kevin Leger
Dev · PHP · Auth
SF
Sofiane Fodil
Dev · SQL · CRUD
CD
Corey Defranc
Analyste · UML

Réalisations par membre

MA

Mohamed Amine Hidaoui

// Développeur · Documentation · UML
Page Compte Rendu Médical Page Facturation Page Planning Public Dashboard Diagramme UML Dictionnaire de données
<?php
require_once "includes/auth.php";
require_once "config/db.php";

// ── INSERT ou UPDATE ─────────────────────────────────────────────
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $id                = !empty($_POST['id'])     ? (int)$_POST['id']     : null;
    $rdv_id            = !empty($_POST['rdv_id']) ? (int)$_POST['rdv_id'] : null;
    $date_consultation = trim($_POST['date_consultation'] ?? '');
    $diagnostic        = trim($_POST['diagnostic']        ?? '');
    $traitement        = trim($_POST['traitement']        ?? '');

    if ($rdv_id && $date_consultation && $diagnostic && $traitement) {
        if (!$id) {
            $pdo->prepare("
                INSERT INTO consultations (rdv_id, date_consultation, diagnostic, traitement)
                VALUES (?, ?, ?, ?)
            ")->execute([$rdv_id, $date_consultation, $diagnostic, $traitement]);
        } else {
            $pdo->prepare("
                UPDATE consultations
                SET rdv_id = ?, date_consultation = ?, diagnostic = ?, traitement = ?
                WHERE id = ?
            ")->execute([$rdv_id, $date_consultation, $diagnostic, $traitement, $id]);
        }
    }
    header("Location: compte_rendu_medical.php");
    exit;
}

// ── Liste des consultations avec toutes les jointures ────────────
$consultations = $pdo->query("
    SELECT
        c.id, c.date_consultation, c.diagnostic, c.traitement, c.rdv_id,
        a.nom AS animal_nom, a.espece,
        v.nom AS vet_nom
    FROM   consultations c
    JOIN   rdv          r ON c.rdv_id         = r.id
    JOIN   animaux      a ON r.animal_id      = a.id
    JOIN   veterinaires v ON r.veterinaire_id = v.id
    ORDER  BY c.date_consultation DESC
")->fetchAll();
<?php
require_once "includes/auth.php";
require_once "config/db.php";

// ── INSERT ou UPDATE ─────────────────────────────────────────────
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    if (empty($_POST['id'])) {
        $pdo->prepare("INSERT INTO factures (consultation_id, montant, statut, date_facture) VALUES (?,?,?,?)")
            ->execute([$_POST['consultation_id'], $_POST['montant'], $_POST['statut'], $_POST['date_facture']]);
    } else {
        $pdo->prepare("UPDATE factures SET consultation_id=?, montant=?, statut=?, date_facture=? WHERE id=?")
            ->execute([$_POST['consultation_id'], $_POST['montant'], $_POST['statut'], $_POST['date_facture'], $_POST['id']]);
    }
    header("Location: service_facturation.php");
    exit;
}

// ── Liste des factures avec jointures multi-tables ────────────────
$data = $pdo->query("
    SELECT f.*,
           a.nom AS animal_nom, a.espece,
           p.nom AS proprio_nom, p.prenom AS proprio_prenom,
           v.nom AS vet_nom
    FROM   factures      f
    JOIN   consultations c ON f.consultation_id = c.id
    JOIN   rdv           r ON c.rdv_id          = r.id
    JOIN   animaux       a ON r.animal_id        = a.id
    JOIN   proprietaires p ON a.proprietaire_id  = p.id
    JOIN   veterinaires  v ON r.veterinaire_id   = v.id
    ORDER  BY f.date_facture DESC
")->fetchAll();

// ── Statuts avec classes CSS dynamiques ─────────────────────────
$statusCls = match($f['statut']) {
    'Payée'     => 'status-payee',
    'Non payée' => 'status-attente',
    default     => 'status-annulee'
};
<?php
// planning_public.php — Page publique sans authentification requise
// Pas de require auth.php, pas de menu intranet
require_once "config/db.php";

// Récupération des RDV avec animaux et vétérinaires
$stmt   = $pdo->query("
    SELECT r.id, r.date_rdv, r.statut,
           a.nom AS animal_nom, a.espece AS animal_espece,
           v.nom AS vet_nom, v.specialite
    FROM rdv r
    JOIN animaux      a ON r.animal_id      = a.id
    JOIN veterinaires v ON r.veterinaire_id = v.id
    ORDER BY r.date_rdv ASC
");
$events = [];

while ($r = $stmt->fetch()) {
    // Couleur conditionnelle selon le statut du RDV
    $color = match($r['statut']) {
        'Terminé' => '#1a62b0',
        'Annulé'  => '#b92c21',
        default   => '#1a8a49'  // Programmé
    };
    $events[] = [
        'id'    => $r['id'],
        'title' => $r['animal_nom'] . ' (' . $r['animal_espece'] . ')',
        'start' => $r['date_rdv'],
        'color' => $color,
        'extendedProps' => [
            'statut'      => $r['statut'],
            'veterinaire' => $r['vet_nom'],
            'specialite'  => $r['specialite'],
        ]
    ];
}
// Injection PHP → JS pour FullCalendar
// const events = <?= json_encode($events, JSON_UNESCAPED_UNICODE) ?>;
<?php
require_once "includes/auth.php";
require_once "config/db.php";

// Compteurs pour les statistiques du tableau de bord
$veterinaires  = $pdo->query("SELECT COUNT(*) FROM veterinaires") ->fetchColumn();
$proprietaires = $pdo->query("SELECT COUNT(*) FROM proprietaires")->fetchColumn();
$animaux       = $pdo->query("SELECT COUNT(*) FROM animaux")      ->fetchColumn();

$nom    = htmlspecialchars($_SESSION['user']['nom']    ?? '');
$prenom = htmlspecialchars($_SESSION['user']['prenom'] ?? '');
?>
<!-- Hero banner avec image de fond et overlay sombre -->
<div class="hero-dashboard mb-4">
    <div class="hero-content">
        <h2>👋 Bienvenue, <?= $prenom ?> <?= $nom ?></h2>
        <p>Tableau de bord — Clinique vétérinaire Lisa Horse</p>
    </div>
</div>
<!-- Cartes de statistiques (vétérinaires, propriétaires, animaux) -->
<div class="row">
    <div class="col-md-4 mb-3">
        <div class="card stat-card">
            <div class="stat-icon">🩺</div>
            <h6>Vétérinaires</h6>
            <h2><?= $veterinaires ?></h2>
        </div>
    </div>
    <!-- ... propriétaires, animaux -->
</div>

// Conclusion

Rôle dans le projet

Développement des pages fonctionnelles les plus complexes (comptes rendus médicaux, facturation, planning public FullCalendar, tableau de bord). Responsable de la documentation technique et du diagramme de classes UML (Win Design, 7 classes).

Défis rencontrés

Orchestration de jointures SQL sur 5 tables (factures, consultations, RDV, animaux, propriétaires). Intégration de FullCalendar avec injection PHP→JS pour le planning public, sans authentification ni Bootstrap personnalisé.

Solutions apportées

Utilisation de requêtes PDO préparées avec jointures multi-tables. Pattern INSERT/UPDATE unifié via un seul formulaire PHP. Sérialisation JSON des événements PHP vers JavaScript (json_encode). Séparation stricte public/intranet sur le planning.

Améliorations possibles

Ajouter des totaux de CA dynamiques sur le dashboard. Implémenter une génération PDF des factures. Sécuriser les suppressions avec tokens CSRF. Compléter les commentaires PHPDoc pour la maintenabilité.

LK

Kevin Leger (LK)

// Développeur PHP · Authentification · Architecture
Page Index Page Admin Login Page Animal (CRUD) Page RDV (CRUD) Harmonisation CSS Architecture app
<?php
require_once "../config/db.php";
session_start();

// Vérification des identifiants à la soumission du formulaire
if ($_POST) {
    $stmt = $pdo->prepare("SELECT * FROM utilisateurs WHERE email=?");
    $stmt->execute([$_POST['email']]);
    $user = $stmt->fetch();

    // password_verify() compare le mdp saisi au hash bcrypt en BDD
    if ($user && password_verify($_POST['password'], $user['mot_de_passe'])) {
        $_SESSION['user'] = $user;
        header("Location: ../dashboard.php");
        exit;
    }
    $error = "Identifiants incorrects";
}
?>
<!DOCTYPE html>
<html lang="fr">
<head>
  <title>Connexion - Clinique vétérinaire Lisa Horse</title>
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
  <style>
    body {
      font-family:'Poppins',sans-serif;
      background: url("../assets/img/accueil-clinique-val-europe.jpg") center/cover no-repeat;
      height: 100vh;
    }
    .overlay { position:absolute; inset:0; background:rgba(0,0,0,0.6); }
    .card    { border-radius: 20px; }
  </style>
</head>
<body>
  <div class="overlay"></div>
  <div class="d-flex justify-content-center align-items-center vh-100 position-relative">
    <form method="post" class="card p-4 shadow-lg" style="width:380px;">
      <h3 class="text-center mb-3">🐎 Clinique vétérinaire Lisa Horse</h3>
      <p class="text-center mb-3">Accès à l'intranet</p>
      <?php if(isset($error)): ?>
        <div class="alert alert-danger"><?= $error ?></div>
      <?php endif; ?>
      <input class="form-control mb-3" name="email" placeholder="Adresse email" required>
      <input class="form-control mb-3" type="password" name="password" placeholder="Mot de passe" required>
      <button class="btn btn-success w-100 py-2">🔐 Se connecter</button>
    </form>
  </div>
</body></html>
<?php
// animaux.php — Gestion des animaux (CRUD complet)
require_once "includes/auth.php";
require_once "config/db.php";

// ── 1. Traitement du formulaire (INSERT ou UPDATE) ───────────────
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $id              = !empty($_POST['id'])              ? (int)$_POST['id']              : null;
    $nom             = trim($_POST['nom']                ?? '');
    $espece          = trim($_POST['espece']             ?? '');
    $race            = trim($_POST['race']               ?? '');
    $sexe            = trim($_POST['sexe']               ?? '');
    $date_naissance  = trim($_POST['date_naissance']     ?? '') ?: null; // NULL si vide
    $proprietaire_id = !empty($_POST['proprietaire_id']) ? (int)$_POST['proprietaire_id'] : null;

    if ($nom && $proprietaire_id) {
        if (!$id) {
            // ── Création d'un nouvel animal ──────────────────────
            $pdo->prepare("
                INSERT INTO animaux (nom, espece, race, sexe, date_naissance, proprietaire_id)
                VALUES (?, ?, ?, ?, ?, ?)
            ")->execute([$nom, $espece, $race, $sexe, $date_naissance, $proprietaire_id]);
        } else {
            // ── Mise à jour d'un animal existant ─────────────────
            $pdo->prepare("
                UPDATE animaux
                SET nom=?, espece=?, race=?, sexe=?, date_naissance=?, proprietaire_id=?
                WHERE id=?
            ")->execute([$nom, $espece, $race, $sexe, $date_naissance, $proprietaire_id, $id]);
        }
    }
    header("Location: animaux.php");
    exit;
}

// ── 4. Liste animaux avec jointure propriétaires ─────────────────
$data = $pdo->query("
    SELECT a.id, a.nom, a.espece, a.race, a.sexe, a.date_naissance, a.proprietaire_id,
           p.nom AS prop_nom, p.prenom AS prop_prenom
    FROM   animaux a
    LEFT JOIN proprietaires p ON a.proprietaire_id = p.id
    ORDER  BY a.nom ASC
")->fetchAll();
<?php
require_once "includes/auth.php";
require_once "config/db.php";

// Ajout ou modification d'un RDV selon la présence de l'ID
if ($_POST) {
    if (empty($_POST['id'])) {
        $pdo->prepare("INSERT INTO rdv (animal_id, veterinaire_id, date_rdv, statut) VALUES (?,?,?,?)")
            ->execute([$_POST['animal_id'], $_POST['veterinaire_id'], $_POST['date_rdv'], $_POST['statut']]);
    } else {
        $pdo->prepare("UPDATE rdv SET animal_id=?, veterinaire_id=?, date_rdv=?, statut=? WHERE id=?")
            ->execute([$_POST['animal_id'], $_POST['veterinaire_id'], $_POST['date_rdv'], $_POST['statut'], $_POST['id']]);
    }
    header("Location: rdv.php"); exit;
}

// Récupération de tous les RDV avec jointures (animal, vétérinaire, propriétaire)
$data = $pdo->query("
    SELECT rdv.*,
           a.nom AS animal_nom, a.espece AS animal_espece,
           v.nom AS vet_nom,
           p.nom AS prop_nom, p.prenom AS prop_prenom
    FROM rdv
    LEFT JOIN animaux       a ON rdv.animal_id      = a.id
    LEFT JOIN veterinaires  v ON rdv.veterinaire_id = v.id
    LEFT JOIN proprietaires p ON a.proprietaire_id  = p.id
    ORDER BY rdv.date_rdv DESC
")->fetchAll();

// Badges colorés selon le statut via match PHP 8
$badge = match($r['statut']) {
    'Terminé'   => 'badge-termine',
    'Programmé' => 'badge-programme',
    'Annulé'    => 'badge-annule',
    default     => ''
};
<!-- index.php — Page d'accueil publique de la clinique -->
<section class="hero">
    <!-- Overlay sombre pour améliorer la lisibilité du texte -->
    <div class="overlay"></div>
    <div class="content">
        <h1>🐴 Clinique vétérinaire Lisa Horse</h1>
        <p class="lead mt-3">Gestion & planning des soins équins</p>
        <div class="mt-4">
            <!-- Accès intranet : redirige vers login si non connecté -->
            <a href="auth/login.php" class="btn btn-success btn-custom">
                🔐 Intranet
            </a>
            <!-- Planning public : aucune authentification requise -->
            <a href="planning_public.php" class="btn btn-primary btn-custom">
                📅 Voir le planning
            </a>
        </div>
    </div>
    <!-- Copyright avec année dynamique -->
    <div class="footer">
        © <?= date('Y') ?> Clinique vétérinaire Lisa Horse
    </div>
</section>

// Conclusion

Rôle dans le projet

Développeur principal de l'intranet PHP. A pris en charge l'architecture de l'application (includes, config, auth), le système d'authentification avec sessions PHP, et les pages CRUD animaux et rendez-vous. Responsable aussi de la page d'accueil publique.

Défis rencontrés

Mise en place d'un système d'authentification sécurisé avec password_verify() et gestion de session. Conception d'un formulaire PHP unique gérant à la fois l'ajout et la modification (INSERT/UPDATE conditionnel). Gestion du type DATETIME pour les RDV.

Solutions apportées

Utilisation de PDO avec requêtes préparées (protection injection SQL). Sessions PHP sécurisées avec vérification de hash bcrypt. Détection de l'action (ajout/modif) via la présence ou absence de l'ID caché dans le formulaire. Redirection POST/GET pour éviter la re-soumission.

Améliorations possibles

Remplacer les suppressions GET par des formulaires POST avec tokens CSRF. Ajouter une gestion des rôles (admin, secrétaire, vétérinaire). Implémenter une recherche/filtrage dans les tableaux. Compléter les commentaires de débogage.

SF

Sofiane Fodil (SF)

// Développeur BDD · Requêtes SQL · Pages CRUD
Requêtes SQL demandées Page Propriétaires Page Vétérinaires Page Consultation Schéma relationnel CSS global
<?php
require_once "includes/auth.php";
require_once "config/db.php";

// Ajout ou modification d'un propriétaire selon la présence de l'ID
if ($_POST) {
    if (empty($_POST['id'])) {
        $pdo->prepare("INSERT INTO proprietaires (nom, prenom, telephone, email, adresse) VALUES (?,?,?,?,?)")
            ->execute([$_POST['nom'], $_POST['prenom'], $_POST['telephone'], $_POST['email'], $_POST['adresse']]);
    } else {
        $pdo->prepare("UPDATE proprietaires SET nom=?, prenom=?, telephone=?, email=?, adresse=? WHERE id=?")
            ->execute([$_POST['nom'], $_POST['prenom'], $_POST['telephone'], $_POST['email'], $_POST['adresse'], $_POST['id']]);
    }
    header("Location: proprietaires.php"); exit;
}

// Chargement du propriétaire ciblé pour pré-remplir le formulaire d'édition
$edit = null;
if (isset($_GET['edit'])) {
    $e = $pdo->prepare("SELECT * FROM proprietaires WHERE id=?");
    $e->execute([$_GET['edit']]);
    $edit = $e->fetch();
}

// Suppression d'un propriétaire par son ID
if (isset($_GET['delete'])) {
    $pdo->prepare("DELETE FROM proprietaires WHERE id=?")->execute([$_GET['delete']]);
    header("Location: proprietaires.php"); exit;
}

// Récupération de tous les propriétaires triés par nom
$data = $pdo->query("SELECT * FROM proprietaires ORDER BY nom")->fetchAll();
<?php
require_once "includes/auth.php"; // Vérifie l'authentification avant toute action
require_once "config/db.php";

// Traitement du formulaire d'ajout ou de modification
if ($_POST) {
    if (empty($_POST['id'])) {
        // Aucun ID fourni → création d'un nouveau vétérinaire
        $pdo->prepare("INSERT INTO veterinaires(nom,specialite,telephone) VALUES(?,?,?)")
            ->execute([$_POST['nom'], $_POST['specialite'], $_POST['telephone']]);
    } else {
        // ID présent → mise à jour du vétérinaire existant
        $pdo->prepare("UPDATE veterinaires SET nom=?, specialite=?, telephone=? WHERE id=?")
            ->execute([$_POST['nom'], $_POST['specialite'], $_POST['telephone'], $_POST['id']]);
    }
    header("Location: veterinaires.php");
    exit;
}

// Récupération de tous les vétérinaires triés alphabétiquement
$data = $pdo->query("SELECT * FROM veterinaires ORDER BY nom ASC")->fetchAll();

// Rendu HTML : tableau avec badge spécialité et actions édition/suppression
// htmlspecialchars() sur chaque sortie pour prévenir les failles XSS
?>
<table>
  <thead>
    <tr>
      <th>Nom du vétérinaire</th>
      <th>Spécialité</th>
      <th>Téléphone</th>
      <th style="text-align:right;">Actions</th>
    </tr>
  </thead>
  <tbody>
    <?php foreach ($data as $r): ?>
      <tr>
        <td style="font-weight:600;"><?= htmlspecialchars($r['nom']) ?></td>
        <td>
          <span style="background:#eff6ff; color:#3b82f6; padding:4px 10px; border-radius:20px;">
            <?= htmlspecialchars($r['specialite']) ?>
          </span>
        </td>
        <td><?= htmlspecialchars($r['telephone']) ?></td>
        <td style="text-align:right;">
          <a href="?edit=<?= $r['id'] ?>"   title="Modifier">✏️</a>
          <!-- Confirmation côté client avant suppression définitive -->
          <a href="?delete=<?= $r['id'] ?>" title="Supprimer"
             onclick="return confirm('Supprimer ce vétérinaire ?')">🗑️</a>
        </td>
      </tr>
    <?php endforeach; ?>
  </tbody>
</table>
/* ── ZONE DE CONTENU PRINCIPAL ───────────────────────────────────
   flex: 1 = prend tout l'espace restant après la sidebar.
   overflow-y: auto = scroll vertical si le contenu est trop long.
─────────────────────────────────────────────────────────────── */
.main-content {
    flex: 1;
    padding: 30px;
    overflow-y: auto;
}

/* ── BARRE LATÉRALE (SIDEBAR) ────────────────────────────────────
   Colonne fixe à gauche, toujours aussi haute que l'écran.
─────────────────────────────────────────────────────────────── */
.sidebar {
    width: 240px;
    background: #111827;
    min-height: 100vh;
}
.sidebar a {
    color: #cbd5e1;
    display: block;
    padding: 10px;
    border-radius: 6px;
    text-decoration: none;
    margin: 4px 0;
    transition: all .15s;
}
.sidebar a:hover {
    background: #1f2937;
    color: #fff;
}

/* ── BANDEAU HÉRO ────────────────────────────────────────────────
   position: relative est indispensable pour que l'overlay (::after)
   et le texte se positionnent correctement par-dessus l'image.
─────────────────────────────────────────────────────────────── */
.hero-banner {
    height: 260px;
    background: url("assets/img/colvert_sport_banner.jpg") center center / cover no-repeat;
    position: relative;
    border-radius: 18px;
    overflow: hidden;
    margin-bottom: 30px;
}
.hero-banner::after {
    content: "";
    position: absolute;
    inset: 0;
    background: linear-gradient(to right, rgba(0,0,0,0.65), rgba(0,0,0,0.25));
}

// Conclusion

Rôle dans le projet

Responsable de la couche données : conception du schéma relationnel (7 tables, PK/FK), rédaction et test des requêtes SQL complexes (jointures, champs calculés), et développement des pages CRUD propriétaires, vétérinaires et consultations. Co-responsable de l'harmonisation CSS globale.

Défis rencontrés

Conception d'un modèle relationnel cohérent sur 7 tables avec contraintes référentielles. Rédaction des requêtes SQL avancées (taux de paiement, CA total, âge animal calculé). Harmonisation du style CSS sur l'ensemble des pages de l'intranet sans introduire de régressions.

Solutions apportées

Utilisation de requêtes préparées PDO pour toutes les opérations. Variables CSS et classes réutilisables pour garantir la cohérence visuelle. Schéma relationnel vérifié avec PhpMyAdmin avant implémentation. Suppression sécurisée avec confirmation JavaScript.

Améliorations possibles

Ajouter des vues SQL pour simplifier les requêtes récurrentes. Mettre en place des contraintes ON DELETE CASCADE ou RESTRICT selon les entités. Compléter les requêtes de reporting (taux d'occupation, statistiques par espèce). Optimiser les index pour les performances.

CD

Corey Defranc (CD)

// Analyste · UML · Architecture Web
Diagramme de classes UML Architecture application Structure includes/config
<?php
// config/db.php — Point de connexion centralisé PDO
// Inclus par toutes les pages via require_once "config/db.php"
$pdo = new PDO(
    "mysql:host=localhost;dbname=lisa_horse;charset=utf8mb4",
    "root",
    "",
    [
        PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
        PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
    ]
);
// ATTR_ERRMODE_EXCEPTION : toute erreur SQL lève une exception
// FETCH_ASSOC            : résultats retournés sous forme de tableaux associatifs
?>
// Structure de l'application web — Clinique Lisa Horse
// Architecture mise en place avec Kevin Leger

Lisa Horse/
├── index.php                 // Accueil public (hero + boutons)
├── planning_public.php       // Calendrier public FullCalendar
├── dashboard.php             // Tableau de bord intranet
├── animaux.php               // CRUD animaux
├── rdv.php                   // CRUD rendez-vous
├── proprietaires.php         // CRUD propriétaires
├── veterinaires.php          // CRUD vétérinaires
├── compte_rendu_medical.php  // CRUD consultations
├── service_facturation.php   // CRUD factures
│
├── auth/
│   ├── login.php             // Formulaire de connexion
│   ├── login_ex.php          // Gestion erreurs login
│   └── logout.php            // Destruction de session
│
├── includes/
│   ├── auth.php              // Vérification session (require_once)
│   ├── header.php            // <head> + Bootstrap + fonts
│   ├── menu.php              // Sidebar navigation
│   ├── hero_banner.php       // Bandeau hero réutilisable
│   └── footer.php            // Pied de page commun
│
├── config/
│   └── db.php                // Connexion PDO centralisée
│
├── assets/
│   ├── style.css             // CSS global (sidebar, layout)
│   └── img/                  // Images (hero, bannières)
│
└── lisa_horse.sql            // Script SQL BDD (7 tables)

// Conclusion

Rôle dans le projet

Analyste et co-architecte de l'application. A participé à la conception du diagramme de classes UML (7 classes, associations, multiplicités, visibilité +/-) sur Win Design, et à la mise en place de la structure globale PHP (includes, config, auth) avec Kevin Leger.

Défis rencontrés

Modélisation UML rigoureuse en respectant la notation standard (visibilité, multiplicités, associations vs compositions). Organisation de l'architecture PHP pour permettre la réutilisation des includes sur toutes les pages sans redondance de code.

Solutions apportées

Conception UML sur Win Design avec relecture croisée Mohamed Amine. Pattern includes/ centralisé : header, menu, footer, hero_banner et auth inclus systématiquement via require_once. Fichier db.php unique garantissant une connexion PDO cohérente dans tout le projet.

Améliorations possibles

Étendre le diagramme UML avec les diagrammes de séquence (authentification, CRUD). Exporter l'architecture vers un autoloader PSR-4 pour un code plus maintenable. Ajouter un diagramme d'activité pour les flux métier principaux.

// Diagramme de classes UML
Win Design · 7 classes · réalisé par Corey & Mohamed Amine
id_proprietaire id_animal id_utilisateur id_animal id_utilisateur id_proprietaire UTILISATEUR PK id_utilisateur INT nom_user VARCHAR(50) prenom_user VARCHAR(50) identifiant VARCHAR(50) mot_de_passe VARCHAR(255) role VARCHAR(30) VETERINAIRE PK id_veterinaire INT nom VARCHAR(100) specialite VARCHAR(50) telephone VARCHAR(15) PROPRIETAIRE PK id_proprietaire INT nom_prop VARCHAR(50) prenom_prop VARCHAR(50) telephone VARCHAR(15) email VARCHAR(100) ville VARCHAR(50) ANIMAL PK id_animal INT nom_animal VARCHAR(50) type_animal VARCHAR(30) race VARCHAR(50) sexe CHAR(1) date_naissance DATE FK id_proprietaire INT RENDEZ_VOUS PK id_rdv INT date_heure DATETIME motif VARCHAR(255) FK id_animal INT FK id_utilisateur INT CONSULTATION PK id_consultation INT date_consult DATE compte_rendu TEXT FK id_animal INT FK id_utilisateur INT FACTURE PK id_facture INT date_facture DATE montant DECIMAL(10,2) statut VARCHAR(20) FK id_proprietaire INT
PK Clé primaire (souligné, or) FK Clé étrangère (bleu, italique) Relation (→ FK pointe vers PK)
// Dictionnaire des données
7 tables · 38 attributs · réalisé par Corey & Mohamed Amine
Entité Attribut Type SQL Taille Contraintes Description
UTILISATEUR id_utilisateur INT - PK Identifiant unique du personnel
nom_user VARCHAR 50 Requis Nom du membre du personnel
prenom_user VARCHAR 50 Requis Prénom du membre du personnel
identifiant VARCHAR 50 Unique, Requis Login de connexion
mot_de_passe VARCHAR 255 Requis Mot de passe haché
role VARCHAR 30 Requis Admin / Vétérinaire / Secrétaire
PROPRIETAIRE id_proprietaire INT - PK Identifiant unique du client
nom_prop VARCHAR 50 Requis Nom de famille
prenom_prop VARCHAR 50 Requis Prénom
telephone VARCHAR 15 Requis Numéro de contact
email VARCHAR 100 Requis Adresse courriel
ville VARCHAR 50 Requis Ville de résidence
ANIMAL id_animal INT - PK Identifiant unique de l'animal
nom_animal VARCHAR 50 Requis Nom (ex: Bella)
type_animal VARCHAR 30 Requis Cheval, Chien, Chat
race VARCHAR 50 Optionnel Ex: Pur-sang, Labrador
sexe CHAR 1 Requis M ou F
date_naissance DATE AAAA-MM-JJ Requis Date de naissance
id_proprietaire INT - FK → PROPRIETAIRE Lien vers le propriétaire
RENDEZ_VOUS id_rdv INT - PK Identifiant unique du RDV
date_heure DATETIME AAAA-MM-JJ HH:MM Requis Date et heure prévues
motif VARCHAR 255 Requis Raison de la visite
id_animal INT - FK → ANIMAL Animal concerné
id_utilisateur INT - FK → UTILISATEUR Vétérinaire en charge
CONSULTATION id_consultation INT - PK Identifiant unique de la consultation
date_consult DATE AAAA-MM-JJ Requis Date effective
compte_rendu TEXT - Requis Détail médical et observations
id_animal INT - FK → ANIMAL Animal ausculté
id_utilisateur INT - FK → UTILISATEUR Vétérinaire rédacteur
FACTURE id_facture INT - PK Identifiant unique de la facture
date_facture DATE AAAA-MM-JJ Requis Date d'émission
montant DECIMAL 10,2 Requis Montant en euros
statut VARCHAR 20 Requis Payée / En attente / Annulée
id_proprietaire INT - FK → PROPRIETAIRE Client facturé
VETERINAIRE id_veterinaire INT - PK Identifiant unique du vétérinaire
nom VARCHAR 100 Requis Nom (ex: Dr Leroux)
specialite VARCHAR 50 Requis Ex: Équin, Chirurgie
telephone VARCHAR 15 Requis Contact professionnel

Bilan du projet

✅ Points forts

  • Séparation claire entre la partie publique et l'intranet sécurisé
  • Code HTML5/CSS3 lisible et homogène, sans framework externe
  • Requêtes SQL avec jointures multi-tables et champs calculés
  • Organisation Kanban efficace via Trello (4 colonnes)
  • Collaboration en temps réel via Live Share (LAN partagé)
  • Application fonctionnelle déployée localement sous WAMP 64

⚡ Axes d'amélioration

  • Présentation finale à finaliser et peaufiner
  • Commentaires du code à compléter (PHPDoc)
  • Débogage de certaines pages encore en cours
  • Sécurisation des suppressions avec tokens CSRF
  • Gestion des rôles utilisateurs (admin, vétérinaire, secrétaire)
  • Tests unitaires et fonctionnels à ajouter