Écrire une mise à niveau de plugin

De temps en temps, il arrive un moment où un plugin doit modifier le contenu ou la structure des données qu’il a stockées soit dans la base de données, soit dans le répertoire des données.

La raison de ceci pourrait être que la structure des données doit être convertie en une structure plus efficace ou plus flexible. Ou peut-être en raison d’un bogue les éléments de données ont été enregistrés d’une manière non valide, et ils doivent être convertis dans le format correct.

De telles migrations et conversions peuvent prendre beaucoup de temps s’il y a beaucoup de données à traiter. C’est pourquoi Elgg fournit la classe Elgg\Upgrade\AsynchronousUpgrade qui peut être utilisée pour implémenter des mises à niveau de longue durée.

Déclarer la mise à niveau d’un plugin

Le plugin peut communiquer la nécessité d’une mise à niveau avec la clé upgrades dans le fichier elgg-plugin.php. Chaque valeur du tableau doit être le nom complet d’une classe de mise à niveau qui étend la classe Elgg\Upgrade\AsynchronousUpgrade.

Exemple tiré du fichier mod/blog/elgg-plugin.php :

return [
        'upgrades' => [
                Blog\Upgrades\AccessLevelFix::class,
                Blog\Upgrades\DraftStatusUpgrade::class,
        ]
];
Les noms de classe dans l’exemple se réfèrent aux classes :
  • mod/blog/classes/Blog/Upgrades/AccessLevelFix

  • mod/blog/classes/Blog/Upgrades/DraftStatusUpgrade

Note

Les classes de mise à niveau du noyau Elgg peuvent être déclarées dans engine/lib/upgrades/async-upgrades.php.

La classe de mise à niveau

Une classe qui étend la classe Elgg\Upgrade\AsynchronousUpgrade a beaucoup de liberté sur la façon dont elle souhaite gérer le traitement réel des données. Elle doit cependant déclarer certaines variables constantes et également prendre soin de marquer si chaque élément traité a été mis à niveau avec succès ou non.

La structure de base de la classe est la suivante :

<?php

namespace Blog\Upgrades;

use Elgg\Upgrade\AsynchronousUpgrade;
use Elgg\Upgrade\Result;

/**
 * Fixes invalid blog access values
 */
class AccessLevelFix extends AsynchronousUpgrade {

        /**
         * Version of the upgrade
         *
         * @return int
         */
        public function getVersion() {
                return 2016120300;
        }

        /**
         * Should the run() method receive an offset representing all processed items?
         *
         * @return bool
         */
        public function needsIncrementOffset() {
                return true;
        }

        /**
         * Should this upgrade be skipped?
         *
         * @return bool
         */
        public function shouldBeSkipped() {
                return false;
        }

        /**
         * The total number of items to process in the upgrade
         *
         * @return int
         */
        public function countItems() {
                // return count of all blogs
        }

        /**
         * Runs upgrade on a single batch of items
         *
         * @param Result $result Result of the batch (this must be returned)
         * @param int    $offset Number to skip when processing
         *
         * @return Result Instance of \Elgg\Upgrade\Result
         */
        public function run(Result $result, $offset) {
                // fix 50 blogs skipping the first $offset
        }
}

Avertissement

Ne présumez pas quand votre classe sera instanciée ou quand/combien de fois ses méthodes publiques seront appelées.

Méthodes de la classe

getVersion()

Cela doit renvoyer un entier représentant la date à laquelle la mise à niveau a été ajoutée. La valeur se compose de huit chiffres au format yyyymmdnn où:

  • yyyy représente l’année

  • mm représente le mois (avec le zéro initial)

  • dd représente le jour (avec le zéro initial)

  • nn est un numéro d’incrément (à partir de 00) qui est utilisé au cas où deux mises à niveau distinctes auraient été ajoutées au cours de la même journée

shouldBeSkipped()

Cela devrait renvoyer false à moins que la mise à niveau ne soit pas nécessaire.

Avertissement

Si true est renvoyé, la mise à niveau ne peut pas être exécutée plus tard.

needsIncrementOffset()

Si true, votre méthode run() recevra comme $offset le nombre d’éléments déjà traités. Ceci est utile si vous ne modifiez que des données et que vous devez utiliser le $offset dans une fonction comme elgg_get_entities() pour savoir combien d’éléments ont déjà été traités.

Si false, votre méthode run() recevra comme $offset le nombre total d’échecs. false doit être utilisé si votre processus supprime ou déplace des données en dehors du processus. Par exemple, si vous supprimez 50 objets sur chaque run(), vous n’avez pas vraiment besoin de $offset.

countItems()

Récupère le nombre total d’éléments à traiter pendant la mise à niveau. Si ce nombre est inconnu, Batch::UNKNOWN_COUNT peut être retourné, mais run() doit alors marquer manuellement la mise à niveau terminée.

run()

Cette fonction doit effectuer une partie de la mise à niveau effective. Et en fonction de la durée nécessaire, elle peut être appelée plusieurs fois au cours d’une seule demande.

Elle reçoit deux arguments :

  • $result : Une instance de l’objet Elgg\Upgrade\Result

  • $offset : Le décalage à partir duquel la prochaine portion de mise à niveau doit commencer (ou le nombre total de défaillances)

Pour chaque élément que la méthode traite, elle doit appeler selon les cas :

  • $result->addSuccesses() : Si l’élément a bien été mis à niveau

  • $result->addFailures() : Si l’élément n’a pas pu être mis à niveau

Les deux méthodes ont par défaut un seul élément, mais vous pouvez éventuellement passer le nombre d’éléments.

En outre, elle peut définir autant de messages d’erreur que nécessaire dans le cas où quelque chose ne va pas :

  • $result->addError("Le message d'erreur est indiqué ici")

Si countItems() renvoie Batch::UNKNOWN_COUNT, alors à un moment donné run() doit appeler $result->markComplete() pour terminer la mise à niveau.

Dans la plupart des cas, votre méthode run() voudra passer le paramètre $offset à l’une des fonctions elgg_get_entities() :

/**
 * Process blog posts
 *
 * @param Result $result The batch result (will be modified and returned)
 * @param int    $offset Starting point of the batch
 * @return Result Instance of \Elgg\Upgrade\Result;
 */
public function run(Result $result, $offset) {
        $blogs = elgg_get_entitites([
                'type' => 'object'
                'subtype' => 'blog'
                'offset' => $offset,
        ]);

        foreach ($blogs as $blog) {
                if ($this->fixBlogPost($blog)) {
                        $result->addSuccesses();
                } else {
                        $result->addFailures();
                        $result->addError("Failed to fix the blog {$blog->guid}.");
                }
        }

        return $result;
}

getUpgrade()

Utilisez cette fonction pour obtenir l’entité ElggUpgrade associée à cette mise à niveau.

Interface d’administration

Chaque mise à niveau étendant la classe Elgg\Upgrade\AsynchronousUpgrade est répertoriée dans le panneau d’administration après avoir déclenché la mise à niveau du site depuis le tableau de bord d’administration.

Lors de l’exécution des mises à niveau, Elgg fournit :

  • Durée estimée de la mise à niveau

  • Nombre d’éléments traités

  • Nombre d’erreurs

  • Messages d’erreur possibles