// BLOG

Kirby CMS - Les hooks

Déclencher l'execution d'une portion de code lorsqu'un évènement précis se produit dans votre site web est un mode de fonctionnement courrant dans les CMS. Ces évènements sont appelés "Hooks" et il en existe de nombreux dans Kirby CMS.

Rédigé le 30.03.2022
Par Gilles Vauvarin

Hooks Kirby CMS

Qu'est ce que les hooks ?

Un hook est un point d'ancrage dans Kirby sur lequel nous pouvons greffer l'execution d'une portion de code. Ce point d'ancrage correspond à un évènement précis qui se produit soit suite à une action de l'utilisateur (dans le front ou le back aka Panel) soit suite à une action codée dans nos scripts.

Exemple de hooks dans Kirby :

- Création d'une page
- Suppression d'une page
- Changement de statut d'un utilisateur
- Modifier le nom d'un fichier ...

Il existe plus d'une trentaine de hooks dont la plupart sont déclinés en deux versions hook:before et hook:after Par exemple, on peut déclencher l'execution d'un code avant et/ou après la création d'une page.

Ces hooks sont très utiles pour automatiser certaines actions. Dans un CMS "Flatfile" dénué de base de données relationelles, ces hooks peuvent servirent par exemple à simuler l'aspect relationnel en déclenchant des suppressions entre des éléments de l'application qui sont étroitement liés.

Comment utiliser les hooks ?

Pour indiquer à Kirby que l'on souhaite executer une portion de code sur un évènement précis (un hook), il faut enregistrer ce hook soit dans le fichier de configuration /site/config/config.php soit dans une section de votre plugin. Je n'aborderais pas dans cet épisode tout ce qui touche aux plugins car ça mériterait plusieurs articles mais si cela vous intéresse, je vous renvoie vers la documentation officielle.

Voici à quoi ressemble l'enregistrement de hooks dans le fichier de configuration de Kirby :

<?php

// /site/config/config.php

return [
    'hooks' => [

        'page.create:after' => function ($page) {
            // code que je souhaite executer sur ce hook
        },

        'page.delete:before' => function ($page, $force) {
            // code que je souhaite executer sur ce hook
        }

    ]
];

Dans le tableau renvoyé par le fichier de configuration, il faut indiquer un indice litéral "hooks" dont la valeur est un tableau contenant la liste des hooks sur lesquels nous souhaitons intervenir. Le nom des hooks est assez explicite et se décline sur le pattern type.action:state Pensez à respecter l'orthographe imposé par Kirby. La liste des paramètres qui seront accessibles dans notre code depuis un hook sont indiqués dans la documentation, l'ordre d'appel n'est pas stricte mais vous devez garder le nom qui leur est donné dans la documentation.

Enregistrement d'un hook dans le fichier de configuration :

Enregistrement de hooks

Les wilcards hook

Si vous souhaitez appliquer une portion de code sur toutes les actions (suppression, création, modification ...) du même type de hook vous pouvez utiliser un wilcard hook.

Par exemple voici comment enregistrer un wilcard hook pour appliquer mon code sur les pages lors de tous les types de modification :

<?php

// /site/config/config.php

return [
    'hooks' => [

        'page.*:after' => function ($page) {
            // code que je souhaite executer sur ce wilcard shook
        },
    ]
];

Ce wilcard peut être utilisé sur le type, l'action ou l'état du hook.

Par exemple, toutes ces combinaisons sont possibles:
type.*:state,

type.action:*,

type.*.*:,

*.action:state,

*.action:*,

*:state,

*

Utilisation de l'objet $event

Un objet $event peut être passé en paramètre de votre fonction anonyme pour ensuite accéder à des informations utiles comme par exemple le type, l'action, le state ... de votre hook. Voici l'ensemble des informations auxquelles vous pourez accéder :

<?php

// /site/config/config.php

return [
    'hooks' => [
        'page.create:before' => function ($event) {
            $name   = $event->name();   // 'page.create:before'
            $type   = $event->type();   // 'page'
            $action = $event->action(); // 'create'
            $state  = $event->state();  // 'before'
            $page   = $event->page();   // $page
            $input  = $event->input();  // $input
        }
    ]
];

Ces informations peuvent être utiles si vous souhaitez par exemple que votre hook réagissent uniquement à certaines actions.

Partons d'un exemple concret pour que cela soit plus claire. Supposons que je souhaite que mon code s'éxecute uniquement lorsqu'une page est soit créée soit supprimée. Voici comment je peux utiliser les informations fournies par l'objet $event pour y arriver :

<?php

// /site/config/config.php

return [
    'hooks' => [
        'page.*:before' => function ($event, $page) {
            // Vérifier si l'action doit être prise en compte
            if ( in_array( $event->action(), ['create', 'delete']) !== true ) {
                // Non, je retourne le résultat tout de suite et rien ne se passe !
                return;
            }

            // Oui, j'execute mon code
            ...
            return $result;
        }
    ]
];

Accéder aux objets $kirby, $site et $user

Pour accéder aux objets Kirby dans le code appelé dans un hook, vous devez utiliser le mot clé this de la manière suivante :

$kirby    = $this;
$site     = $this->site();
$user     = $this->user();

Ce qui donne dans un hook :

<?php

// /site/config/config.php

return [
    'hooks' => [

        'page.create:after' => function ($page) {
            // code que je souhaite executer sur ce hook
            $site = $this->site();
            $projects = $site->children()->filterBy('template','project');
            ...
        }

    ]
];

Gestion des erreurs

Vous pouvez renvoyer des erreurs à l'utilisateur sous forme d'une modale. Si l'erreur est affichée, l'action ciblée dans le hook ne sera pas executée. On peut par exemple se servir de ce mécanisme pour valider des champs de formulaire du panel.

Voici comment afficher une modale d'erreur pour valider un champ requis :

<?php

// /site/config/config.php

return [
    'hooks' => [
        'page.create:after' => function ($page) {
            // Si le champ creationDate est vide, j'affiche un message ... 
            if ( empty( $page->creationDate()->value() ) ) :
               throw new Exception('La date de création est un champ requis.');
            endif;
        }

    ]
];

La vidéo YouTube