Partie 5 – WordPress et la programmation orientée objet : un exemple WordPress – Implémentation : le menu Administration

Publié: 2022-02-04

Dans notre article précédent sur la programmation orientée objet, nous avons discuté de la conception que nous avons finalement proposée pour notre plugin orienté objet.

Maintenant, nous allons entrer dans la partie la plus excitante où nous allons approfondir la façon dont nous l'avons implémenté !

Nous vous guiderons à travers certaines parties de l'implémentation, une étape à la fois, en parlant des bases mêmes de la programmation orientée objet, de la syntaxe PHP, de certains concepts de base, et nous jetterons même un coup d'œil sur les principes SOLID.

À la fin de cet article, vous aurez, espérons-le, une meilleure compréhension de la POO et serez enthousiaste à l'idée d'écrire vos propres plugins orientés objet !

Commencer

Nous supposons que vous êtes familier avec le développement de plugins WordPress en général, nous allons donc nous concentrer sur les aspects orientés objet de notre plugin. Si vous débutez dans le développement de plugins ou avez besoin d'un rappel, vous devriez d'abord apprendre à créer votre premier plugin WordPress.

Commençons comme nous le faisons toujours, en créant un nouveau fichier prsdm-limit-login-attempts.php, sous notre répertoire de plugins (c'est-à-dire /wp-content/plugins/prsdm-limit-login-attempts).

Le fichier principal du plugin inclura l'en-tête du plugin que vous connaissez déjà :

 /** * Plugin Name: PRSDM Limit Login Attempts * Plugin URI: https://pressidium.com * Description: Limit rate of login attempts, including by way of cookies, for each IP. * Author: Pressidium * Author URI: https://pressidium.com * Text Domain: prsdm-limit-login-attempts * License: GPL-2.0+ * Version: 1.0.0 */

Et une simple instruction if pour empêcher un accès direct à celle-ci.

 if ( ! defined( 'ABSPATH' ) ) { exit; }

C'est tout ce dont nous avons besoin pour l'instant. Nous reviendrons sur ce fichier plus tard !

Construire un menu d'administration

Lorsque vous développez un plugin, vous devez souvent fournir à vos utilisateurs un moyen de le configurer. C'est là qu'intervient une page de paramètres. Pour en créer une, nous allons ajouter un menu d'administration qui utilise l'API WordPress Settings.

Commençons donc à réfléchir à l'apparence de notre API orientée objet .

Idéalement, nous aimerions instancier notre Pressidium_LLA_Settings_Page et en finir avec. Pour créer une instance d'une classe, le new mot-clé doit être utilisé.

 new Pressidium_LLA_Settings_Page();

Maintenant, réfléchissons à l'apparence de notre classe Pressidium_LLA_Settings_Page .

Nous allons commencer par créer une nouvelle classe, en utilisant le mot-clé class :

 class Pressidium_LLA_Settings_Page {}

Notre nom de classe doit être précédé d'un identifiant unique, Pressidium_LLA_ pour éviter toute collision de noms avec d'autres plugins WordPress. Les préfixes empêchent d'autres plugins d'écraser et/ou d'appeler accidentellement nos classes. Tant que nos noms de classe sont uniques ou que nous utilisons des espaces de noms, il n'y aura pas de conflits avec d'autres plugins.

Le Constructeur

Maintenant, nous allons nous connecter à admin_menu et admin_init. Pour garder les choses simples, nous appellerons simplement add_action() dans notre constructeur (alerte spoiler : nous changerons cela plus tard).

 class Pressidium_LLA_Settings_Page { /** * Settings_Page constructor. */ public function __construct() { add_action( 'admin_menu', array( $this, 'add_page' ) ); add_action( 'admin_init', array( $this, 'register_sections' ) ); } }

Les classes qui ont un constructeur appellent cette méthode lorsqu'un objet est instancié. Ainsi, la méthode __construct() est idéale pour toute initialisation que nous pourrions vouloir effectuer.

Examinons de plus près nos add_action() . Si vous avez développé des plugins WordPress dans le passé, vous vous attendiez peut-être à quelque chose comme ceci :

 add_action( 'admin_menu', 'my_plugin_prefix_add_page' );

Mais à la place, nous avons :

 add_action( 'admin_menu', array( $this, 'add_page' ) );

Vous pourriez être confus quant à l'utilisation d'un tableau ici. Chaque fois que nous voulons passer une méthode d'un objet instancié en tant que callback/callable, nous pouvons utiliser un tableau contenant un objet à l'index 0 et un nom de méthode à l'index 1.

Qu'est-ce que c'est ?

C'est une pseudo-variable qui est disponible lorsqu'une méthode est appelée depuis un contexte d'objet. $this est la valeur de l'objet appelant. Dans ce cas, $this est une instance de Pressidium_LLA_Settings_Page .

De plus, toutes nos "fonctions" sont maintenant des méthodes, enveloppées dans une classe, il n'est donc pas nécessaire de préfixer nos noms de méthodes.

Espaces de noms

Les espaces de noms en PHP nous permettent de regrouper des classes, des interfaces, des fonctions, etc., en évitant les collisions de noms entre notre code et les classes/fonctions PHP internes ou tierces.

Allons-y et utilisons-les, afin que nous n'ayons pas à préfixer l'une de nos classes à l'avenir.

Nous allons déclarer un espace de noms en utilisant le mot-clé namespace .

 namespace Pressidium;

Les espaces de noms peuvent être définis avec des sous-niveaux.

 namespace Pressidium\Limit_Login_Attempts;

Puisque nous construisons une page de paramètres, nous allons déclarer un sous-espace de noms "pages" pour regrouper tout ce qui concerne les pages d'administration.

 namespace Pressidium\Limit_Login_Attempts\Pages;

On peut enfin se débarrasser du préfixe Pressidium_LLA_ !

 namespace Pressidium\Limit_Login_Attempts\Pages; class Settings_Page { // ...

Un autre plugin WordPress contenant une classe Settings_Page n'est plus un problème, puisque sa classe et notre classe ne vivront pas dans le même espace de noms.

Lors de l'instanciation de notre Settings_Page dans le même espace de noms, nous pouvons l'omettre :

 namespace Pressidium\Limit_Login_Attempts\Pages; $settings_page = new Settings_Page();

Lors de l'instanciation de notre Settings_Page en dehors de son espace de noms, nous devons le spécifier comme ceci :

 namespace Another\Namespace; $settings_page = new \Pressidium\Limit_Login_Attempts\Pages\Settings_Page();

Alternativement, nous pourrions importer notre classe avec l'opérateur use :

 use Pressidium\Limit_Login_Attempts\Pages\Settings_Page; $settings_page = new Settings_Page();

Ajout de rappels de hook

Maintenant, déclarons ces add_page() et register_sections() .

 class Settings_Page { /** * Settings_Page constructor. */ public function __construct() { add_action( 'admin_menu', array( $this, 'add_page' ) ); add_action( 'admin_init', array( $this, 'register_sections' ) ); } /** * Add this page as a top-level menu page. */ public function add_page() { // TODO: Implement this method. } /** * Register sections. */ public function register_sections() { // TODO: Implement this method. } }

Notre méthode add_page() appellera simplement la fonction WordPress add_menu_page().

 public function add_page() { add_menu_page( __( 'Limit Login Attempts Settings', 'prsdm-limit-login-attempts' ), __( 'Limit Login Attempts', 'prsdm-limit-login-attempts' ), 'manage_options', 'prsdm_limit_login_attempts_settings', array( $this, 'render' ), 'dashicons-shield-alt', null ); }

Cela semble être une manière compliquée de développer des plugins WordPress. Il s'agit simplement d'appeler des fonctions WordPress, avec des étapes supplémentaires.

Eh bien, ce n'est pas exactement "réutilisable", nous aurions encore à écrire tout ce code supplémentaire pour chaque menu/page d'administration que nous voulons ajouter.

Refactorisation

Allons-y et refactorisons un peu notre code pour profiter de la programmation orientée objet et rendre notre code réutilisable . Nous commencerons par remplacer nos valeurs codées en dur dans add_page() par quelques méthodes, comme ceci :

 public function add_page() { add_menu_page( $this->get_page_title(), // page_title $this->get_menu_title(), // menu_title $this->get_capability(), // capability $this->get_slug(), // menu_slug array( $this, 'render' ), // callback function $this->get_icon_url(), // icon_url $this->get_position() // position ); }

Nous définirons ces méthodes comme protected , afin qu'elles ne soient accessibles qu'au sein de la classe elle-même et par ses classes enfant/parent.

 protected function get_page_title() { /* ... */ } protected function get_menu_title() { /* ... */ } protected function get_capability() { /* ... */ } protected function get_slug() { /* ... */ } protected function get_icon_url() { /* ... */ } protected function get_position() { /* ... */ }

Super! Nous pouvons maintenant utiliser cette classe comme une classe générique réutilisable à partir de laquelle étendre.

Refonte

Nous vous avions dit que cela finirait probablement par arriver. Nous voilà en train de repenser le design de notre classe tout en la construisant.

Comme cela va être notre classe de base , nous allons la renommer en un nom plus générique, comme Admin_Page . Jusqu'ici, ça ressemble à ça :

 class Admin_Page { /** * Admin_Page constructor. */ public function __construct() { add_action( 'admin_menu', array( $this, 'add_page' ) ); add_action( 'admin_init', array( $this, 'register_sections' ) ); } /** * Add this page as a top-level menu page. */ public function add_page() { add_menu_page( $this->get_page_title(), // page_title $this->get_menu_title(), // menu_title $this->get_capability(), // capability $this->get_slug(), // menu_slug array( $this, 'render' ), // callback function $this->get_icon_url(), // icon_url $this->get_position() // position ); } /** * Register sections. */ public function register_sections() { // TODO: Implement this method. } protected function get_page_title() { /* ... */ } protected function get_menu_title() { /* ... */ } protected function get_capability() { /* ... */ } protected function get_slug() { /* ... */ } protected function get_icon_url() { /* ... */ } protected function get_position() { /* ... */ } }

Nous pouvons maintenant créer une Settings_Page distincte qui étend cette classe de base Admin_Page .

 class Settings_Page extends Admin_Page { // ... }

C'est un excellent exemple d' héritage , l'un des concepts fondamentaux de la programmation orientée objet. Lors de l'extension d'une classe, la classe enfant — Settings_Page , dans ce cas — hérite de toutes les méthodes, propriétés et constantes publiques et protégées de la classe parent.

Nous pouvons nous en servir et définir des valeurs par défaut. Par exemple, nous allons définir une icône générique pour toutes les pages de menu, en définissant notre méthode get_icon_url() comme ceci :

 class Admin_Page { // ... /** * Return the menu icon to be used for this menu. * * @link https://developer.wordpress.org/resource/dashicons/ * * @return string */ protected function get_icon_url() { return 'dashicons-admin-generic'; } }

À moins qu'une classe ne remplace ces méthodes, elles conserveront leur fonctionnalité d'origine. Ainsi, par défaut, toutes les classes enfants vont utiliser cette icône générique.

Cependant, si nous voulons définir une autre icône pour une page de menu spécifique, nous pouvons simplement remplacer la méthode get_icon_url() dans notre classe enfant, comme ceci :

 class Settings_Page extends Admin_Page { protected function get_icon_url() { return 'dashicons-shield-alt'; } }

Cependant, certaines valeurs doivent être différentes pour chaque classe enfant. Par exemple, le slug de menu — le quatrième argument de add_menu_page() — devrait être unique pour chaque page de menu.

Si nous devions définir cette méthode dans notre classe de base Admin_Page , nous aurions besoin d'un moyen de nous assurer que chaque classe enfant remplace cette méthode. Eh bien, nous pouvons faire quelque chose d'encore mieux. Nous pouvons déclarer la signature de la méthode et ignorer complètement son implémentation.

Entrez dans les méthodes abstraites !

Classes abstraites et méthodes

Les méthodes définies comme abstraites déclarent simplement la signature de la méthode et elles ne peuvent pas définir son implémentation.

 /** * Return page slug. * * @return string */ abstract protected function get_slug();

Toute classe contenant au moins une méthode abstraite doit également être abstraite. Cela signifie que notre classe Admin_Page doit également être définie comme abstraite.

 abstract class Admin_Page { // ...

Il est également important de souligner ici que les classes définies comme abstraites ne peuvent pas être instanciées. Ainsi, nous ne pouvons plus instancier directement Admin_Page .

Voici également une visualisation de la classe :

Lors de l'héritage d'une classe abstraite, la classe enfant doit définir toutes les méthodes marquées abstraites dans la déclaration de sa classe parent. Cela signifie que notre Settings_Page doit implémenter la méthode get_slug() .

 class Settings_Page extends Admin_Page { // ... protected function get_slug() { return 'prsdm_limit_login_attempts_settings'; } // ... }

De la même manière, nous devrions implémenter le reste des méthodes protégées add_page() besoin.

Avant de continuer sur la façon dont nous allons enregistrer les sections et les champs de la page d'administration et rendre leur contenu, parlons un peu des paramètres de WordPress.

L'API des paramètres

Nous supposerons que vous connaissez déjà l'API Settings. Mais, juste au cas où, voici l'essentiel:

  • settings_fields() — Affiche les champs nonce, action et option_page pour une page de paramètres. Fondamentalement, les champs de formulaire cachés.
  • do_settings_sections() — Imprime toutes les sections de paramètres (et leurs champs) ajoutées à une page de paramètres particulière.
  • add_settings_section() — Ajoute une nouvelle section à une page de paramètres.
  • add_settings_field() — Ajoute un nouveau champ à une section d'une page de paramètres.
  • register_setting() — Enregistre un paramètre et ses données.

Si vous n'êtes pas déjà familiarisé avec cela, vous pouvez mettre en pause la lecture de cet article et consulter notre article connexe sur la façon de créer la page des paramètres pour un plugin personnalisé.

Maintenant que nous sommes sur la même page, revenons à notre méthode register_sections() . Encore une fois, nous devons prendre du recul et réfléchir à notre API.

Puisque nous avons défini la méthode add_page() dans la classe Admin_Page , nous y définirons également la méthode render() . Nous transmettrons les valeurs de retour de nos autres méthodes en tant qu'arguments aux fonctions WordPress.

 abstract class Admin_Page { // ... /** * Render this admin page. */ public function render() { ?> <div class="wrap"> <form action="options.php" method="post"> <h1><?php echo esc_html( $this->get_page_title() ); ?></h1> <?php settings_fields( $this->get_slug() ); do_settings_sections( $this->get_slug() ); submit_button( __( 'Change Options', 'prsdm-limit-login-attempts' ) ); ?> </form> </div> <?php } }

De cette façon, nous n'aurons plus jamais à nous soucier directement de ces fonctions WordPress. En effet, toute page d'administration que nous pourrions ajouter à l'avenir sera construite via une classe enfant, tout comme Settings_Page , et son rendu sera effectué via la méthode héritée render() de la classe parent Admin_Page .

Conclusion

Super! Nous avons créé les classes qui sont chargées d'enregistrer un menu d'administration et d'ajouter une page de paramètres.

Dans le prochain article de la série, nous continuerons à construire notre page de paramètres et enregistrerons ses sections, champs et éléments.

Cliquez ici pour lire la partie 6 de notre série sur la programmation orientée objet

Voir également

  • WordPress et la programmation orientée objet - Un aperçu
  • Partie 2 – WordPress et la programmation orientée objet : un exemple concret
  • Partie 3 – WordPress et la programmation orientée objet : Α Exemple WordPress – Définition de la portée
  • Partie 4 – WordPress et la programmation orientée objet : un exemple WordPress – Conception