# 📚 GUIDE COMPLET - CRÉATION DES MODULES DE COURS SQL

## 🎯 OBJECTIF
Créer des modules de cours SQL complets avec leçons, exemples exécutables et exercices, insérés en base de données Oracle.

---

## ⚠️ PIÈGES CRITIQUES À ÉVITER

### 🚨 PIÈGE #1 : Échappement PHP vs SQL
**NE JAMAIS confondre les deux types d'échappement !**

```php
// ❌ ERREUR FATALE - Parse Error
'description' => 'Les pays d''Europe'  // '' = Échappement SQL, PAS PHP !

// ✅ CORRECT - Dans les tableaux PHP
'description' => 'Les pays d\'Europe'   // \' = Échappement PHP
"description" => "Les pays d'Europe"    // Ou guillemets doubles

// ✅ CORRECT - Pour l'insertion SQL
$desc_safe = str_replace("'", "''", $description);  // '' = Échappement SQL
```

**Règle d'or** : `\'` dans les tableaux PHP, `''` dans str_replace pour le SQL !

### 🚨 PIÈGE #2 : Sidebar non mise à jour
**Après CHAQUE module créé, mettre à jour `html/page/layout/sidebar.php` !**

Sans ça, les leçons ne seront pas accessibles dans l'interface web.

### 🚨 PIÈGE #3 : Slugs incorrects
**Les slugs dans la sidebar DOIVENT correspondre EXACTEMENT aux slugs en base !**

Vérifier avec :
```bash
php check_slugs.php
```

### 🚨 PIÈGE #4 : Échappement dans les INSERT SQL ⚠️ CRITIQUE
**ATTENTION : Ne JAMAIS utiliser `\'` dans les chaînes SQL entre guillemets doubles !**

```php
// ❌ ERREUR - L'INSERT échoue silencieusement (pas d'ID récupéré)
$sql = "INSERT INTO COURS_LECONS (..., sous_titre, objectifs, ...)
        VALUES (...,
                'Comprendre le concept et l\'utilité des jointures',  // ❌ MAUVAIS
                '<ul><li>Comprendre ce qu\'est une jointure</li></ul>',  // ❌ MAUVAIS
                ...)";

// ✅ CORRECT - Utiliser '' (double apostrophe) pour échapper en SQL
$sql = "INSERT INTO COURS_LECONS (..., sous_titre, objectifs, ...)
        VALUES (...,
                'Comprendre le concept et l''utilité des jointures',  // ✅ BON
                '<ul><li>Comprendre ce qu''est une jointure</li></ul>',  // ✅ BON
                ...)";
```

**Symptômes du problème** :
- L'INSERT retourne success = true MAIS la leçon n'est pas en base
- Warning "Undefined array key 0" lors de la récupération de l'ID
- Certaines leçons s'insèrent, d'autres non (selon qu'elles ont des apostrophes ou non)
- Le COUNT final est inférieur au nombre de leçons attendu

**Solution** :
- Dans les chaînes PHP entre guillemets doubles `"..."` utilisées pour SQL :
  - Utiliser `''` (double apostrophe) pour échapper les apostrophes SQL
  - OU ne pas mettre d'apostrophes du tout (utiliser des guillemets doubles)
- Pour le contenu HTML : toujours utiliser `str_replace("'", "''", $contenu)`

**Vérification après insertion** :
```bash
# Compter les leçons insérées
php -r "require 'api/config/database.php'; \$db = new Database(); \$db->connect(); \$r = \$db->executeQuery('SELECT COUNT(*) as cnt FROM COURS_LECONS WHERE module_id = X'); echo \$r['data'][0]['cnt'];"
```

---

## 📊 STRUCTURE DE LA BASE DE DONNÉES

### Tables de Cours (à remplir)
```
COURS_LECONS
├─ lecon_id (NUMBER, PK, auto via SEQ_COURS_LECON.NEXTVAL)
├─ module_id (NUMBER) - ID du module (1-13)
├─ slug (VARCHAR2) - identifiant unique (ex: 'select-basics')
├─ titre (VARCHAR2) - titre de la leçon
├─ sous_titre (VARCHAR2) - sous-titre descriptif
├─ contenu_html (CLOB) - contenu HTML de la leçon
├─ objectifs (CLOB) - liste HTML des objectifs
├─ ordre_affichage (NUMBER) - ordre dans le module
├─ duree_minutes (NUMBER) - durée estimée
└─ difficulte (VARCHAR2) - 'DEBUTANT', 'INTERMEDIAIRE', 'AVANCE'

COURS_EXEMPLES
├─ exemple_id (NUMBER, PK, auto via SEQ_COURS_EXEMPLE.NEXTVAL)
├─ lecon_id (NUMBER, FK) - lien vers COURS_LECONS
├─ titre (VARCHAR2) - titre de l'exemple
├─ explication (CLOB) - description de l'exemple
├─ code_sql (CLOB) - requête SQL exécutable
├─ resultat_attendu (CLOB) - description du résultat
└─ ordre_affichage (NUMBER) - ordre d'affichage

COURS_EXERCICES
├─ exercice_id (NUMBER, PK, auto via SEQ_COURS_EXERCICE.NEXTVAL)
├─ lecon_id (NUMBER, FK) - lien vers COURS_LECONS
├─ titre (VARCHAR2) - titre de l'exercice
├─ enonce (CLOB) - énoncé de l'exercice
├─ difficulte (VARCHAR2) - 'FACILE', 'MOYEN', 'DIFFICILE'
├─ solution_sql (CLOB) - solution SQL
└─ ordre_affichage (NUMBER) - ordre d'affichage
```

### Tables de Données (à utiliser dans les exemples)
```
EMPLOYES
├─ employe_id, nom, prenom, email, telephone
├─ date_embauche, poste, salaire, commission
├─ manager_id, departement_id
└─ 107 lignes de données

DEPARTEMENTS
├─ departement_id, nom_departement
├─ manager_id, localisation_id
└─ 27 départements

PAYS
├─ pays_code (ex: 'FR', 'US')
├─ nom_pays, capitale, population
├─ superficie, pib, continent
└─ region_id

REGIONS
├─ region_id (1=Europe, 2=Ameriques, 3=Asie, 4=Moyen-Orient et Afrique)
└─ nom_region

LOCALISATIONS
├─ localisation_id, adresse_rue, code_postal
├─ ville, province_etat
└─ pays_code
```

---

## ✅ RÈGLES IMPÉRATIVES

### 1. Échappement SQL
```php
// TOUJOURS échapper le contenu HTML AVANT de le mettre dans le SQL
$contenu_safe = str_replace("'", "''", $contenu_html);

// JAMAIS comme ça (casse tout le SQL):
$db->executeDML(str_replace("'", "''", $sql)); // ❌ NON !
```

### 2. Récupération des IDs
```php
// Avec ORDS (REST), on NE PEUT PAS utiliser CURRVAL
// On récupère l'ID par SELECT après COMMIT

$db->executeDML($sql);
$db->executeDML("COMMIT");
$result = $db->executeQuery("SELECT lecon_id FROM COURS_LECONS WHERE slug = 'mon-slug' AND module_id = X");
$lecon_id = $result['data'][0]['lecon_id']; // Clés en MINUSCULES avec ORDS
```

### 3. Colonnes exactes
```php
// COURS_EXEMPLES - Colonnes réelles
INSERT INTO COURS_EXEMPLES (exemple_id, lecon_id, titre, explication, code_sql, resultat_attendu, ordre_affichage)
// PAS de 'description' ni 'est_executable' !

// COURS_EXERCICES - Colonnes réelles
INSERT INTO COURS_EXERCICES (exercice_id, lecon_id, titre, enonce, difficulte, solution_sql, ordre_affichage)
// PAS de 'description' ni 'points' !
```

### 4. Exemples SQL EXÉCUTABLES
```php
// Chaque exemple DOIT être une vraie requête qui fonctionne
'sql' => 'SELECT nom, prenom FROM EMPLOYES WHERE departement_id = 60'
// PAS de pseudo-code, PAS de ...

// Résultat attendu = description de ce qui est retourné
'resultat_attendu' => '5 employés du département IT avec NOM et PRENOM'
```

### 5. Ordre et cohérence
- Chaque leçon a un `ordre_affichage` qui commence à 1
- Chaque exemple a un `ordre` dans son tableau
- Chaque exercice a un `ordre` dans son tableau
- Les IDs sont auto-générés par les séquences

---

## 📝 TEMPLATE COMPLET D'UN MODULE

```php
<?php
require_once __DIR__ . '/../../api/config/database.php';

$db = new Database();
$db->connect();

echo "INSERTION MODULE X - TITRE DU MODULE\n";
echo "====================================\n\n";

// Nettoyage du module
echo "Nettoyage du module X...\n";
$db->executeDML("DELETE FROM COURS_EXERCICES WHERE lecon_id IN (SELECT lecon_id FROM COURS_LECONS WHERE module_id = X)");
$db->executeDML("DELETE FROM COURS_EXEMPLES WHERE lecon_id IN (SELECT lecon_id FROM COURS_LECONS WHERE module_id = X)");
$db->executeDML("DELETE FROM COURS_LECONS WHERE module_id = X");
$db->executeDML("COMMIT");

echo "Insertion des leçons...\n\n";

// ====================================
// LEÇON X.1 : TITRE DE LA LEÇON
// ====================================
$contenu_lecon1 = <<<HTML
<div class="lecon-content">
    <h2>Titre de la Leçon</h2>

    <section>
        <h3>Introduction</h3>
        <p>Explication claire du concept...</p>
    </section>

    <section>
        <h3>Syntaxe</h3>
        <div class="syntax-block">
            <pre><code>SELECT colonne FROM table WHERE condition;</code></pre>
        </div>
        <p>Explication de la syntaxe...</p>
    </section>

    <section>
        <h3>Exemples d'utilisation</h3>
        <p>Démonstration avec des cas concrets...</p>
    </section>

    <section>
        <h3>Bonnes pratiques</h3>
        <ul>
            <li>✅ Conseil 1</li>
            <li>✅ Conseil 2</li>
            <li>⚠️ Attention à...</li>
        </ul>
    </section>
</div>
HTML;

// Échapper le contenu AVANT de créer le SQL
$contenu_lecon1_safe = str_replace("'", "''", $contenu_lecon1);

// Insertion de la leçon
$sql = "INSERT INTO COURS_LECONS (lecon_id, module_id, slug, titre, sous_titre, contenu_html, objectifs, ordre_affichage, duree_minutes, difficulte)
        VALUES (SEQ_COURS_LECON.NEXTVAL, X, 'slug-unique', 'Titre de la Leçon',
                'Sous-titre descriptif',
                '$contenu_lecon1_safe',
                '<ul><li>Objectif 1</li><li>Objectif 2</li></ul>',
                1, 20, 'DEBUTANT')";
$db->executeDML($sql);
$db->executeDML("COMMIT");

// Récupérer l'ID de la leçon
$result = $db->executeQuery("SELECT lecon_id FROM COURS_LECONS WHERE slug = 'slug-unique' AND module_id = X");
$lecon1_id = $result['data'][0]['lecon_id'];
echo "✓ Leçon X.1 insérée (ID: $lecon1_id)\n";

// Exemples pour leçon X.1
$exemples_lecon1 = [
    [
        'titre' => 'Titre exemple 1',
        'description' => 'Explication de ce que fait cet exemple',
        'sql' => 'SELECT nom, prenom FROM EMPLOYES WHERE departement_id = 60',
        'resultat_attendu' => '5 employés du département IT',
        'ordre' => 1
    ],
    [
        'titre' => 'Titre exemple 2',
        'description' => 'Autre cas d\'utilisation',
        'sql' => 'SELECT COUNT(*) as total FROM EMPLOYES',
        'resultat_attendu' => 'Nombre total d\'employés (107)',
        'ordre' => 2
    ],
    // Minimum 4 exemples par leçon
];

foreach ($exemples_lecon1 as $ex) {
    $titre = str_replace("'", "''", $ex['titre']);
    $desc = str_replace("'", "''", $ex['description']);
    $sql_ex = str_replace("'", "''", $ex['sql']);
    $resultat = str_replace("'", "''", $ex['resultat_attendu']);

    $insert = "INSERT INTO COURS_EXEMPLES (exemple_id, lecon_id, titre, explication, code_sql, resultat_attendu, ordre_affichage)
               VALUES (SEQ_COURS_EXEMPLE.NEXTVAL, $lecon1_id, '$titre', '$desc', '$sql_ex', '$resultat', {$ex['ordre']})";
    $result = $db->executeDML($insert);
    if (!$result['success']) {
        echo "  ERREUR insertion exemple '$titre': " . ($result['error'] ?? 'Inconnue') . "\n";
        die();
    }
}
$db->executeDML("COMMIT");
echo "  → " . count($exemples_lecon1) . " exemples exécutables ajoutés\n";

// Exercices pour leçon X.1
$exercices_lecon1 = [
    [
        'titre' => 'Exercice 1',
        'description' => 'Énoncé clair de l\'exercice',
        'difficulte' => 'FACILE',
        'solution' => 'SELECT nom FROM EMPLOYES WHERE salaire > 10000',
        'ordre' => 1
    ],
    [
        'titre' => 'Exercice 2',
        'description' => 'Énoncé plus complexe',
        'difficulte' => 'MOYEN',
        'solution' => 'SELECT departement_id, COUNT(*) FROM EMPLOYES GROUP BY departement_id',
        'ordre' => 2
    ],
    // Minimum 4 exercices par leçon
];

foreach ($exercices_lecon1 as $exo) {
    $titre = str_replace("'", "''", $exo['titre']);
    $desc = str_replace("'", "''", $exo['description']);
    $solution = str_replace("'", "''", $exo['solution']);

    $insert = "INSERT INTO COURS_EXERCICES (exercice_id, lecon_id, titre, enonce, difficulte, solution_sql, ordre_affichage)
               VALUES (SEQ_COURS_EXERCICE.NEXTVAL, $lecon1_id, '$titre', '$desc', '{$exo['difficulte']}', '$solution', {$exo['ordre']})";
    $db->executeDML($insert);
}
$db->executeDML("COMMIT");
echo "  → " . count($exercices_lecon1) . " exercices ajoutés\n\n";

// COMMIT FINAL
$db->executeDML("COMMIT");

echo "\n" . str_repeat("=", 50) . "\n";
echo "✅ MODULE X COMPLET INSÉRÉ\n";
echo str_repeat("=", 50) . "\n\n";

// Vérifications
$verif_lecons = $db->executeQuery("SELECT COUNT(*) as cnt FROM COURS_LECONS WHERE module_id = X");
$verif_exemples = $db->executeQuery("SELECT COUNT(*) as cnt FROM COURS_EXEMPLES WHERE lecon_id IN (SELECT lecon_id FROM COURS_LECONS WHERE module_id = X)");
$verif_exercices = $db->executeQuery("SELECT COUNT(*) as cnt FROM COURS_EXERCICES WHERE lecon_id IN (SELECT lecon_id FROM COURS_LECONS WHERE module_id = X)");

echo "📊 Statistiques Module X:\n";
echo "  • Leçons     : " . $verif_lecons['data'][0]['cnt'] . "\n";
echo "  • Exemples   : " . $verif_exemples['data'][0]['cnt'] . " (tous exécutables)\n";
echo "  • Exercices  : " . $verif_exercices['data'][0]['cnt'] . "\n";
echo "\n";

$db->close();

echo "🎓 Le module X est maintenant complet avec :\n";
echo "   ✓ Contenu théorique détaillé\n";
echo "   ✓ Exemples SQL exécutables\n";
echo "   ✓ Exercices pratiques avec solutions\n";
```

---

## 🎓 CONTENU PÉDAGOGIQUE REQUIS

### Structure d'une leçon HTML
```html
<div class="lecon-content">
    <h2>Titre Principal</h2>

    <section>
        <h3>Qu'est-ce que [CONCEPT] ?</h3>
        <p>Définition claire et simple</p>
        <p>Analogie si possible (ex: "C'est comme...")</p>
    </section>

    <section>
        <h3>Syntaxe</h3>
        <div class="syntax-block">
            <pre><code>SYNTAXE SQL ICI</code></pre>
        </div>
        <ul>
            <li><code>MOT_CLE</code> : explication</li>
            <li><code>AUTRE</code> : explication</li>
        </ul>
    </section>

    <section>
        <h3>Exemple simple</h3>
        <div class="syntax-block">
            <pre><code>SELECT nom FROM EMPLOYES;</code></pre>
        </div>
        <p>Explication de ce que fait cet exemple</p>
    </section>

    <section>
        <h3>Cas d'usage</h3>
        <ul>
            <li>📊 Utilisation 1</li>
            <li>🔍 Utilisation 2</li>
        </ul>
    </section>

    <section>
        <h3>⚠️ Points d'attention</h3>
        <p>Pièges à éviter, erreurs courantes</p>
    </section>

    <section>
        <h3>Bonnes pratiques</h3>
        <ul>
            <li>✅ À faire</li>
            <li>❌ À éviter</li>
        </ul>
    </section>
</div>
```

### Exemples SQL
Chaque exemple DOIT :
1. **Être exécutable** sur les tables EMPLOYES, DEPARTEMENTS, PAYS, LOCALISATIONS
2. **Retourner un résultat** concret
3. **Illustrer un concept** spécifique
4. **Progresser en complexité**

```php
[
    'titre' => 'Nom court et clair',
    'description' => 'Ce que fait l\'exemple et pourquoi',
    'sql' => 'SELECT ... FROM ... WHERE ...', // Requête complète et valide
    'resultat_attendu' => 'Description du résultat (ex: "10 lignes avec NOM, SALAIRE")',
    'ordre' => 1
]
```

### Exercices
Chaque exercice DOIT :
1. **Avoir un énoncé clair** qui dit exactement quoi faire
2. **Avoir une solution SQL** complète et correcte
3. **Progresser en difficulté** : FACILE → MOYEN → DIFFICILE

```php
[
    'titre' => 'Titre de l\'exercice',
    'description' => 'Énoncé précis : "Affichez... depuis la table... où..."',
    'difficulte' => 'FACILE', // ou 'MOYEN', 'DIFFICILE'
    'solution' => 'SELECT ... FROM ... WHERE ...', // Solution complète
    'ordre' => 1
]
```

---

## 📋 LISTE DES 13 MODULES

```
MODULE 1 : SELECT et Bases ✅ FAIT
MODULE 2 : Filtrage et Tri (WHERE, ORDER BY)
MODULE 3 : Fonctions SQL
MODULE 4 : Agrégation (GROUP BY, HAVING)
MODULE 5 : Jointures
MODULE 6 : Sous-requêtes
MODULE 7 : Opérateurs ensemblistes (UNION, INTERSECT, MINUS)
MODULE 8 : Modification des données (INSERT, UPDATE, DELETE)
MODULE 9 : Création de tables (DDL)
MODULE 10 : Vues
MODULE 11 : Transactions
MODULE 12 : Fonctions avancées (Window Functions)
MODULE 13 : Introduction
```

---

## 🚀 LANCEMENT DU SCRIPT

### Commande
```bash
php data/seeds/MODULE_X_COMPLET.php
```

### Vérification
```bash
# Vérifier le contenu inséré
php -r "require 'api/config/database.php'; \$db = new Database(); \$db->connect(); \$r = \$db->executeQuery('SELECT COUNT(*) as cnt FROM COURS_LECONS WHERE module_id = X'); echo 'Leçons: ' . \$r['data'][0]['cnt'] . PHP_EOL;"
```

### Accès Web
```
http://localhost/app/api/cours/modules/X/lecons
http://localhost/app/api/cours/lecons/XXX/exemples
http://localhost/app/api/cours/lecons/XXX/exercices
```

---

## ⚠️ CHECKLIST AVANT DE GÉNÉRER

- [ ] Le module_id est correct (1-13)
- [ ] Les slugs sont uniques
- [ ] Tous les exemples SQL sont testables sur les tables existantes
- [ ] Il y a AU MINIMUM 4 exemples par leçon
- [ ] Il y a AU MINIMUM 4 exercices par leçon
- [ ] Le contenu HTML est échappé AVANT l'insertion
- [ ] Les clés de récupération sont en minuscules (`['lecon_id']`, `['cnt']`)
- [ ] Pas de colonnes inexistantes (`description`, `est_executable`, `points`)
- [ ] COMMIT après chaque insertion de leçon
- [ ] Vérifications finales à la fin du script

---

## 📖 EXEMPLE DE MODULE COMPLET

Voir le fichier : **data/seeds/MODULE_1_COMPLET.php**

C'est le template de référence absolue pour tous les modules.

---

## 💡 CONSEILS PÉDAGOGIQUES

### Pour les débutants (Module 1-3)
- Exemples très simples
- Beaucoup d'explications
- Pas de concepts avancés
- Résultats visuels et concrets

### Pour les intermédiaires (Module 4-8)
- Exemples plus complexes
- Combinaisons de concepts
- Cas d'usage réels
- Optimisation et bonnes pratiques

### Pour les avancés (Module 9-13)
- Scénarios complexes
- Performance et optimisation
- Architecture et conception
- Cas d'usage professionnels

---

## 🔍 DEBUGGING

### Problème : Exemples/Exercices = 0
**Cause** : Mauvaises colonnes dans INSERT
**Solution** : Vérifier `COURS_EXEMPLES` et `COURS_EXERCICES` structures

### Problème : Erreur SQL
**Cause** : Échappement incorrect
**Solution** : Échapper AVANT de créer le SQL, pas après

### Problème : ID non récupéré
**Cause** : CURRVAL ne fonctionne pas avec ORDS
**Solution** : SELECT après COMMIT avec le slug

### ⚠️ PROBLÈME CRITIQUE : Échappement PHP - Parse Error
**Erreur** : `Parse error: syntax error, unexpected single-quoted string "XXX", expecting "]"`

**Cause** : Utilisation de `''` (deux guillemets simples) dans les tableaux PHP
```php
// ❌ MAUVAIS - Cause Parse Error
'description' => 'Les pays d''Europe'
'sql' => 'SELECT * FROM PAYS WHERE continent = ''Europe'''
```

**Solution** : Utiliser l'échappement backslash `\'` OU des guillemets doubles
```php
// ✅ BON - Échappement avec backslash
'description' => 'Les pays d\'Europe'
'sql' => 'SELECT * FROM PAYS WHERE continent = \'Europe\''

// ✅ BON - Guillemets doubles
"description" => "Les pays d'Europe"
"sql" => "SELECT * FROM PAYS WHERE continent = 'Europe'"
```

**Règle d'or** :
- `''` (double quote simple) = Échappement SQL (pour les INSERT)
- `\'` (backslash quote) = Échappement PHP (dans les tableaux)
- Ne PAS les confondre !

### 🎯 Problème : Leçon non trouvée dans l'interface
**Erreur** : "La leçon que vous recherchez n'existe pas ou n'est pas encore disponible"

**Cause** : Les slugs dans la sidebar (`html/page/layout/sidebar.php`) ne correspondent pas aux slugs en base de données

**Solution** :
1. Vérifier les slugs réels en base :
```bash
php -r "require 'api/config/database.php'; $db = new Database(); $db->connect(); $r = $db->executeQuery('SELECT lecon_id, slug, titre FROM COURS_LECONS WHERE module_id = X ORDER BY ordre_affichage'); foreach($r['data'] as $row) { echo $row['slug'] . PHP_EOL; }"
```

2. Mettre à jour `html/page/layout/sidebar.php` avec les VRAIS slugs :
```html
<li class="menu-item">
  <a href="cours.php?slug=SLUG_EXACT_DE_LA_BASE" class="menu-link">
    <div data-i18n="Titre">Titre de la leçon</div>
  </a>
</li>
```

**IMPORTANT** : À CHAQUE nouveau module créé, mettre à jour la sidebar !

---

## 📋 CHECKLIST POST-CRÉATION MODULE

Après avoir créé un module, TOUJOURS :

- [ ] Exécuter le script PHP (`php data/seeds/MODULE_X_COMPLET.php`)
- [ ] Vérifier les statistiques finales (nombre de leçons, exemples, exercices)
- [ ] Générer le code sidebar avec `php generate_sidebar_module.php X`
- [ ] Copier-coller le code généré dans `html/page/layout/sidebar.php`
- [ ] Tester les URLs dans le navigateur
- [ ] Vérifier que les leçons s'affichent correctement

### 🛠️ Scripts Helper Disponibles

```bash
# Vérifier les slugs d'un module
php check_slugs.php

# Générer le code HTML pour la sidebar d'un module
php generate_sidebar_module.php 2

# Vérifier les structures de tables
php data/seeds/show_structures.php
```

---

## 📞 AIDE

Si erreur, vérifier :
1. Structure des tables avec `data/seeds/show_structures.php`
2. Exemples existants dans MODULE_1_COMPLET.php
3. Logs PHP pour erreurs SQL
4. Slugs en base vs sidebar avec `check_slugs.php`
