Nous allons créer un projet en MVC contenant au moins trois tables : deux tables jointes par une relation N-N, avec une table de jointure entre les deux. Le projet sera développé en architecture MVC et en utilisant des packages Composer.
Les projets sont les suivants :
Vous allez créer un système de gestion d'évènements pour Eventbrite : l'utilisateur pourra créer des évènements et des utilisateurs, et les utilisateurs pourront s'inscrire à un ou plusieurs évènements. De même, on pourra ajouter un ou plusieurs utilisateurs à un évènement.
- Un évènement a plusieurs utilisateurs.
- Un utilisateur a plusieurs évènements.
Vous allez créer un tracker pour Netflix qui permettra de répertorier les films vus par un utilisateur, ainsi que la liste des utilisateurs ayant vu un film.
- Un utilisateur a vu plusieurs films.
- Un film a été vu par plusieurs utilisateurs.
Vous allez créer un système de panier pour Amazon : on pourra ajouter plusieurs produits à un client, et on pourra ajouter plusieurs clients à un produit.
- Un client a plusieurs produits.
- Un produit a plusieurs clients.
Vous allez créer un système de playlists pour Spotify : un utilisateur pourra ajouter plusieurs musiques à sa playlist personnelle, et une musique pourra être ajouter dans les playlist de plusieurs utilisateurs.
- Une playlist a plusieurs musiques.
- Une musique appartient à plusieurs playlists.
Vous allez créer le système de gestion de prêts de la bibliothèque Kindle. Un lecteur pourra louer plusieurs livres, et un livre pourra être loué à plusieurs utilisateurs.
- Un lecteur a plusieurs livres.
- Un livre est lu par plusieurs lecteurs.
Tous vos projets vont pouvoir se calquer sur le projet suivant. Vous allez adapter les tables et models par rapport à l'exemple ci-dessous :
Nous allons créer un système de gestion de classe et de cours.
- Chaque étudiant a plusieurs cours
- Chaque cours accueille plusieurs étudiants
Vous créérez les interfaces suivantes :
AJOUTS
- Ajout d'un nouvel étudiant
- Ajout d'un nouveau cours
- Attribution de plusieurs cours à un étudiant
LISTES
- Liste des étudiants
- Liste des cours
- Liste des inscriptions (liste de tous les cours et étudiants inscrits)
PAGES
- Page d'un étudiant et la liste de ses cours
- Page d'un cours et la liste des étudiants inscrits
- Dans chacune de ces pages, on pourra éditer les informations d'un étudiant ou les informations d'un cours
- Dans chacune de ces pages, on pourra supprimer les cours d'un étudiant, ou supprimer un étudiant d'un cours
Vous travaillerez en groupe avec Git :
- Un développeur créée le dépôt Git sur Github, en privé ou public.
- Il invite en administrateur les autres développeurs.
- Les autres développeurs vont cloner le dépôt Git sur leur ordinateur.
- Tout le monde pourra effectuer des commits et les pusher sur le même repository.
Vous trouverez une idée du résultat attendu ici : https://imgur.com/a/vinByNa. N'hésitez pas à adapter à votre projet ! Pensez à soigner le front une fois le back bien avancé.
-
Vous créérez le modèle conceptuel de base de données sur papier, en listant les tables, les relations entre les tables, la liste des champs à insérer dans les tables.
-
Une fois le modèle validé, vous créérez la base de données.
- Vous listerez dans un document toutes les routes nécessaires à ce projet et leurs méthodes (
GET students
,POST student
...).
- Vous utiliserez le projet
base-mvc
que vous installerez (ouverture du projet avec VSCode puiscomposer install
dans la console, et modification des fichiers de config de sorte à ce qu'ils correspondent à votre base de données, vos URL...).
- Traduisez les routes que vous aurez préparé au point 2. dans le fichier de routes de votre projet.
- C'est l'occasion de prévoir les contrôleurs nécessaires et les méthodes associées !
- Créez les controllers nécessaires au projet et les méthodes associées.
- Dans les controllers, appelez toutes les vues nécessaires aux affichages (
view('students.index')
par exemple), et créez les fichiers de vues.
- Créez un fichier Model par table. Pensez bien à hériter de la classe
Db
en déclarant le model ainsi :class Student extends Db { }
- Pour chaque Model :
- Déclarez la constante
TABLE_NAME
- Déclarez des attributs
protected
pour chacun des champs de la table correspondante - Déclarez vos setters
- Déclarez vos getters
- Déclarez la constante
Rappel setters :
public function setName($name) {
$this->name = $name;
return $name;
}
Rappel getters :
public function getName() {
return $this->name;
}
- Ajoutez les méthodes suivantes à tous vos models en les adaptant correctement pour chaque model :
Méthode save() : (sauvegarder un nouvel objet en bdd)
public function save() {
$data = [
"firstname" => $this->firstname(),
"surname" => $this->surname()
];
if ($this->id > 0) return $this->update();
$nouvelId = Db::dbCreate(self::TABLE_NAME, $data);
$this->setId($nouvelId);
return $this;
}
Méthode update() : (mettre à jour l'objet en bdd)
public function update() {
if ($this->id > 0) {
$data = [
"firstname" => $this->firstname(),
"surname" => $this->surname()
];
Db::dbUpdate(self::TABLE_NAME, $data);
return $this;
}
return;
}
Méthode delete() : (supprimer l'objet de la bdd)
public function delete() {
$data = [
'id' => $this->id(),
];
Db::dbDelete(self::TABLE_NAME, $data);
return;
}
Méthode findAll() : (retrouver tous les éléments du Model)
public static function findAll() {
$data = Db::dbFind(self::TABLE_NAME);
return $data;
}
Méthode find() : (retrouver en fonction d'un array $request
)
public static function find(array $request) {
$data = Db::dbFind(self::TABLE_NAME, $request);
return $data;
}
Méthode findOne() : (retrouver 1 élément en paramètre via son id)
public static function findOne(int $id) {
$request = [
['id', '=', $id]
];
$element = Db::dbFind(self::TABLE_NAME, $request);
if (count($element) > 0) $element = $element[0];
else return;
return $element;
}
-
Vos Model maintenant créés, appelez les données depuis les contrôleurs, notamment dans les méthodes correspondant à l'affichage de listes.
- Par exemple :
$students = Student::findAll();
- Par exemple :
-
Vous pouvez passer une variable
$students
à la vue de cette façon :view('students.index', compact('students'));
-
Mettez également à jour les méthodes correspondant à la création d'éléments en utilisant les méthodes des models, par exemple :
// Route POST student
public function save() {
$student = new Student;
$student->setName($_POST['name']);
$student->setBirthdate($_POST['birthdate']);
$student->save();
}
- Si vous êtes sûrs que vos formulaires fonctionnent parfaitement, vous pouvez rediriger vers une autre page à l'issue du traitement du formulaire :
public function save() {
$student = new Student;
$student->setName($_POST['name']);
$student->setBirthdate($_POST['birthdate']);
$student->save();
// On redirige vers la page d'un étudiant
Header('Location: ' . url('/students/' . $student->getId() ));
}
- Maintenant que les controllers passent des données aux vues, mettez à jour vos fichiers de vues afin d'afficher les données. Par exemple avec un foreach pour les listes.
- Créez une route
edit
qui prendra aussi en paramètres l'ID de l'élément à modifier - Créez la méthode dans le controlleur, qui va appeler la vue de la page d'update et qui va passer à la page l'élément à modifier
- Dans la vue, dans les champs
value
, mettez la valeur actuelle de l'élément à modifier. - Faites pointer le formulaire vers une méthode de contrôleur dédiée à l'update
Routes :
// Formulaire d'update
$router->get('student/{id}/edit', 'StudentsController@edit');
// Traitement de l'update
$router->post('student/{id}/edit', 'StudentsController@update');
Méthode affichant le formulaire:
class StudentController {
public function edit($id) {
$student = Student::findOne($id);
view('students.edit', compact('student'));
}
}
Vue :
<form action=">
<input type="text" name="firstname" value="<?= $student->getName() ?>"
</form>
Méthode traitant le formulaire:
class StudentController {
public function update($id) {
$student = Student::findOne($id);
$student->setFirstname($_POST['firstname']);
$student->update();
// On redirige vers la page de l'étudiant
Header('Location: ' . url('students/' . $student->getId() ));
}
}
Pour la suppression de l'enregistrement, il va falloir créer un lien ou un bouton qui redirige vers une méthode de contrôleur dédiée à cela :
Route vers laquelle pointe le bouton ou le lien de suppression :
$routes->get('students/{id}/delete', 'StudentsController@delete');
Controller :
class StudentsController {
public function delete($id) {
$student = Db::findOne($id);
$student->delete();
// On redirige vers la liste des étudiants
Header('Location: ' . url('students'));
}
}
Maintenant que les formulaires fonctionnent, tant pour créer que pour éditer, vous pouvez maintenant ajouter des validations dans les Model. Pour rappel, il faut ajouter les validations dans les setters
: c'est en effet leur rôle ! Par exemple :
public function setFirstname($name) {
// Liste des validations
if ( strlen($name) < 3) {
throw new Exception ('Le nom est trop court.');
}
// Si pas d'erreur :
$this->name = $name;
return $this;
}
L'idée est de pouvoir faire des choses comme ceci dans les vues :
Nom : <?= $student->firstname ?>
Liste des cours :
<?php foreach ($student->courses() as $course) : ?>
Nom du cours : <?= $course->getTitle() ?>
<?php endforeach ?>
Ou bien l'inverse :
Titre du cours : <?= $course->getTitle() ?>
Liste des étudiants :
<?php foreach ($course->students() as $student) : ?>
Nom de l'élève : <?= $student->getFirstname() ?>
<?php endforeach ?>
C'est à dire que, depuis une vue concernant par exemple un étudiant, on aurait accès à ses cours ! C'est tout l'intérêt des relations, 1-1, 1-N ou N-N.
Voici comment procéder : on va utiliser une requête INNER JOIN
écrite à la main dans le Model.
Fichier Student.php
:
class Student extends Db {
// ...
public function courses() {
// J'utilise getDb de la classe Db qui me donne un pointeur PDO.
$bdd = Db::getDb();
// Définition de la requête
$req = "SELECT *
FROM `student_course`
INNER JOIN course ON course.id = student_course.id_course
WHERE student_course.student_id = " . $this->getId();
$res = $bdd->query($req);
$courses = $res->fetchAll(PDO::FETCH_ASSOC);
return $courses;
}
}
Pensez bien à tester dans PHPMyAdmin vos requêtes !
- Ajoutez en Javascript une validation de suppression : lorsque l'on clique sur le lien ou le bouton de suppression, une
alert()
s'ouvre et nous demande de confirmer si on veut supprimer l'élément.
Vous rédigerez les requêtes SQL suivantes, qui peuvent être utiles dans les développements futurs de l'application :
- Afficher le nombre d'étudiants.
- Afficher le nombre de cours.
- Afficher le nombre d'inscriptions.
- Afficher les cours n’ayant pas d'étudiants
- Afficher les étudiants n’ayant pas de cours
- Afficher les cours suivis par l'étudiant « Jean Dupont » (adaptez à vos données)
- Afficher tous les étudiants (meme ceux qui n'ont pas de correspondance) ainsi que les cours
- Afficher les étudiants et tous les cours (meme ceux qui n'ont pas de correspondance)
- Afficher tous les étudiants et tous les cours, peut importe les correspondances.
Thank you so much your course help me a lot