Ajouter

page utilisateur/create.php

Nous ajoutons un fichier utilisateur/create.php à la racine du site, contenant un formulaire permettant d’ajouter un nouvel utilisateur.

Méthodeutilisateur/create.php

Dans un premier temps, le fichier affiche un formulaire pour ajouter un utilisateur.

Ensuite, il vérifie si le formulaire a été soumis et traite l’ajout de l’utilisateur.

1
<?php
2
// ==============================
3
// CONNEXION À LA BASE DE DONNÉES
4
// ==============================
5
$db = require '../bdd.php';
6
7
// ==============================
8
// INCLUSION HELPERS
9
// ==============================
10
require '../helpers.php';
11
12
// ==============================
13
// TRAITEMENT DU FORMULAIRE
14
// ==============================
15
16
if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_POST['ajouter'])) {
17
18
  // ------------------------------
19
  // RÉCUPÉRATION DES DONNÉES
20
  // ------------------------------
21
22
  // Nettoyage des champs texte - supprime les espaces superflus
23
  $nom = trim($_POST['nom'] ?? '');
24
  $prenom = trim($_POST['prenom'] ?? '');
25
26
  // Cast en entier pour le champ âge
27
  $age = (int) ($_POST['age'] ?? 0);
28
29
  // Slug qui servira à la fois pour l’URL et le nom du fichier photo
30
  // La fonction slug() transforme une chaîne en format compatible URL 
31
  // (par exemple : "Jean Dupont" → "jean-dupont")
32
  // On s’assure que ce slug est unique : si un enregistrement existe déjà,
33
  // un suffixe numérique sera ajouté pour créer un slug distinct
34
  $baseSlug = slug("$prenom-$nom");
35
  $slug = $baseSlug;
36
37
  $i = 1;
38
  // Requête SQL avec paramètres nommés
39
  $sql = "SELECT * FROM utilisateurs WHERE slug = :slug";
40
  // Préparation de la requête (sécurité contre les injections SQL)
41
  $stmt = $db->prepare($sql);
42
  // Exécution de la requête avec le slug actuel
43
  $stmt->execute(['slug' => $slug]);
44
  // Tant qu’un enregistrement existe avec ce slug, on ajoute un suffixe numérique
45
  while ($stmt->fetch()) {
46
    $slug = "{$baseSlug}_{$i}";
47
    $stmt->execute(['slug' => $slug]);
48
    $i++;
49
  }
50
51
  // ------------------------------
52
  // GESTION DE LA PHOTO
53
  // ------------------------------
54
55
  // Dossier relatif de stockage des photos
56
  // par rapport à la racine du projet
57
  $relDir =  '../';
58
  // Dossier de stockage des photos
59
  $photoDir = 'images/photos/';
60
61
  // Création du dossier s’il n’existe pas
62
  if (!is_dir($relDir . $photoDir)) {
63
    mkdir($relDir . $photoDir, 0755, true);
64
  }
65
66
  // Cas 1 : une photo a été uploadée
67
  if (
68
    isset($_FILES['photo']) &&
69
    is_uploaded_file($_FILES['photo']['tmp_name'])
70
  ) {
71
72
    // Récupération de l’extension du fichier (en minuscule)
73
    $extension = strtolower(
74
      pathinfo($_FILES['photo']['name'], PATHINFO_EXTENSION)
75
    );
76
77
    // Nom final du fichier photo
78
    $photo = "{$photoDir}{$slug}.{$extension}";
79
80
    // Déplacement du fichier temporaire vers le dossier photos
81
    move_uploaded_file(
82
      $_FILES['photo']['tmp_name'],
83
      "{$relDir}{$photo}"
84
    );
85
86
    // Cas 2 : aucune photo envoyée → image par défaut
87
  } else {
88
89
    // Nom de la photo par défaut
90
    $photo = "{$photoDir}{$slug}.png";
91
92
    // Copie de l’image générique
93
    copy(
94
      "{$relDir}{$photoDir}photo.png",
95
      "{$relDir}{$photo}"
96
    );
97
  }
98
99
  // ------------------------------
100
  // INSERTION EN BASE DE DONNÉES
101
  // ------------------------------
102
103
  // Requête SQL avec paramètres nommés
104
  $sql = "INSERT INTO utilisateurs
105
              (nom, prenom, age, slug, photo)
106
          VALUES
107
              (:nom, :prenom, :age, :slug, :photo)";
108
109
  // Préparation de la requête (sécurité SQL)
110
  $statement = $db->prepare($sql);
111
112
  // Exécution avec liaison automatique des paramètres
113
  // compact() crée un tableau associatif :
114
  // ['nom' => $nom, 'prenom' => $prenom, ...]
115
  $statement->execute(compact('nom', 'prenom', 'age', 'slug', 'photo'));
116
117
  // ------------------------------
118
  // REDIRECTION
119
  // ------------------------------
120
121
  // Redirection après succès (évite la resoumission du formulaire)
122
  header('location:../utilisateur/index.php');
123
  exit;
124
}
125
126
// ==============================
127
// VARIABLES POUR LA VUE
128
// ==============================
129
130
$page_base = '../';
131
$page_title = 'Ajouter un utilisateur';
132
$page_styles = ['utilisateur', 'form'];
133
?>
134
135
<!DOCTYPE html>
136
<html lang='fr'>
137
138
<?php require "$page_base/head.php" ?>
139
140
<body>
141
  <?php require "$page_base/header.php" ?>
142
  <main>
143
    <h1>Ajouter</h1>
144
    <form action='' method='post' enctype='multipart/form-data'>
145
      <fieldset>
146
        <legend>Identité</legend>
147
        <ul>
148
          <li>
149
            <label for="nom">Nom</label>
150
            <input type='text' name='nom' id='nom'>
151
          </li>
152
153
          <li>
154
            <label for="prenom">Prénom</label>
155
            <input type='text' name='prenom' id='prenom'>
156
          </li>
157
158
          <li>
159
            <label for="age">Age</label>
160
            <input type='text' name='age' id='age'>
161
          </li>
162
        </ul>
163
      </fieldset>
164
      <fieldset>
165
        <legend>Photo</legend>
166
        <ul>
167
          <li>
168
            <label for="photo">Photo</label>
169
            <input type='file' name='photo' id='photo'>
170
          </li>
171
172
        </ul>
173
      </fieldset>
174
175
      <section class="form-buttons">
176
        <button type="submit" name="ajouter">Ajouter</button>
177
        <a href="utilisateur/index.php" class="btn-cancel">Annuler</a>
178
      </section>
179
180
    </form>
181
  </main>
182
  <?php require "$page_base/footer.php" ?>
183
</body>
184
185
</html>
1
<form action="" method="post" enctype="multipart/form-data">

Le formulaire transmet les données via la méthode POST et autorise l’envoi de fichiers grâce à l’attribut enctype.

La validation s’effectue sur la même URL que celle du fichier en cours, l’attribut action étant laissé vide.

1
<input type="text" name="nom">

L’attribut name définit le nom de la variable transmise lors de l’envoi du formulaire.

Dans la page de traitement, ces valeurs sont accessibles via la superglobale $_POST.

1
if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_POST['ajouter']))

Pour traiter les données, on commence par vérifier si le formulaire a été soumis en cliquant sur le bouton « Ajouter ».

1
$nom = $_POST['nom'] ?? ""

La valeur envoyée par le formulaire est récupérée depuis $_POST.

Si la clé 'nom' n’existe pas (par exemple si le formulaire n’a pas encore été soumis), une chaîne vide est utilisée par défaut.

1
INSERT INTO utilisateurs (nom, prenom, age, slug, photo) VALUES (:nom, :prenom, :age, :slug, :photo)

Requête SQL permettant d’ajouter un utilisateur.

1
compact('nom', 'prenom', 'age', 'slug', 'photo')

Crée un tableau associatif regroupant les informations de l’utilisateur : ['nom' => $nom, 'prenom' => $prenom, 'age' => $age, 'slug' => $slug, 'photo' => $photo].

Méthodehelpers.php

Un fichier helpers.php est un fichier qui contient des fonc­tions utilitaires globales réutilisables dans tout le projet.

Il permet :

  • d’éviter de répéter du code

  • de centraliser des fonctions communes

  • d’améliorer la lisibilité

  • d’organiser le projet proprement

On l’inclut généralement au début du projet et on y met uniquement des fonctions générales, indépendantes d’une page précise.

1
<?php
2
3
// permet de générer un slug à partir d'une chaîne de caractères
4
// un slug doit être url-friendly, c'est à dire sans espaces ni caractères spéciaux
5
// ex: "Jean Dupont" → "jean-dupont"
6
function slug(string $string): string
7
{
8
    // Supprimer les espaces inutiles
9
    $string = trim($string);
10
11
    // Convertir en minuscules (UTF-8 compatible)
12
    $string = mb_strtolower($string, 'UTF-8');
13
14
    // Remplacer les caractères accentués
15
    $string = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $string);
16
17
    // Remplacer tout ce qui n’est pas lettre ou chiffre par un tiret
18
    $string = preg_replace('/[^a-z0-9]+/', '-', $string);
19
20
    // Supprimer les tirets multiples
21
    $string = preg_replace('/-+/', '-', $string);
22
23
    // Supprimer les tirets en début et fin
24
    return trim($string, '-');
25
}
26
27
// permet d'afficher une ou plusieurs variables de manière lisible dans le navigateur
28
function dumpVar(mixed $data): void
29
{
30
    if (is_null($data)) {
31
        echo '<span class="dump-null">null</span>';
32
        return;
33
    }
34
35
    if (is_bool($data)) {
36
        echo '<span class="dump-bool">' . ($data ? 'true' : 'false') . '</span>';
37
        return;
38
    }
39
40
    if (is_int($data) || is_float($data)) {
41
        echo '<span class="dump-number">' . $data . '</span>';
42
        return;
43
    }
44
45
    if (is_string($data)) {
46
        echo '<span class="dump-string">"' . htmlspecialchars($data) . '"</span>';
47
        return;
48
    }
49
50
    if (is_array($data)) {
51
        echo '<details open>';
52
        echo '<summary class="dump-type">array: ' . count($data) . '</summary>';
53
54
        foreach ($data as $key => $value) {
55
            echo '<div>';
56
            echo '<span class="dump-key">' . htmlspecialchars((string)$key) . '</span> => ';
57
            dumpVar($value);
58
            echo '</div>';
59
        }
60
61
        echo '</details>';
62
        return;
63
    }
64
65
    if (is_object($data)) {
66
        echo '<details open>';
67
        echo '<summary class="dump-type">object: ' . get_class($data) . '</summary>';
68
        dumpVar(get_object_vars($data));
69
        echo '</details>';
70
        return;
71
    }
72
73
    var_dump($data);
74
}
75
76
function dump(mixed ...$data): void
77
{
78
    echo "<style>
79
.dump-wrapper {
80
    background: #2d2d2d;
81
    color: #f8f8f2;
82
    font-family: Consolas, Monaco, monospace;
83
    font-size: 14px;
84
    padding: 15px;
85
    border-radius: 6px;
86
    margin: 15px 0;
87
    overflow-x: auto;
88
}
89
90
.dump-wrapper hr {
91
    border: 0;
92
    border-top: 1px solid #444;
93
    margin: 10px 0;
94
}
95
96
.dump-wrapper details {
97
    margin-left: 15px;
98
}
99
100
.dump-wrapper summary {
101
    cursor: pointer;
102
    color: #66d9ef;
103
    font-weight: bold;
104
}
105
106
.dump-key {
107
    color: #a6e22e;
108
}
109
110
.dump-string {
111
    color: #e6db74;
112
}
113
114
.dump-number {
115
    color: #ae81ff;
116
}
117
118
.dump-bool {
119
    color: #fd971f;
120
}
121
122
.dump-null {
123
    color: #75715e;
124
    font-style: italic;
125
}
126
127
.dump-type {
128
    color: #66d9ef;
129
    font-style: italic;
130
131
}
132
</style>
133
<div class='dump-wrapper'>";
134
135
    foreach ($data as $item) {
136
        dumpVar($item);
137
        echo '<hr>';
138
    }
139
    echo '</div>';
140
}
141
142
function dd(mixed ...$data)
143
{
144
    dump(...$data);
145
    die;
146
}

La fonction slug()

Elle permet de transformer une chaîne de caractères en slug, c’est-à-dire un texte compatible avec une URL.

Exemple : "Jean Dupont""jean-dupont"

  • Suppression des espaces inutiles trim($string);

  • Passage en minuscules mb_strtolower($string);

  • Suppression des accents iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $string);

  • Remplacement des caractères non alphanumériques par des tirets preg_replace('/[^a-z0-9]+/', '-', $string);

  • Suppression des tirets multiples preg_replace('/-+/', '-', $string);

  • Suppression des tirets au début et à la fin trim($string, '-');

La Fonction dumpVar()

Permet d'afficher proprement une variable dans le navigateur.

Elle améliore var_dump() en rendant l’affichage plus lisible, notamment pour les tableaux.

Exemple : dumpVar($value);

La fonction dump()

Elle permet d’afficher plusieurs variables en une seule fois.

Le ...$data signifie que la fonction accepte un nombre illimité d’arguments.

dump() est une fonction de confort qui repose sur dumpVar().

Exemple : dump($value, $liste);

La fonction dd()

Elle permet d'afficher une variable et arrêter immédiatement le script.

dd($user);

Equivalent à :

dump($user);

die;

Très utile pour le débogage.

Exemple : dd($value);

Méthodecss/utilisateur-form.css

1
/* ====== Formulaire - Mobile First ====== */
2
form {
3
    display: flex;
4
    flex-direction: column;
5
    gap: 1rem;
6
    align-items: center;
7
}
8
9
/* ====== Fieldset ====== */
10
fieldset {
11
    border: 1px solid #ccc;
12
    border-radius: 6px;
13
    padding: 1rem;
14
    width: 100%;
15
    max-width: 350px;
16
    display: flex;
17
    flex-direction: column;
18
    gap: 0.8rem;
19
}
20
21
legend {
22
    font-weight: bold;
23
}
24
25
/* ====== Liste de champs ====== */
26
fieldset ul {
27
    list-style: none;
28
    padding: 0;
29
    margin: 0;
30
    display: flex;
31
    flex-direction: column;
32
    gap: 0.6rem;
33
}
34
35
fieldset li {
36
    display: flex;
37
    flex-direction: column;
38
    /* label au-dessus du champ */
39
    gap: 0.3rem;
40
    align-items: flex-start;
41
}
42
43
label {
44
    font-weight: 500;
45
}
46
47
input[type="text"],
48
input[type="file"] {
49
    padding: 0.4rem 0.5rem;
50
    border: 1px solid #aaa;
51
    border-radius: 4px;
52
    font-size: 1rem;
53
    width: 100%;
54
}
55
56
/* ====== Boutons empilés sur mobile ====== */
57
.form-buttons {
58
    display: flex;
59
    flex-direction: column;
60
    gap: 0.5rem;
61
    width: 80%;
62
    max-width: 600px;
63
}
64
65
/* Boutons */
66
button[type="submit"],
67
.btn-cancel {
68
    padding: 0.6rem 1.2rem;
69
    font-size: 1rem;
70
    border-radius: 4px;
71
    cursor: pointer;
72
    border: none;
73
    text-decoration: none;
74
    width: 100%;
75
}
76
77
button[type="submit"] {
78
    background-color: #0066cc;
79
    color: #fff;
80
}
81
82
button[type="submit"]:hover {
83
    background-color: #004999;
84
}
85
86
.btn-cancel {
87
    background-color: #ccc;
88
    color: #000;
89
}
90
91
.btn-cancel:hover {
92
    background-color: #aaa;
93
}
94
95
/* ====== Desktop / Large Screens ====== */
96
@media (min-width: 600px) {
97
    fieldset li {
98
        flex-direction: row;
99
        /* label devant le champ */
100
        align-items: center;
101
    }
102
103
    label {
104
        width: 120px;
105
        /* largeur fixe pour alignement */
106
        margin-bottom: 0;
107
    }
108
109
    input[type="text"],
110
    input[type="file"] {
111
        width: auto;
112
        flex: 1 1 auto;
113
    }
114
115
    .form-buttons {
116
        flex-direction: row;
117
        /* boutons côte à côte */
118
        gap: 1rem;
119
    }
120
121
    .form-buttons>* {
122
        margin: 1rem;
123
    }
124
125
    fieldset {
126
        width: 80%;
127
        max-width: 600px;
128
    }
129
}