You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
On créée les méthodes qui correspondent dans PagesController.php. On appelle une vue pour chaque méthode (en effet, ce sont 3 pages différentes donc 3 vues à créer).
Vous créérez une vue pour GET ajout-article avec un formulaire qui pointera vers POST ajout-article.
Vous ferez un dump de $_POST dans la méthode qui gère POST ajout-article.
AIDE : En configurant correctement la BASE_URL dans config/config.php, vous pourrez mettre l'action du formulaire ainsi :
On voit que l'URL est la même, mais la méthode HTTP change (get et post), et la méthode de la classe change aussi (GET est traité dans ajout(), POST est traité dans save()) :
Méthode HTTP
URL
Classe
Méthode de la classe
Notes
GET
ajout-article
ArticlesController
ajout()
Affiche le formulaire
POST
ajout-article
ArticlesController
save()
Acion du formulaire
class ArticlesController {
// Route GET ajout-articlepublicfunctionajout() {
view('articles.ajout');
}
// Route POST ajout-articlepublicfunctionsave() {
dump($_POST);
}
}
EXERCICE : Enregistrer les données d'un formulaire
Prérequis :
Soient les routes suivantes déjà créées :
- GET ajout-article => ArticlesController@add
- POST ajout-article => ArticlesController@save
Soit la table articles :
articles
---
id INT PK AI
title VARCHAR(255)
content TEXT
author_id INT
created_at DATETIME DEFAULT=CURRENT_TIMESTAMP
updated_at DATETIME ON_UPDATE=CURRENT_TIMESTAMP
N'oubliez pas de modifier config.php par rapport à votre configuration de base de données et de projet !
** NOTE IMPORTANTE** : Vous allez créer de nombreux éléments. À chaque étape, n'oubliez pas de tester ce que vous faites. En particulier, vous allez créer un Model dans un fichier Article.php qui vous permettra de faire certaines actions, mais c'est bien dans le controller ArticlesController.php que les tests (dump()) seront à faire ! En effet, le controller appelle le Model.
Exercice :
On va gérer l'enregistrement des données de formulaire dans la méthode ArticlesController@save.
Pour pouvoir créer un article, le controller va faire appel au Model. Le rôle du Model est d'être un modèle d'une table de la base de données, c'est à dire qu'il sera une représentation des données au sein de notre code PHP.
Par exemple, le type de données array est une représentation d'un tableau, avec ses règles (des clés, valeurs, des obligations de format...). Le model va nous permettre de créer un type de données correspondant à une table de la base de données.
Par exemple, une donnée de type Article doit être un élément contenant un titre, un contenu, un auteur existant, une date de création...
Le Model nous permet d'établir toutes ces règles.
Description du model Article
Dans le dossier src/model, créez le fichier Article.php.
Attention : les Model sont nommés en PascalCase et au singulier !
Fichier Article.php:
class Article {
}
Le Model est une classe. Une classe est une structure de code qui peut contenir des fonctions qui lui sont propres (les méthodes), et des variables qui lui sont propres, les attributs.
De la même manière qu'un élément de type array a des sortes d'attributs (une taille, des données, des clés, des valeurs...) et des sortes de méthodes (fonctions de tri, fonctions d'ajout, fonctions de suppression, fonctions de recherche...), un élément de type Article a en effet des fonctions et des attributs !
Les attributs peuvent être par exemple :
un id
un titre
un contenu
une date de création
...
Il a aussi des méthodes :
une fonction pour s'enregistrer
une fonction pour se supprimer
une fonction pour s'éditer
...
Un Model n'est donc ni plus ni moins qu'un type de données que nous créons.
Liste des attributs
Listons les attributs possibles de Article :
class Article {
/** * LISTE DES ATTRIBUTS */// Il s'agit du nom de la table dans la base de donnéesconstTABLE_NAME = "articles";
// il s'agit de la liste des champs de la table, mis en attributs :protected$id;
protected$title;
protected$content;
protected$authorId;
protected$createdAt;
protected$updatedAt;
}
Comme prévu, le type Article a comme attributs la liste des champs de la table. En effet, un Article est en fait un élément qui contient un id, un titre, un contenu.... etc.
Qu'est-ce que protected ? En programmation orientée objet, il s'agit de la portée de l'attribut. Cela veut dire que ces attributs peuvent être connus au sein de la classe Article ou de ses classes enfants - on en reparlera -, mais pas d'ailleurs.
Comment donner un titre à l'article ou y accéder alors ? Voir ci-dessous les setters et getters !
Liste des méthodes : setters
Parmi les méthodes du Model, on a des setters et des getters. En POO, on parle de mutateurs pour les setters, et d'accesseurs pour les getters.
Les setters servent à établir la valeur d'un attribut.
Les getters servent à accéder à la valeur d'un attribut.
Pourquoi passer par des méthodes plutôt que de leur donner une valeur immédiatement ?
On pourrait en effet accéder aux données du Model directement, par exemple :
$article = newArticle; // On créée un nouvel objet Article$article->title = "Nouvel article"; // On donne une valeur à $article->titleecho$article->title; // On accède à la valeur de $article->title
Mais ce n'est pas une bonne façon de faire : en faisant comme ça, on n'a aucune assurance que les données que l'on met dans le titre soient valides (par exemple pas le bon nombre de caractères, pas de majuscule au début...), et on n'a aucune assurance que la donnée du titre soit celle que l'on souhaite (pas exemple, dans le cas d'un système de traduction où le titre doit être traduit avant).
Les setters et getters sont donc des garde-fou qui nous permettent de nous assurer que les données soient bien telles que l'on souhaite qu'elles soient.
On a donc des setters pour chaque attribut, par exemple :
class Article {
// .../** * LISTE DES MÉTHODES : SETTERS */publicfunctionsetTitle($title) {
$this->title = $title;
}
}
Décrivons ce setter :
// La méthode prend en argument le titre que l'on souhaite donner : $title// Le setter ne fait qu'une chose pour l'instant : il donne la valeur $title à l'attribut de l'objet actuel.// Pour parler d'un attribut d'un objet, dans la classe elle même, on utilise $this->attribut. Ici, $this->title.publicfunctionsetTitle($title) {
$this->title = $title;
}
À l'utilisation, dans le controller par exemple :
$article = newArticle; // On crée une instance de Article, un nouvel objet Article$article->setTitle("Hello world");
Mais pour l'instant rien ne change, au final l'attribut prend la valeur que je lui donne sans rien contrôler ! En effet, mais c'est dans le setter que nous allons gérer les validations. Améliorons notre setter.
publicfunctionsetTitle($title) {
if (strlen($title) < 3) {
thrownewException ("Le titre est trop court.");
}
if (strlen($title) > 255) {
thrownewException ("Le titre est trop long.");
}
// Enfin, si tous les tests sont passés...$this->title = $title;
}
D'accord, mais je tappe où tout ce code ? Maintenant votre model commencé, vous pouvez l'appeler depuis le controller :
Dans le fichier ArticlesController.php :
// Méthode de la route POST ajouter-articlespublicfunctionsave() {
// On créée une instance de notre model Article$article = newArticle;
$article->setTitle( $_POST['title'] );
}
Attention : Comme on parle de $_POST['title'], on admet ici que dans votre formulaire, il y ait un champ title bien sûr ! Sinon, adaptez ce code au votre ;)
Testez ce code en mettant trop ou pas assez de caractères pour voir si cela lève une erreur.
Exercice : Faites des setters cohérents pour tous les attributs du Model ! Il y a parfois des setters qui ne contiendront pas de validations, comme pour setId($id) qui ne doit contenir que $this->id = $id : comme cette donnée ne peut pas venir de nous ou de l'utilisateur, aucune raison de faire des validations dessus.
Liste des méthodes : getters
Là où les setters nous permettent d'enregistrer une valeur pour notre objet, le getter permet de récupérer une valeur de notre objet.
À quoi servent les getters, plutôt que de récupérer la donnée directement ? Les getters nous permettent de modifier la donnée brute de la base de données afin de l'adapter à nos besoins. Par exemple :
Le getter nous permet donc d'adapter la donnée à nos besoins.
Comme les setters, nous allons faire des getters pour tous les attributs, bien que certains getters n'aient pas besoin de modifications ! Par exemple, l'id :
publicfunctiongetId() {
return$this->id;
}
Exercice : Faites des getters cohérents pour tous les attributs du Model.
Liste des méthodes : CRUD
à suivre
CORRECTION
La correction contient le model Article.php ainsi que les tests effectués dans la méthode save() de ArticlesController ( => donc pour tester, il faut aller dans le formulaire ajout-articles et le poster)
Fichier src/model/Article.php :
class Article {
/** * LISTE DES ATTRIBUTS */// Il s'agit du nom de la table dans la base de donnéesconstTABLE_NAME = "articles";
// il s'agit de la liste des champs de la table, mis en attributs :protected$id;
protected$title;
protected$content;
protected$authorId;
protected$createdAt;
protected$updatedAt;
publicfunctionsetId($id) {
$this->id = $id;
}
publicfunctionsetTitle($title) {
/** * Tests : */if (strlen($title) < 5) {
thrownewException('Le titre est trop court.');
}
if (strlen($title) > 255) {
thrownewException('Le titre est trop long.');
}
// Si les tests ne lancent pas d'erreurs, alors :$this->title = $title;
}
publicfunctionsetContent($content) {
/** * Tests : */if (strlen($content) < 5) {
thrownewException('Le contenu est trop court.');
}
// Si les tests ne lancent pas d'erreurs, alors :$this->content = $content;
}
publicfunctionsetAuthorId($authorId) {
/** * Tests : */if (!is_numeric($authorId)) {
thrownewException('\'author_id doit être un entier.');
}
// Si les tests ne lancent pas d'erreurs, alors :$this->authorId = $authorId;
}
publicfunctionsetCreatedAt($createdAt) {
/** * Tests : */// 1. On essaie de créer une date avec $createdAt$date = DateTime::createFromFormat('Y-m-d', $createdAt);
// Si on n'y arrive pas (donc la $createdAt est à un mauvais format), alors $date retourne false.if (!$date) {
thrownewException('La date n\'est pas au format YYYY-MM-DD.');
}
// Si les tests ne lancent pas d'erreurs, alors :$this->createdAt = $createdAt;
}
publicfunctionsetUpdatedAt($updatedAt) {
/** * Tests : */// 1. On essaie de créer une date avec $createdAt$date = DateTime::createFromFormat('Y-m-d', $updatedAt);
// Si on n'y arrive pas (donc la $createdAt est à un mauvais format), alors $date retourne false.if (!$date) {
thrownewException('La date n\'est pas au format YYYY-MM-DD.');
}
// Si les tests ne lancent pas d'erreurs, alors :$this->updatedAt = $updatedAt;
}
publicfunctiongetId() {
return$this->id;
}
/** * Getter pour retourner le titre avec la première lettre en majuscules systématiquement */publicfunctiongetTitle() {
$titleUppercase = ucfirst($this->title);
return$titleUppercase;
}
publicfunctiongetContent() {
return$this->content;
}
publicfunctiongetAuthorId() {
return$this->authorId;
}
publicfunctiongetCreatedAt() {
return$this->createdAt;
}
/** * Getter pour retourner la date de création en français */publicfunctiongetCreatedAtFr() {
$timestamp = strtotime($this->createdAt);
$dateFr = date('d/m/Y', $timestamp);
return$dateFr;
}
publicfunctiongetUpdatedAt() {
return$this->updatedAt;
}
/** * Getter pour retourner la date de mise à jour en français */publicfunctiongetUpdatedAtFr() {
$timestamp = strtotime($this->updatedAt);
$dateFr = date('d/m/Y', $timestamp);
return$dateFr;
}
}
Fichier ArticlesController.php :
<?phpclass ArticlesController {
publicfunctionadd() {
view('articles.add');
}
publicfunctionsave() {
$article = newArticle;
/********************* * TEST DES SETTERS * Décommentez les tests pour voir le résultat ! ********************/// Test du setter setTitle() avec un nom trop court//$article->setTitle('a');// Test du setter setTitle() avec un nom trop long//$article->setTitle( 'Lorem ipsum dolor sit amet consectetur, adipisicing elit. Molestiae ratione, fugiat laboriosam tenetur tempora dolorem. Eos nihil, ipsam sequi iusto accusantium nostrum culpa natus eligendi quae, provident et quis odio.'); // Test avec un nom trop court// Test du setter setTitle() avec un nom correct$article->setTitle('Nouvel article de blog !');
// Test du setter setContent() avec un contenu trop court//$article->setContent('a');// Test du setter setContent() avec un contenu correct$article->setContent( 'Lorem ipsum dolor sit amet consectetur, adipisicing elit. Molestiae ratione, fugiat laboriosam tenetur tempora dolorem. Eos nihil, ipsam sequi iusto accusantium nostrum culpa natus eligendi quae, provident et quis odio.');
// Test du setter setAuthorId() avec un caractère string//$article->setAuthorId('hello');// Test du setter setAuthorId() avec un entier$article->setAuthorId(23);
// Test du setter setCreatedAt() avec une date au mauvais format// $article->setCreatedAt('23 juillet 2019');// $article->setCreatedAt('23/07/2019');// $article->setCreatedAt('23-07-2019');// Test du settter setCreatedAt() avec une date au bon format$article->setCreatedAt('2019-07-23');
// Test du setter setUpdatedAt() avec une date au mauvais format// $article->setUpdatedAt('23 juillet 2019');// $article->setUpdatedAt('23/07/2019');// $article->setUpdatedAt('23-07-2019');// Test du settter setUpdatedAt() avec une date au bon format$article->setUpdatedAt('2019-07-23');
// On visualise à quoi ressemble $article :dump($article);
/******************** * TEST DES GETTERS ********************/echo"Test de getId() : ";
dump($article->getId());
echo"Test de getTitle() : ";
dump($article->getTitle());
echo"Test de getContent() : ";
dump($article->getContent());
echo"Test de getAuthorId() : ";
dump($article->getAuthorId());
echo"Test de getCreatedAt() : ";
dump($article->getCreatedAt());
echo"Test de getCreatedAtFr() : ";
dump($article->getCreatedAtFr());
echo"Test de getUpdatedAt() : ";
dump($article->getUpdatedAt());
echo"Test de getUpdatedAtFr() : ";
dump($article->getUpdatedAtFr());
}
publicfunctionshow() {
echo"Affichage de l'article";
}
}
EXERCICE : Ajouter des méthodes CRUD dans le Model
Ajout de save()
En plus des setters et getters qui nous permettent de nous assurer du format de donnée (à l'enregistrment et à la récupération), le Model possède des fonctions communes à tous les Models, qui nous permettent de bien enregistrer l'objet en base de données : save(), update(), delete().
Le problème de cette méthode, c'est que nous devons rapeller $bdd, saisir une requête SQL à chaque fois que nous avons un nouveau Model.
Nous avons déjà les informations variables pour le modèle: le nom de la table (la constante TABLE_NAME) et la liste des attributs.
On peut utiliser une librairie qui nous permettra de générer des requêtes automatiquement grâce à ces données : la classe Db qui se trouve dans le dossier config. Cette classe contient déjà toutes les requêtes déjà prêtes à l'usage pour communiquer avec la base de données.
Pour que notre Model puisse utiliser les méthodes de la classe Db, nous allons utiliser le concept d'héritage : notre classe Article va hériter de la classe Db, et pouvoir utiliser les méthodes qui y sont présentes.
Héritage
Pour faire un héritage, on utilise le mot clé extends :
Fichier Article.php :
class Article extends Db {
}
Utilisation des méthodes de Db dans Article
Vous pouvez jeter un oeil à Db.php afin de voir comment les méthodes sont écrites. Il n'est pas nécessaire de comprendre tout le fichier pour utiliser la classe !
Créons la méthode save() pour faire en sorte que l'article s'enregistre en base de données. Cette méthode va utiliser la méthode dbCreate() venant de la classe Db dont on a hérité.
Fichier Article.php :
class Article extends Db {
// .../** * LISTE DES MÉTHODES : CRUD */publicfunctionsave() {
/** 1. On prépare notre tableau de données à enregistrer : * en clé, les champs de la table en base de données, * en valeur, les valeurs de l'objet lui même (donc avec... les getters !) */$data = [
"title" => $this->getTitle(),
"content" => $this->getContent(),
"author_id" => $this->getAuthorId(),
];
/** * 2. On utilise la méthode dbCreate() de la classe parente Db. * Comme on en hérite, on peut l'utiliser comme une méthode de Article, * c'est à dire avec $this->... * La méthode dbCreate() retourne l'ID de l'élément créé. */$id = $this->dbCreate(self::TABLE_NAME, $data);
/** * 3. L'élément étant créé, on enregistre son ID dans l'objet : */$this->id = $id;
// Enfin, on retourne l'objet lui même (quitte à retourner quelque chose, on peut retourner l'objet lui même si on ne sait pas quoi retourner).return$this;
}
}
Si tout s'est bien passé : refaites l'exercice avec une table Commentaire par exemple !
CORRECTION :
class Article extends Db {
constTABLE_NAME = "articles";
protected$id;
protected$title;
protected$content;
protected$authorId;
protected$createdAt;
protected$updatedAt;
publicfunctionsetId($id) {
$this->id = $id;
}
publicfunctionsetTitle($title) {
if (strlen($title) < 5) {
thrownewException('Le titre est trop court.');
}
if (strlen($title) > 255) {
thrownewException('Le titre est trop long.');
}
$this->title = $title;
}
publicfunctionsetContent($content) {
if (strlen($content) < 5) {
thrownewException('Le contenu est trop court.');
}
$this->content = $content;
}
publicfunctionsetAuthorId($authorId) {
if (!is_numeric($authorId)) {
thrownewException('\'author_id doit être un entier.');
}
$this->authorId = $authorId;
}
publicfunctionsetCreatedAt($createdAt) {
$date = DateTime::createFromFormat('Y-m-d', $createdAt);
if (!$date) {
thrownewException('La date n\'est pas au format YYYY-MM-DD.');
}
$this->createdAt = $createdAt;
}
publicfunctionsetUpdatedAt($updatedAt) {
$date = DateTime::createFromFormat('Y-m-d', $updatedAt);
if (!$date) {
thrownewException('La date n\'est pas au format YYYY-MM-DD.');
}
$this->updatedAt = $updatedAt;
}
publicfunctiongetId() {
return$this->id;
}
publicfunctiongetTitle() {
$titleUppercase = ucfirst($this->title);
return$titleUppercase;
}
publicfunctiongetContent() {
return$this->content;
}
publicfunctiongetAuthorId() {
return$this->authorId;
}
publicfunctiongetCreatedAt() {
return$this->createdAt;
}
publicfunctiongetCreatedAtFr() {
$timestamp = strtotime($this->createdAt);
$dateFr = date('d/m/Y', $timestamp);
return$dateFr;
}
publicfunctiongetUpdatedAt() {
return$this->updatedAt;
}
publicfunctiongetUpdatedAtFr() {
$timestamp = strtotime($this->updatedAt);
$dateFr = date('d/m/Y', $timestamp);
return$dateFr;
}
/** * LISTE DES SETTERS : METHODES CRUD */publicfunctionsave() {
$data = [
"title" => $this->getTitle(),
"content" => $this->getContent(),
"author_id" => $this->getAuthorId(),
];
$id = $this->dbCreate(self::TABLE_NAME, $data);
$this->id = $id;
return$this;
}
}