15. Ajouter un article

Il est temps maintenant d'ajouter des articles depuis notre application.
Nous allons commencer par créer la route, la méthode de contrôleur qui doit gérer cette route, et la vue associée.

C'est parti 😉

Commençons par créer la nouvelle route dans notre fichier Router.php  :

<?php

namespace App\config;
use App\src\controller\BackController;
use App\src\controller\ErrorController;
use App\src\controller\FrontController;
use Exception;

class Router
{
    private $frontController;
    private $backController;
    private $errorController;

    public function __construct()
    {
        $this->frontController = new FrontController();
        $this->backController = new BackController();
        $this->errorController = new ErrorController();
    }

    public function run()
    {
        try{
            if(isset($_GET['route']))
            {
                if($_GET['route'] === 'article'){
                    $this->frontController->article($_GET['articleId']);
                }
                elseif($_GET['route'] === 'addArticle'){
                    $this->backController->addArticle($_POST);
                }
                else{
                    $this->errorController->errorNotFound();
                }
            }
            else{
                $this->frontController->home();
            }
        }
        catch (Exception $e)
        {
            $this->errorController->errorServer();
        }
    }
}

Jusqu'ici, rien de nouveau, la route est passée à une méthode appelée  addArticle  du  backController  qui prend en paramètre la variable superglobale  $_POST  . Quelques compléments d'infos au cas où : 
-  addArticle  est appelée dans  BackController  , pour qu'à l'avenir, lorsque l'on mettra en place notre espace d'administration, seul l'utilisateur connecté avec les droits d'administrateur pourra ajouter un nouvel article.
- on passe à  addArticle  la variable  $_POST , pour récupérer les données du formulaire que l'on va utiliser par la suite pour insérer nos données en base.

En parlant de addArticle, que contient cette méthode ? 

Voici notre  BackController  actualisé :

<?php

namespace App\src\controller;

use App\src\DAO\ArticleDAO;
use App\src\model\View;

class BackController
{
    private $view;

    public function __construct()
    {
        $this->view = new View();
    }

    public function addArticle($post)
    {
        if(isset($post['submit'])) {
            $articleDAO = new ArticleDAO();
            $articleDAO->addArticle($post);
            header('Location: ../public/index.php');
        }
        return $this->view->render('add_article', [
            'post' => $post
        ]);
    }
}

Ici, on a simplement ajouté une condition : 
- soit le formulaire n'a pas été soumis, auquel cas on ne rentre pas dans la condition.
- soit le formulaire a été soumis, auquel cas, on fait appel à la méthode  addArticle  de  ArticleDAO

Je vous mets le fichier  ArticleDAO.php  actualisé : 

<?php

namespace App\src\DAO;

use App\src\model\Article;

class ArticleDAO extends DAO
{
    private function buildObject($row)
    {
        $article = new Article();
        $article->setId($row['id']);
        $article->setTitle($row['title']);
        $article->setContent($row['content']);
        $article->setAuthor($row['author']);
        $article->setCreatedAt($row['createdAt']);
        return $article;
    }

    public function getArticles()
    {
        $sql = 'SELECT id, title, content, author, createdAt FROM article ORDER BY id DESC';
        $result = $this->createQuery($sql);
        $articles = [];
        foreach ($result as $row){
            $articleId = $row['id'];
            $articles[$articleId] = $this->buildObject($row);
        }
        $result->closeCursor();
        return $articles;
    }

    public function getArticle($articleId)
    {
        $sql = 'SELECT id, title, content, author, createdAt FROM article WHERE id = ?';
        $result = $this->createQuery($sql, [$articleId]);
        $article = $result->fetch();
        $result->closeCursor();
        return $this->buildObject($article);
    }

    public function addArticle($article)
    {
        //Permet de récupérer les variables $title, $content et $author
        extract($article);
        $sql = 'INSERT INTO article (title, content, author, createdAt) VALUES (?, ?, ?, NOW())';
        $this->createQuery($sql, [$title, $content, $author]);
    }
}

Rien de nouveau ici, si ce n'est qu'on effectue une requête INSERT.

Ajoutons aussi un petit lien vers notre page d'ajout d'article depuis notre page d'accueil pour avoir une navigation 😉

Voici le fichier  home.php  actualisé : 

<?php $this->title = "Accueil"; ?>

<h1>Mon blog</h1>
<p>En construction</p>
<a href="../public/index.php?route=addArticle">Nouvel article</a>
<?php
foreach ($articles as $article)
{
    ?>
    <div>
        <h2><a href="../public/index.php?route=article&articleId=<?= htmlspecialchars($article->getId());?>"><?= htmlspecialchars($article->getTitle());?></a></h2>
        <p><?= htmlspecialchars($article->getContent());?></p>
        <p><?= htmlspecialchars($article->getAuthor());?></p>
        <p>Créé le : <?= htmlspecialchars($article->getCreatedAt());?></p>
    </div>
    <br>
    <?php
}
?>

Voici le fichier  add_article.php  : 

<?php $this->title = "Nouvel article"; ?>
<h1>Mon blog</h1>
<p>En construction</p>
<div>
    <form method="post" action="../public/index.php?route=addArticle">
        <label for="title">Titre</label><br>
        <input type="text" id="title" name="title"><br>
        <label for="content">Contenu</label><br>
        <textarea id="content" name="content"></textarea><br>
        <label for="author">Auteur</label><br>
        <input type="text" id="author" name="author"><br>
        <input type="submit" value="Envoyer" id="submit" name="submit">
    </form>
    <a href="../public/index.php">Retour à l'accueil</a>
</div>

Si vous essayez d'ajouter un article, cela doit fonctionner maintenant 😉

Génial, l'ajout d'article fonctionne, on passe à la suite ? 😅

Pas si vite... On a un problème là... 😱

Le problème que l'on a... c'est que quelque soit ce que l'on saisi dans les différents champs, ça nous ajoute un nouvel article.

Mais non c'est très bien comme ça, pourquoi tu vois le mal partout ? 😡

Je ne sais pas pour vous mais personnellement, ça m'ennuie de pouvoir ajouter des articles vides, ou avec certains champs non renseignés.

Ne vous inquiétez pas, pour parer à ça, nous allons parler... des contraintes de validation.

 

Les contraintes de validation

Si vous essayez maintenant de publier un nouvel article, en ne saisissant aucun champ, vous allez vous rendre compte que cela fonctionne.

Oui, mais c'est bien ce qu'on veut, non ? 😒

En réalité, pas exactement...
On veut bien qu'un article soit ajouté dans notre application, mais en respectant des règles que l'on a définies.

 C'est-à-dire ? 😅

Dans notre application, voici les règles que l'on pourrait mettre en place concernant nos articles :

 - notre champ  titre  de notre formulaire ne doit pas être vide, et doit contenir un minimum de 2 caractères ainsi qu'un maximum de 255 caractères 
- notre champ  contenu  de notre formulaire ne doit pas être vide, et contenir un minimum de 10 caractères
- notre champ  auteur  aura les mêmes caractéristiques que notre champ  titre

Attention, c'est bien à vous de définir vos propres règles dans votre application selon ce que vous souhaitez avoir ! Celles-ci varient d'une application à l'autre, alors soyez vigilants 😉

Les contraintes de validation vont donc nous permettre de nous assurer que l'on respecte bien les règles que l'on vient  de mettre en place.

Et donc d'éviter d'insérer un article avec les champs vierges ? 😄

Vous avez bien compris 😉

Pour le moment, nous n'allons mettre en place... aucune contrainte.

Tu te fiches de nous, tu nous en parle, mais on ne fait rien ? 😡

Je vous en parle pour vous sensibiliser à cette notion qui est extrêmement importante, c'est la seule protection que vous avez pour garantir une base de données cohérente selon les données saisies par l'utilisateur.

Je vous en dis quand même un peu plus concernant les contraintes, voici quelques exemples de contraintes existantes (celles qui sont utilisées dans le framework Symfony) :
NotBlank(pour éviter de recevoir des données vides)
Email(pour vérifier qu'un email soit valide)
Range (pour comparer des valeurs numériques)
... 

La liste complète se trouve ici : Liste des contraintes

La liste est... longue 😱😱😱

Bien évidemment, vous pouvez mettre en place vos propres contraintes.

Nous mettrons en place des contraintes un peu plus tard dans le cours 😆

Que fait-on maintenant ?

On va mettre en place un nouveau Controller 😁

 

Bilan

Dans ce chapitre, nous avons mis en place la fonctionnalité d'ajout d'article et nous avons abordé la notion de contraintes de validation.

Vous pouvez retrouver le code associé à ce chapitre sur GitHub.