É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.

Migrations and convertions like this may take a long time if there is a lot of data to be processed. This is why Elgg provides the Elgg\Upgrade\AsynchronousUpgrade class that can be used for implementing long-running upgrades.

Déclarer la mise à niveau d’un plugin

Plugin can communicate the need for an upgrade under the upgrades key in elgg-plugin.php file. Each value of the array must be the fully qualified name of an upgrade class that extends the Elgg\Upgrade\AsynchronousUpgrade class.

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

A class extending the Elgg\Upgrade\AsynchronousUpgrade class has a lot of freedom on how it wants to handle the actual processing of the data. It must however declare some constant variables and also take care of marking whether each processed item was upgraded successfully or not.

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()

Use this function to get the related ElggUpgrade entity that is related to this upgrade.

Interface d’administration

Each upgrade extending the Elgg\Upgrade\AsynchronousUpgrade class gets listed in the admin panel after triggering the site upgrade from the Administration dashboard.

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