Guide de Démarrage Rapide

Le meilleur moyen de tester et d’apprendre CakePHP est de s’assoir et de construire une application simple de gestion de Contenu (CMS).

Tutoriel d’un système de gestion de contenu

Ce tutoriel vous accompagnera dans la création d’une application de type CMS. Pour commencer, nous installerons CakePHP, créerons notre base de données et construirons un système simple de gestion d’articles.

Voici les pré-requis:

  1. Un serveur de base de données. Nous utiliserons MySQL dans ce tutoriel. Vous avez besoin de connaître assez de SQL pour créer une base de données et exécuter quelques requêtes SQL que nous fournirons dans ce tutoriel. CakePHP se chargera de construire les requêtes nécessaires pour votre application. Puisque nous allons utiliser MySQL, assurez-vous que pdo_mysql est bien activé dans PHP.

  2. Les connaissances de base en PHP.

Avant de commencer, assurez-vous que votre version de PHP est à jour:

php -v

Vous devez avoir au minimum PHP 8.1 installé (en CLI). Votre version serveur de PHP doit au moins être aussi 8.1 et, dans l’idéal, devrait également être la même que pour votre version en ligne de commande (CLI).

Récupérer CakePHP

La manière la plus simple d’installer CakePHP est d’utiliser Composer. Composer est une manière simple d’installer CakePHP via votre terminal. Premièrement, vous devez télécharger et installer Composer si vous ne l’avez pas déjà fait. Si vous avez cURL installé, exécutez la commande suivante:

curl -s https://getcomposer.org/installer | php

Ou vous pouvez télécharger composer.phar depuis le site de Composer.

Ensuite, tapez la commande suivante dans votre terminal pour installer le squelette d’application CakePHP dans le dossier cms du dossier courant:

php composer.phar create-project --prefer-dist cakephp/app:5.* cms

Si vous avez téléchargé et utilisé l’Installer de Composer pour Windows, tapez la commande suivante dans votre terminal depuis le dossier d’installation (par exemple C:\wamp\www\dev):

composer self-update && composer create-project --prefer-dist cakephp/app:4.* cms

Utiliser Composer a l’avantage d’exécuter automatiquement certaines tâches importantes d’installation, comme définir les bonnes permissions sur les dossiers et créer votre fichier config/app.php.

Il existe d’autres moyens d’installer CakePHP. Si vous ne pouvez pas (ou ne voulez pas) utiliser Composer, rendez-vous dans la section Installation.

Quelque soit la manière de télécharger et installer CakePHP, une fois que la mise en place est terminée, votre dossier d’installation devrait ressembler à ceci:

/cms
  /bin
  /config
  /logs
  /plugins
  /resources
  /src
  /templates
  /tests
  /tmp
  /vendor
  /webroot
  .editorconfig
  .gitignore
  .htaccess
  .travis.yml
  composer.json
  index.php
  phpunit.xml.dist
  README.md

C’est le bon moment pour en apprendre d’avantage sur le fonctionnement de la structure des dossiers de CakePHP : rendez-vous dans la section Structure du dossier de CakePHP pour en savoir plus.

Si vous vous perdez pendant ce tutoriel, vous pouvez voir le résultat final on GitHub.

Vérifier l’installation

Il est possible de vérifier que l’installation est terminée en vous rendant sur la page d’accueil. Avant de faire ça, vous allez devoir lancer le serveur de développement:

cd /path/to/our/app

bin/cake server

Note

Pour Windows, la commande doit être bin\cake server (notez le backslash).

Cela démarrera le serveur embarqué de PHP sur le port 8765. Ouvrez http://localhost:8765 dans votre navigateur pour voir la page d’accueil. Tous les éléments de la liste devront être validés sauf le point indiquant si CakePHP arrive à se connecter à la base de données. Si d’autres points ne sont pas validés, vous avez peut-être besoin d’installer des extensions PHP supplémentaires ou définir les bonnes permissions sur certains dossiers.

Ensuite, nous allons créer notre base de données et créer notre premier model.

Tutoriel CMS - Création de la base de données

Maintenant que CakePHP est installé, il est temps d’installer la base de données pour notre application CMS. Si vous ne l’avez pas encore fait, créez une base de données vide qui servira pour ce tutoriel, avec le nom de votre choix (par exemple cake_cms). Si vous utilisez MySQL/MariaDB, vous pouvez exécuter le SQL suivant pour créer le tables nécessaires:

USE cake_cms;

CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    email VARCHAR(255) NOT NULL,
    password VARCHAR(255) NOT NULL,
    created DATETIME,
    modified DATETIME
);

CREATE TABLE articles (
    id INT AUTO_INCREMENT PRIMARY KEY,
    user_id INT NOT NULL,
    title VARCHAR(255) NOT NULL,
    slug VARCHAR(191) NOT NULL,
    body TEXT,
    published BOOLEAN DEFAULT FALSE,
    created DATETIME,
    modified DATETIME,
    UNIQUE KEY (slug),
    FOREIGN KEY user_key (user_id) REFERENCES users(id)
) CHARSET=utf8mb4;

CREATE TABLE tags (
    id INT AUTO_INCREMENT PRIMARY KEY,
    title VARCHAR(191),
    created DATETIME,
    modified DATETIME,
    UNIQUE KEY (title)
) CHARSET=utf8mb4;

CREATE TABLE articles_tags (
    article_id INT NOT NULL,
    tag_id INT NOT NULL,
    PRIMARY KEY (article_id, tag_id),
    FOREIGN KEY tag_key(tag_id) REFERENCES tags(id),
    FOREIGN KEY article_key(article_id) REFERENCES articles(id)
);

INSERT INTO users (email, password, created, modified)
VALUES
('[email protected]', 'secret', NOW(), NOW());

INSERT INTO articles (user_id, title, slug, body, published, created, modified)
VALUES
(1, 'First Post', 'first-post', 'This is the first post.', 1, NOW(), NOW());

Si vous utilisez PostgreSQL, connectez-vous à la base de données cake_cms et exécutez le code SQL suivant à la place:

CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    email VARCHAR(255) NOT NULL,
    password VARCHAR(255) NOT NULL,
    created TIMESTAMP,
    modified TIMESTAMP
);

CREATE TABLE articles (
    id SERIAL PRIMARY KEY,
    user_id INT NOT NULL,
    title VARCHAR(255) NOT NULL,
    slug VARCHAR(191) NOT NULL,
    body TEXT,
    published BOOLEAN DEFAULT FALSE,
    created TIMESTAMP,
    modified TIMESTAMP,
    UNIQUE (slug),
    FOREIGN KEY (user_id) REFERENCES users(id)
);

CREATE TABLE tags (
    id SERIAL PRIMARY KEY,
    title VARCHAR(191),
    created TIMESTAMP,
    modified TIMESTAMP,
    UNIQUE (title)
);

CREATE TABLE articles_tags (
    article_id INT NOT NULL,
    tag_id INT NOT NULL,
    PRIMARY KEY (article_id, tag_id),
    FOREIGN KEY (tag_id) REFERENCES tags(id),
    FOREIGN KEY (article_id) REFERENCES articles(id)
);

INSERT INTO users (email, password, created, modified)
VALUES
('[email protected]', 'secret', NOW(), NOW());

INSERT INTO articles (user_id, title, slug, body, published, created, modified)
VALUES
(1, 'First Post', 'first-post', 'This is the first post.', TRUE, NOW(), NOW());

Vous avez peut-être remarqué que la table articles_tags utilise une clé primaire composée. CakePHP supporte les clés primaires composées presque partout, vous permettant d’avoir des shémas plus simples qui ne nécessitent pas de colonnes id supplémentaires.

Les noms de tables et de colonnes utilisés ne sont pas arbitraires. En utilisant les conventions de nommages de CakePHP, nous allons bénéficier des avantages de CakePHP de manière plus efficace et allons éviter d’avoir trop de configuration à effectuer. Bien que CakePHP soit assez flexible pour supporter presque n’importe quel schéma de base de données, adhérer aux conventions va vous faire gagner du temps.

Configuration de la base de données

Ensuite, disons à CakePHP où est notre base de données et comment nous y connecter. Remplacez les valeurs dans le tableau Datasources.default de votre fichier config/app.php avec celle de votre installation de base de données. Un exemple de configuration complétée ressemblera à ceci:

<?php
return [
    // D'autres configurations au dessus
    'Datasources' => [
        'default' => [
            'className' => Connection::class,
            // Remplacez Mysql par Postgres si vous utilisez PostgreSQL
            'driver' => Mysql::class,
            'persistent' => false,
            'host' => 'localhost',
            'username' => 'cakephp',
            'password' => 'AngelF00dC4k3~',
            'database' => 'cake_cms',
            // Commentez la ligne ci-dessous si vous utilisez PostgreSQL
            'encoding' => 'utf8mb4',
            'timezone' => 'UTC',
            'cacheMetadata' => true,
        ],
    ],
    // D'autres configurations en dessous
];

Une fois que vous avez sauvegardé votre fichier config/app.php, vous devriez voir que CakePHP est capable de se connecter à la base de données sur la page d’accueil de votre projet.

Note

Si vous avez config/app_local.php dans votre dossier d’application, vous devez plutôt configurer votre connexion à la base de données dans ce fichier.

Création du premier Model

Les models font partie du coeur des applications CakePHP. Ils nous permettent de lire et modifier les données, de construire des relations entre nos données, de valider les données et d’appliquer les règles spécifiques à notre application. Les models sont les fondations nécessaires pour construire nos actions de controllers et nos templates.

Les models de CakePHP sont composés d’objets Table et Entity. Les objets Table nous permettent d’accéder aux collections d’entities stockées dans une table spécifique. Ils sont stockés dans le dossier src/Model/Table. Le fichier que nous allons créer sera sauvegardé dans src/Model/Table/ArticlesTable.php. Le fichier devra contenir ceci:

<?php
// src/Model/Table/ArticlesTable.php
namespace App\Model\Table;

use Cake\ORM\Table;

class ArticlesTable extends Table
{
    public function initialize(array $config): void
    {
        $this->addBehavior('Timestamp');
    }
}

Nous y avons attaché le behavior Timestamp qui remplira automatiquement les colonnes created et modified de notre table. En nommant notre objet Table ArticlesTable, CakePHP va utiliser les conventions de nommages pour savoir que notre model va utiliser la table articles. Toujours en utilisant les conventions, il saura que la colonne id est notre clé primaire.

Note

CakePHP créera dynamiquement un objet model s’il n’en trouve pas un qui correspond dans le dossier src/Model/Table. Cela veut dire que si vous faites une erreur lors du nommage du fichier (par exemple articlestable.php ou ArticleTable.php), CakePHP ne reconnaitra pas votre configuration et utilisera ce model généré à la place.

Nous allons également créer une classe Entity pour nos Articles. Les Entities représentent un enregistrement spécifique en base et donnent accès aux données d’une ligne de notre base. Notre Entity sera sauvegardée dans src/Model/Entity/Article.php. Le fichier devra ressembler à ceci:

<?php
// src/Model/Entity/Article.php
namespace App\Model\Entity;

use Cake\ORM\Entity;

class Article extends Entity
{
    protected array $_accessible = [
        '*' => true,
        'id' => false,
        'slug' => false,
    ];
}

Notre entity est assez simple pour l’instant et nous y avons seulement défini la propriété _accessible qui permet de contrôler quelles propriétés peuvent être modifiées via Assignement de Masse.

Pour l’instant, nous ne pouvons pas faire grande chose avec notre model. Pour intéragir avec notre model, nous allons ensuite créer nos premiers Controller et Template.

Tutoriel CMS - Création du Controller Articles

Maintenant que notre model est créé, nous avons besoin d’un controller pour nos articles. Dans CakePHP, les controllers se chargent de gérer les requêtes HTTP et exécutent la logique métier des méthodes des models pour préparer une réponse. Nous placerons le code de ce controller dans un nouveau fichier ArticlesController.php, dans le dossier src/Controller. La base du controller ressemblera à ceci:

<?php
// src/Controller/ArticlesController.php

namespace App\Controller;

class ArticlesController extends AppController
{
}

Ajoutons maintenant une action à notre controller. Les actions sont les méthodes des controllers qui sont connectées aux routes. Par exemple, quand un utilisateur appelle la page www.example.com/articles/index (ce qui est la même chose qu’appeler www.example.com/articles), CakePHP appelera la méthode index de votre controller ArticlesController. Cette méthode devra à son tour faire appel à la couche Model et préparer une réponse en faisant le rendu d’un Template via la couche de View. Le code de notre action index sera le suivant:

<?php
// src/Controller/ArticlesController.php

namespace App\Controller;

class ArticlesController extends AppController
{
    public function index()
    {
        $this->loadComponent('Paginator');
        $articles = $this->Paginator->paginate($this->Articles->find());
        $this->set(compact('articles'));
    }
}

Maintenant que nous avons une méthode index() dans notre ArticlesController, les utilisateurs peuvent y accéder via www.example.com/articles/index. De la même manière, si nous définissions une méthode foobar(), les utilisateurs pourraient y accéder via www.example.com/articles/foobar. Vous pourriez être tenté de nommer vos controllers et vos actions afin d’obtenir des URL spécifiques. Cependant, ceci est déconseillé. Vous devriez plutôt suivre les Conventions de CakePHP et créer des noms d’actions lisibles ayant un sens pour votre application. Vous pouvez ensuite utiliser le Routing pour obtenir les URLs que vous souhaitez et les connecter aux actions que vous avez créées.

Notre action est très simple. Elle récupère un jeu d’articles paginés dans la base de données en utilisant l’objet Model Articles qui est chargé automatiquement via les conventions de nommage. Elle utilise ensuite la méthode set() pour passer les articles récupérés au Template (que nous créerons par la suite). CakePHP va automatiquement rendre le Template une fois que notre action de Controller sera entièrement exécutée.

Création du Template de liste des Articles

Maintenant que notre controller récupère les données depuis le model et qu’il prépare le contexte pour la view, créons le template pour notre action index.

Les templates de view de CakePHP sont des morceaux de PHP qui sont insérés dans le layout de votre application. Bien que nous créerons du HTML ici, les Views peuvent générer du JSON, du CSV ou même des fichiers binaires comme des PDFs.

Un layout est le code de présentation qui englobe la view d’une action. Les fichiers de layout contiennent les éléments communs au site comme les headers, les footers et les éléments de navigation. Votre application peut très bien avoir plusieurs layouts et vous pouvez passer de l’un à l’autre. Mais pour le moment, utilisons seulement le layout par défaut.

Les fichiers de template de CakePHP sont stockés dans templates et dans un dossier au nom du controller auquel ils sont attachés. Nous devons donc créer un dossier nommé “Articles” dans notre cas. Ajoutez le code suivant dans ce fichier:

<!-- Fichier : templates/Articles/index.php -->

<h1>Articles</h1>
<table>
    <tr>
        <th>Titre</th>
        <th>Créé le</th>
    </tr>

    <!-- C'est ici que nous bouclons sur notre objet Query $articles pour afficher les informations de chaque article -->

    <?php foreach ($articles as $article): ?>
    <tr>
        <td>
            <?= $this->Html->link($article->title, ['action' => 'view', $article->slug]) ?>
        </td>
        <td>
            <?= $article->created->format(DATE_RFC850) ?>
        </td>
    </tr>
    <?php endforeach; ?>
</table>

Dans la précédente section, nous avons assigné la variable “articles” à la view en utilisant la méthode set(). Les variables passées à la view sont disponibles dans les templates de view comme des variables locales, comme nous l’avons fait ci-dessus.

Vous avez peut-être remarqué que nous utilisons un objet appelé $this->Html. C’est une instance du HtmlHelper. CakePHP inclut plusieurs helpers de view qui peuvent créer des liens, des formulaires et des éléments de paginations. Vous pouvez en apprendre plus à propos des Helpers (Assistants) dans le chapitre de la documentation qui leur est consacré, mais le plus important ici est la méthode link(), qui générera un lien HTML avec le texte fourni (le premier paramètre) et l’URL (le second paramètre).

Quand vous spécifiez des URLs dans CakePHP, il est recommandé d’utiliser des tableaux ou des routes nommées. Ces syntaxes vous permettent de bénéficier du reverse routing fourni par CakePHP.

A partir de maintenant, si vous accédez à http://localhost:8765/articles/index, vous devriez voir votre view qui liste les articles avec leur titre et leur lien.

Création de l’action View

Si vous cliquez sur le lien d’un article dans la page qui liste nos articles, vous tombez sur une page d’erreur vous indiquant que l’action n’a pas été implémentée. Vous pouvez corriger cette erreur en créant l’action manquante correspondante:

// Ajouter au fichier existant src/Controller/ArticlesController.php

public function view($slug = null)
{
    $article = $this->Articles->findBySlug($slug)->firstOrFail();
    $this->set(compact('article'));
}

Bien que cette action soit simple, nous avons utilisé quelques-unes des fonctionnalités de CakePHP. Nous commençons par utiliser la méthode findBySlug() qui est un finder dynamique. Cette méthode nous permet de créer une requête basique qui permet de récupérer des articles par un « slug » donné. Nous utilisons ensuite la méthode firstOrFail() qui nous permet de récupérer le premier enregistrement ou lancera une NotFoundException si aucun article correspondant n’est trouvé.

Notre action attend un paramètre $slug, mais d’où vient-il ? Si un utilisateur requête /articles/view/first-post, alors la valeur “first-post” sera passée à $slug par la couche de routing et de dispatching de CakePHP. Si nous rechargeons notre navigateur, nous aurons une nouvelle erreur, nous indiquant qu’il manque un template de View; corrigeons cela.

Création du Template View

Créons le template de view pour notre action « view » dans templates/Articles/view.php.

<!-- Fichier : templates/Articles/view.php -->

<h1><?= h($article->title) ?></h1>
<p><?= h($article->body) ?></p>
<p><small>Créé le : <?= $article->created->format(DATE_RFC850) ?></small></p>
<p><?= $this->Html->link('Modifier', ['action' => 'edit', $article->slug]) ?></p>

Vous pouvez vérifier que tout fonctionne en essayant de cliquer sur un lien de /articles/index ou en vous rendant manuellement sur une URL de la forme /articles/view/first-post.

Ajouter des articles

Maintenant que les views de lecture ont été créées, il est temps de rendre possible la création d’articles. Commencez par créer une action add() dans le ArticlesController. Notre controller doit maintenant ressembler à ceci:

// src/Controller/ArticlesController.php

namespace App\Controller;

use App\Controller\AppController;

class ArticlesController extends AppController
{

    public function initialize(): void
    {
        parent::initialize();

        $this->loadComponent('Paginator');
        $this->loadComponent('Flash'); // Inclusion du FlashComponent
    }

    public function index()
    {
        $articles = $this->Paginator->paginate($this->Articles->find());
        $this->set(compact('articles'));
    }

    public function view($slug)
    {
        $article = $this->Articles->findBySlug($slug)->firstOrFail();
        $this->set(compact('article'));
    }

    public function add()
    {
        $article = $this->Articles->newEmptyEntity();
        if ($this->request->is('post')) {
            $article = $this->Articles->patchEntity($article, $this->request->getData());

            // L'écriture de 'user_id' en dur est temporaire et
            // sera supprimée quand nous aurons mis en place l'authentification.
            $article->user_id = 1;

            if ($this->Articles->save($article)) {
                $this->Flash->success(__('Votre article a été sauvegardé.'));

                return $this->redirect(['action' => 'index']);
            }
            $this->Flash->error(__('Impossible d\'ajouter votre article.'));
        }
        $this->set('article', $article);
    }
}

Note

Vous devez inclure le Flash dans tous les controllers où vous avez besoin de l’utiliser. Il est souvent conseillé de le charger directement dans le AppController.

Voici ce que l’action add() fait:

  • Si la méthode HTTP de la requête est un POST, cela tentera de sauvegarder les données en utilisant le model Articles.

  • Si pour une quelconque raison la sauvegarde ne se fait pas, cela rendra juste la view. Cela nous donne ainsi une chance de montrer les erreurs de validation ou d’autres messages à l’utilisateur.

Toutes les requêtes de CakePHP incluent un objet request qui est accessible via $this->request. L’objet request contient des informations à propos de la requête qui vient d’être reçue. Nous utilisons la méthode Cake\Http\ServerRequest::is() pour vérifier que la requête possède bien le verbe HTTP POST.

Les données passées en POST sont disponibles dans $this->request->getData(). Vous pouvez utiliser les fonctions pr() ou debug() pour afficher les données si vous voulez voir à quoi elles ressemblent. Pour sauvegarder les données, nous devons tout d’abord « marshaller » les données du POST en une Entity Article. L’Entity sera ensuite persistée en utilisant la classe ArticlesTable que nous avons créée plus tôt.

Après la sauvegarde de notre article, nous utilisons la méthode success() du FlashComponent pour définir le message en Session. La méthode success est fournie via les méthodes magiques de PHP. Les messages Flash seront affichés sur la page suivante après redirection. Dans notre layout, nous avons <?= $this->Flash->render() ?> qui affichera un message Flash et le supprimera du stockage dans la session. Enfin, après la sauvegarde, nous utilisons Cake\Controller\Controller::redirect pour renvoyer l’utilisateur à la liste des articles. Le paramètre ['action' => 'index'] correspond à l’URL /articles, c’est-à-dire l’action index du ArticlesController. Vous pouvez vous référer à la méthode Cake\Routing\Router::url() dans la documentation API pour voir les formats dans lesquels vous pouvez spécifier une URL.

Création du Template Add

Voici le code de notre template de la view « add »:

<!-- File: templates/Articles/add.php -->

<h1>Ajouter un article</h1>
<?php
    echo $this->Form->create($article);
    // Hard code the user for now.
    echo $this->Form->control('user_id', ['type' => 'hidden', 'value' => 1]);
    echo $this->Form->control('title');
    echo $this->Form->control('body', ['rows' => '3']);
    echo $this->Form->button(__('Sauvegarder l\'article'));
    echo $this->Form->end();
?>

Nous utilisons le FormHelper pour générer l’ouverture du formulaire HTML. Voici le HTML que $this->Form->create() génère:

<form method="post" action="/articles/add">

Puisque nous appelons create() sans passer d’option URL, le FormHelper va partir du principe que le formulaire doit être soumis sur l’action courante.

La méthode $this->Form->control() est utilisée pour créer un élément de formulaire du même nom. Le premier paramètre indique à CakePHP à quel champ il correspond et le second paramètre vous permet de définir un très grand nombre d’options - dans notre cas, le nombre de lignes (rows) pour le textarea. Il y a un peu d’instrospection et de conventions utilisées ici. La méthode control() affichera des éléments de formulaire différents en fonction du champ du model spécifié et utilisera une inflection automatique pour définir le label associé. Vous pouvez personnaliser le label, les inputs ou tout autre aspect du formulaire en utilisant les options. La méthode $this->Form->end() ferme le formulaire.

Retournons à notre template templates/Articles/index.php pour ajouter un lien « Ajouter un article ». Avant le <table>, ajoutons la ligne suivante:

<?= $this->Html->link('Ajouter un article', ['action' => 'add']) ?>

Ajout de la génération de slug

Si nous sauvons un article tout de suite, la sauvegarde échouerait car nous ne créons pas l’attribut « slug » et la colonne correspondante est définie comme NOT NULL. Un slug est généralement une version « URL compatible » du titre d’un article. Nous pouvons utiliser le callback beforeSave() de l’ORM pour créer notre slug:

<?php
// dans src/Model/Table/ArticlesTable.php
namespace App\Model\Table;

use Cake\ORM\Table;
// la classe Text
use Cake\Utility\Text;
// la classe EventInterface
use Cake\Event\EventInterface;

// Ajouter la méthode suivante

public function beforeSave($event, $entity, $options)
{
    if ($entity->isNew() && !$entity->slug) {
        $sluggedTitle = Text::slug($entity->title);
        // On ne garde que le nombre de caractère correspondant à la longueur
        // maximum définie dans notre schéma
        $entity->slug = substr($sluggedTitle, 0, 191);
    }
}

Ce code est simple et ne prend pas en compte les potentiels doublons de slug. Mais nous nous occuperons de ceci plus tard.

Ajout de l’action Edit

Notre application peut maintenant sauvegarder des articles, mais nous ne pouvons pas modifier les articles existants. Rectifions cela maintenant. Ajoutez l’action suivante dans votre ArticlesController:

// dans src/Controller/ArticlesController.php

// Ajouter la méthode suivante.

public function edit($slug)
{
    $article = $this->Articles
        ->findBySlug($slug)
        ->firstOrFail();

    if ($this->request->is(['post', 'put'])) {
        $this->Articles->patchEntity($article, $this->request->getData());
        if ($this->Articles->save($article)) {
            $this->Flash->success(__('Votre article a été mis à jour.'));

            return $this->redirect(['action' => 'index']);
        }
        $this->Flash->error(__('Impossible de mettre à jour l\'article.'));
    }

    $this->set('article', $article);
}

Cette action va d’abord s’assurer que l’utilisateur essaie d’accéder à un enregistrement existant. Si vous n’avez pas passé de paramètre $slug ou que l’article n’existe pas, une NotFoundException sera lancée et le ErrorHandler de CakePHP rendra la page d’erreur appropriée.

Ensuite l’action va vérifier si la requête est une requête POST ou PUT. Si c’est le cas, nous utiliserons alors les données du POST/PUT pour mettre à jour l’entity de l’article en utilisant la méthode patchEntity(). Enfin, nous appelons la méthode save(), nous définissons un message Flash approprié et soit nous redirigeons, soit nous affichons les erreurs de validation en fonction du résultat de l’opération de sauvegarde.

Création du Template Edit

Le template edit devra ressembler à ceci:

<!-- Fichier : templates/Articles/edit.php -->

<h1>Modifier un article</h1>
<?php
    echo $this->Form->create($article);
    echo $this->Form->control('user_id', ['type' => 'hidden']);
    echo $this->Form->control('title');
    echo $this->Form->control('body', ['rows' => '3']);
    echo $this->Form->button(__('Sauvegarder l\'article'));
    echo $this->Form->end();
?>

Ce template affiche le formulaire de modification (avec les valeurs déjà remplies), ainsi que les messages d’erreurs de validation nécessaires.

Vous pouvez maintenant mettre à jour notre view index avec les liens pour modifier les articles:

<!-- Fichier : templates/Articles/index.php (liens de modification ajoutés) -->

<h1>Articles</h1>
<p><?= $this->Html->link("Ajouter un article", ['action' => 'add']) ?></p>
<table>
    <tr>
        <th>Titre</th>
        <th>Créé le</th>
        <th>Action</th>
    </tr>

    <!-- C'est ici que nous bouclons sur notre objet Query $articles pour afficher les informations de chaque article -->

<?php foreach ($articles as $article): ?>
    <tr>
        <td>
            <?= $this->Html->link($article->title, ['action' => 'view', $article->slug]) ?>
        </td>
        <td>
            <?= $article->created->format(DATE_RFC850) ?>
        </td>
        <td>
            <?= $this->Html->link('Modifier', ['action' => 'edit', $article->slug]) ?>
        </td>
    </tr>
<?php endforeach; ?>

</table>

Mise à jour des règles de validation pour les Articles

Jusqu’à maintenant, nos Articles n’avaient aucune validation de données. Occupons-nous de ça en utilisant un validator:

// src/Model/Table/ArticlesTable.php

// Ajouter ce "use" juste sous la déclaration du namespace pour importer
// la classe Validator
use Cake\Validation\Validator;

// Ajouter la méthode suivante.
public function validationDefault(Validator $validator): Validator
{
    $validator
        ->notEmptyString('title')
        ->minLength('title', 10)
        ->maxLength('title', 255)

        ->notEmptyString('body')
        ->minLength('body', 10);

    return $validator;
}

La méthode validationDefault() indique à CakePHP comment valider les données quand la méthode save() est appelée. Ici, il est spécifié que les champs title et body ne peuvent pas être vides et qu’ils ont aussi des contraintes sur la longueur.

Le moteur de validation de CakePHP est à la fois puissant et flexible. Il vous fournit un jeu de règles sur des validations communes comme les adresses emails, les adresses IP, etc. mais aussi la flexibilité d’ajouter vos propres règles de validation. Pour plus d’informations, rendez-vous dans la section Validation de la documentation.

Maintenant que nos règles de validation sont en place, utilisons l’application et essayons d’ajouter un article avec un title ou un body vide pour voir ce qu’il se passe. Puisque nous avons utiliser la méthode Cake\View\Helper\FormHelper::control() du FormHelper pour créer les éléments de formulaire, nos messages d’erreurs de validation seront affichés automatiquement.

Ajout de l’Action de Suppression

Donnons maintenant la possibilité à nos utilisateurs de supprimer des articles. Commencez par créer une action delete() dans ArticlesController:

// src/Controller/ArticlesController.php

// Ajouter la méthode suivante.

public function delete($slug)
{
    $this->request->allowMethod(['post', 'delete']);

    $article = $this->Articles->findBySlug($slug)->firstOrFail();
    if ($this->Articles->delete($article)) {
        $this->Flash->success(__('L\'article {0} a été supprimé.', $article->title));

        return $this->redirect(['action' => 'index']);
    }
}

Ce code va supprimer l’article ayant le slug $slug et utilisera la méthode $this->Flash->success() pour afficher un message de confirmation à l’utilisateur après l’avoir redirigé sur /articles. Si l’utilisateur essaie d’aller supprimer un article avec une requête GET, la méthode allowMethod() lancera une exception. Les exceptions non capturées sont récupérées par le gestionnaire d’exception de CakePHP qui affichera une belle page d’erreur. Il existe plusieurs Exceptions intégrées qui peuvent être utilisées pour remonter les différentes erreurs HTTP que votre application aurait besoin de générer.

Avertissement

Permettre de supprimer des données via des requêtes GET est très dangereux, car il est possible que des crawlers suppriment accidentellement du contenu. C’est pourquoi nous utilisons la méthode allowMethod() dans notre controller.

Puisque nous exécutons seulement de la logique et redirigeons directement sur une autre action, cette action n’a pas de template. Vous devez ensuite mettre à jour votre template index pour ajouter les liens qui permettront de supprimer les articles:

<!-- Fichier : templates/Articles/index.php (ajout des liens de suppression) -->

<h1>Articles</h1>
<p><?= $this->Html->link("Add Article", ['action' => 'add']) ?></p>
<table>
    <tr>
        <th>Titre</th>
        <th>Créé le</th>
        <th>Action</th>
    </tr>

    <!-- C'est ici que nous bouclons sur notre objet Query $articles pour afficher les informations de chaque article -->

<?php foreach ($articles as $article): ?>
    <tr>
        <td>
            <?= $this->Html->link($article->title, ['action' => 'view', $article->slug]) ?>
        </td>
        <td>
            <?= $article->created->format(DATE_RFC850) ?>
        </td>
        <td>
            <?= $this->Html->link('Modifier', ['action' => 'edit', $article->slug]) ?>
            <?= $this->Form->postLink(
                'Supprimer',
                ['action' => 'delete', $article->slug],
                ['confirm' => 'Êtes-vous sûr ?'])
            ?>
        </td>
    </tr>
<?php endforeach; ?>

</table>

Utiliser postLink() va créer un lien qui utilisera du JavaScript pour faire une requête POST et supprimer notre article.

Note

Ce code de view utilise également le FormHelper pour afficher à l’utilisateur une boîte de dialogue de confirmation en JavaScript avant la suppression effective de l’article.

Maintenant que nous avons un minimum de gestion sur nos articles, il est temps de créer des actions basiques pour nos tables Tags et Users.