Widgets WordPress : refactorisation, partie 3
En termes de mise à jour du WordPress Widget Boilerplate (qui est entièrement suivi dans la branche de développement ), nous avons parcouru un long chemin en termes de refactorisation de son organisation.
Jusqu’à présent, nous avons :
- examiné comment l’API WordPress Widget fournit un exemple de programmation orientée objet,
- comment nous pouvons utiliser cette API pour déterminer la programmation orientée objet dans d’autres domaines de WordPress,
- des outils installés pour nous aider à évaluer la qualité du code,
- trouvé des erreurs qui existent actuellement dans le code en termes de normes de programmation modernes,
- et a commencé à réorganiser la base de code afin qu’elle corresponde à des pratiques plus modernes.
Nous sommes maintenant prêts à commencer à refactoriser ce code d’une manière beaucoup plus orientée objet.
Donc, si vous n’avez pas encore rattrapé les messages précédents (aucun d’entre eux, vraiment), je vous recommande de le faire car cela va prendre un peu de temps pour mettre cela à jour. Il y a beaucoup de code pour écrire une explication.
Commençons.
Le passe-partout du widget WordPress: refactorisation, partie 3
On peut dire que le plus gros problème avec le Boilerplate est que tout est encapsulé dans une seule classe.
Bien sûr, il y a quelques bonnes choses comme garder nos vues séparées de la logique côté serveur, mais c’est à peu près tout.
D’autres problèmes qui existent juste en regardant le code incluent:
- ajouter des actions et des filtres dans le constructeur,
- avoir des méthodes faisant plus d’une chose,
- ne pas avoir de classes responsables de la mise en œuvre de choses comme l’enregistrement des dépendances,
- etc.
Dans cet article, nous allons commencer le processus de création d’abstractions que nous finirons par implémenter pour briser la nature de classe divine du Boilerplate tel qu’il est.
Cela va être divisé en plusieurs messages afin que je sois en mesure de fournir une explication solide de la raison pour laquelle nous faisons certaines choses que nous faisons ainsi que d’expliquer les exemples derrière cela.
Si je le fais autrement, la série laisse de côté trop d’informations précieuses applicables à d’autres pratiques de programmation orientée objet.
Qu’est-ce qu’un abonné ?
Le système de crochet WordPress – c’est-à-dire les actions et les filtres dont nous disposons – est basé sur un modèle de conception piloté par les événements. Cela signifie que chaque fois que quelque chose se produit, un événement, WordPress déclenchera tout autre code qui s’est abonné audit événement.
Ainsi, lorsque nous enregistrons une fonction avec un crochet, nous nous abonnons à l’ événement. À cette fin, je suis fan de la création d’abonnés pour n’importe quel crochet dont nous aurons besoin.
En plus de cela, les abonnés suivent généralement un format cohérent. Cela signifie qu’il est vraiment facile de créer une classe abstraite qui implémente certaines des fonctionnalités cohérentes, puis permet à la classe implémentant la classe abstraite de se concentrer uniquement sur la logique métier.
L’un des moyens les plus simples de le démontrer consiste à s’abonner aux fichiers CSS et aux fichiers JavaScript, car ce sont deux des éléments les plus courants que nous utilisons lors de la création de plugins.
Création d’une classe abstraite
Avant d’implémenter la classe abstraite, exposons exactement ce que nous allons faire pour créer ceci.
- Nous avons besoin d’une propriété qui représente l’événement auquel nous nous abonnons.
- Nous avons besoin d’une fonction qui se déclenche chaque fois que le hook est déclenché par WordPress. Une autre façon de penser à cela est que nous avons besoin d’une fonction à implémenter chaque fois qu’une action ou un filtre donné est déclenché par WordPress.
- Nous avons besoin de classes pour implémenter l’abstraction.
Commençons par définir les classes abstraites. Directement du manuel PHP, nous lisons :
Les classes définies comme abstraites ne peuvent pas être instanciées et toute classe contenant au moins une méthode abstraite doit également être abstraite. Les méthodes définies comme abstraites déclarent simplement la signature de la méthode – elles ne peuvent pas définir l’implémentation.
En bref, cela signifie que nous ne pouvons pas réellement créer une instance d’une classe abstraite. Nous ne pouvons instancier que des classes qui définissent l’implémentation.
Cela ne signifie pas, cependant, que la classe abstraite ne peut pas implémenter des choses concrètes (comme la définition d’un crochet). Mais cela signifie qu’il existe certaines méthodes qui n’ont pas d’implémentation.
Sinon, il ne nous reste plus qu’une classe de base.
Avoir du sens ? Nous allons jeter un coup d’oeil.
Création d’une classe abstraite
Pour cet article, nous allons créer une classe abstraite spécifiquement pour les fichiers CSS et JavaScript.
Rappelez-vous, comme il s’agit d’une classe abstraite, les abonnés concrets peuvent être appelés quelque chose qui identifie ce qu’ils font (c’est-à-dire qu’ils peuvent s’appeler tout ce qui représente leur objectif). Et nous allons arriver à cela.
Mais d’abord, la classe abstraite. Je vais partager le code puis expliquer exactement ce qui se passe avec:
<?php
/*
* This file is part of the WordPress Widget Boilerplate
*
* (c) Tom McFarlin <tom@tommcfarlin.com>
*
* This source file is subject to the GPL license that is bundled
* with this source code in the file LICENSE.
*/
namespace WordPressWidgetBoilerplateSubscriber;
/**
* An abstract implementation of a subscriber that requires a hook and the ability to
* start the class.
*/
abstract class AbstractSubscriber
{
/**
* @var string a reference to the hook to which the subscriber should be registered
*/
protected $hook;
/**
* @param string $hook the hook to which the subscriber is registered
*/
public function __construct(string $hook)
{
$this->hook = $hook;
}
/**
* @return string the hook to which the subscriber is registered
*/
public function getHook(): string
{
return $this->hook;
}
/**
* Implements the domain logic for the concrete class implementating this subcriber.
*/
abstract public function load();
}
- Notez que la propriété que nous avons créée est protected. C’est ainsi que les classes d’implémentation peuvent y accéder, mais que tout ce qui se trouve en dehors ne le peut pas.
- J’ai créé une fonction pour récupérer le crochet qui deviendra apparent plus tard. En règle générale, je déteste implémenter des fonctionnalités qui ne sont pas immédiatement évidentes, mais c’est quelque chose d’important compte tenu de la direction que nous prenons.
- Il existe une fonction abstraite appelée load. C’est là que toute classe qui implémente cette fonction hébergera sa logique métier, comme nous le verrons dans un instant.
- J’aime documenter le but des fonctions lorsque cela est nécessaire afin qu’elles soient au même endroit, puis laisser les classes d’implémentation fournir la documentation dont elles ont besoin pour leur implémentation. Vous le verrez momentanément aussi.
Maintenant que nous avons la classe abstraite en place, la dernière chose que nous devons faire est de nous assurer qu’elle est placée dans le bon répertoire et dans l’espace de noms. Si vous avez suivi le post précédent, vous avez probablement pu deviner où il va résider en fonction de l’espace de noms dans le code.
Et sinon, pas de soucis. Comprendre les espaces de noms et autres peut prendre un peu de temps. Donc, à travers ces messages d’adhésion et ces exemples, j’espère que cela deviendra clair avec le temps.
Création de classes concrètes
Implémentons maintenant cette classe particulière pour ajouter à la fois les feuilles de style et les sources JavaScript dont nous disposons. Ce que vous allez remarquer, cependant, c’est qu’ils sont très similaires.
La seule chose qui diffère est la mise en œuvre de la fonctionnalité de chargement, qui correspond exactement à la manière dont cela devrait fonctionner.
Feuilles de style
Étant donné la classe abstraite ci-dessus, nous devons maintenant créer une classe pour enregistrer les feuilles de style. Puisque nous avons deux feuilles de style, nous allons créer deux classes :
- la première classe sera chargée d’enregistrer la feuille de style pour le tableau de bord et spécifiquement pour la page du widget WordPress,
- la deuxième classe sera responsable de l’enregistrement du plugin pour le blog réel.
Appelons chacun de ces AdminStylesheetSubscriber et PublicStylesheetSubscriber respectivement.
Tout d’abord, l’abonné de la feuille de style admin :
<?php
/*
* This file is part of the WordPress Widget Boilerplate
*
* (c) Tom McFarlin <tom@tommcfarlin.com?
*
* This source file is subject to the GPL license that is bundled
* with this source code in the file LICENSE.
*/
namespace WordPressWidgetBoilerplateSubscriber;
/**
* The subscriber responsible for loading the stylesheet on the Widget administration page.
*/
class AdminStyleAssetSubscriber extends AbstractSubscriber
{
/**
* {@inheritdoc}
*/
public function __construct(string $hook)
{
parent::__construct($hook);
}
/**
* Adds the administrative stylesheet to the widget administration page.
*/
public function load()
{
if ('widgets' !== get_current_screen()->id) {
return;
}
wp_enqueue_style(
'wordpress-widget-boilerplate',
plugin_dir_url(dirname(__DIR__)).'assets/css/admin.css'
);
}
}
Notez que cela utilise la fonction get_current_screen() que j’ai utilisée dans les articles précédents pour m’assurer que nous ajoutons des dépendances uniquement lorsque cela est nécessaire.
Maintenant, l’abonné JavaScript public. Cela utilise la fonction is_admin() pour s’assurer que nous ne sommes pas dans la zone administrative de WordPress.
<?php
/*
* This file is part of the WordPress Widget Boilerplate
*
* (c) Tom McFarlin <tom@tommcfarlin.com>
*
* This source file is subject to the GPL license that is bundled
* with this source code in the file LICENSE.
*/
namespace WordPressWidgetBoilerplateSubscriber;
/**
* The subscriber responsible for loading the stylesheet on the blog.
*/
class PublicStyleAssetSubscriber extends AbstractSubscriber
{
/**
* {@inheritdoc}
*/
public function __construct(string $hook)
{
parent::__construct($hook);
}
/**
* Adds the stylesheet to the public-facing side of the site.
*/
public function load()
{
if (is_admin()) {
return;
}
wp_enqueue_style(
'wordpress-widget-boilerplate',
plugin_dir_url(dirname(__DIR__)).'assets/css/widget.css'
);
}
}
Évidemment, nous n’avons pas encore réellement instancié ces classes. Cela viendra plus tard dans la série.
Javascript
Les abonnés JavaScript ne sont pas très différents, comme vous l’avez peut-être deviné. Nous les séparerons en fonction du domaine de l’application dans lequel ils se concentrent et nous les nommerons de manière appropriée.
Tout d’abord, l’abonné JavaScript admin :
<?php
/*
* This file is part of the WordPress Widget Boilerplate
*
* (c) Tom McFarlin <tom@tommcfarlin.com>
*
* This source file is subject to the GPL license that is bundled
* with this source code in the file LICENSE.
*/
namespace WordPressWidgetBoilerplateSubscriber;
/**
* The subscriber responsible for loading the JavaScript on the Widget's adminsitration page.
*/
class AdminScriptAssetSubscriber extends AbstractSubscriber
{
/**
* {@inheritdoc}
*/
public function __construct(string $hook)
{
parent::__construct($hook);
}
/**
* Adds the administrative JavaScript to the widget administration page.
*/
public function load()
{
if ('widgets' !== get_current_screen()->id) {
return;
}
wp_enqueue_script(
'wordpress-widget-boilerplate',
plugin_dir_url(dirname(__DIR__)).'assets/js/admin.js'
);
}
}
Et l’abonné JavaScript public :
<?php
/*
* This file is part of the WordPress Widget Boilerplate
*
* (c) Tom McFarlin <tom@tommcfarlin.com>
*
* This source file is subject to the GPL license that is bundled
* with this source code in the file LICENSE.
*/
namespace WordPressWidgetBoilerplateSubscriber;
/**
* The subscriber responsible for loading the JavaScript on the blog.
*/
class PublicScriptAssetSubscriber extends AbstractSubscriber
{
/**
* {@inheritdoc}
*/
public function __construct(string $hook)
{
parent::__construct($hook);
}
/**
* Adds the JavaScript to the public-facing side of the site.
*/
public function load()
{
if (is_admin()) {
return;
}
wp_enqueue_script(
'wordpress-widget-boilerplate',
plugin_dir_url(dirname(__DIR__)).'assets/js/widget.js'
);
}
}
Encore une fois, ces classes ne peuvent pas encore être instanciées, mais nous nous concentrerons là-dessus dans un prochain article.
Abstractions et interfaces
N’oubliez pas que les abstractions et les interfaces sont différentes mais se confondent facilement. Les interfaces ne contiennent absolument aucune implémentation. Au lieu de cela, ils fournissent une garantie que toute classe qui implémente l’interface implémentera toutes les méthodes.
Les classes abstraites, d’autre part, peuvent avoir certaines fonctionnalités implémentées dans la classe abstraite tout en laissant le code spécifique au domaine – comme le chargement des feuilles de style et JavaScript – à la méthode appropriée.
Cela deviendra évident, si ce n’est déjà fait, au fur et à mesure que nous avancerons dans cette série. En attendant – et comme d’habitude – n’oubliez pas de consulter la branche develop pour voir où nous en sommes avec le code.


