Parte 5 – WordPress e programmazione orientata agli oggetti: un esempio di WordPress – Implementazione: il menu di amministrazione

Pubblicato: 2022-02-04

Nel nostro precedente articolo sulla programmazione orientata agli oggetti abbiamo discusso il design che alla fine abbiamo ideato per il nostro plug-in orientato agli oggetti.

Ora entreremo nella parte più eccitante in cui approfondiremo il modo in cui l'abbiamo implementato!

Ti guideremo attraverso alcune parti dell'implementazione, un passo alla volta, parlando delle basi della programmazione orientata agli oggetti, della sintassi PHP, di alcuni concetti fondamentali e daremo anche uno sguardo ai principi SOLID.

Entro la fine di questo articolo, si spera che avrai una migliore comprensione di OOP e sarai entusiasta di scrivere i tuoi plugin orientati agli oggetti!

Iniziare

Partiamo dal presupposto che tu abbia familiarità con lo sviluppo di plugin di WordPress in generale, quindi ci concentreremo sugli aspetti orientati agli oggetti del nostro plugin. Se non conosci lo sviluppo di plugin o hai bisogno di un aggiornamento, dovresti prima imparare a creare il tuo primo plugin per WordPress.

Iniziamo come facciamo sempre, creando un nuovo file prsdm-limit-login-attempts.php, nella nostra directory dei plugin (cioè /wp-content/plugins/prsdm-limit-login-attempts).

Il file del plug-in principale includerà l'intestazione del plug-in con cui hai già familiarità:

 /** * 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 */

E una semplice istruzione if per impedirne l'accesso diretto.

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

Questo è tutto ciò di cui abbiamo bisogno per ora. Rivisiteremo questo file più tardi!

Creazione di un menu di amministrazione

Quando sviluppi un plug-in, spesso devi fornire ai tuoi utenti un modo per configurarlo. È qui che entra in gioco una pagina delle impostazioni. Per crearne una, aggiungeremo un menu di amministrazione che utilizza l'API delle impostazioni di WordPress.

Quindi, iniziamo a pensare a come sarebbe la nostra API orientata agli oggetti .

Idealmente, vorremmo creare un'istanza della nostra Pressidium_LLA_Settings_Page e farla finita. Per creare un'istanza di una classe, è necessario utilizzare la new parola chiave.

 new Pressidium_LLA_Settings_Page();

Ora, pensiamo a come sarebbe la nostra classe Pressidium_LLA_Settings_Page .

Inizieremo creando una nuova classe, utilizzando la parola chiave class :

 class Pressidium_LLA_Settings_Page {}

Il nome della nostra classe deve essere preceduto da un identificatore univoco, Pressidium_LLA_ per evitare qualsiasi collisione di nomi con altri plugin di WordPress. I prefissi impediscono ad altri plugin di sovrascrivere e/o chiamare accidentalmente le nostre classi. Finché i nomi delle nostre classi sono univoci o usiamo spazi dei nomi, non ci saranno conflitti con altri plugin.

Il costruttore

Ora ci collegheremo a admin_menu e admin_init. Per semplificare le cose, chiameremo semplicemente add_action() nel nostro costruttore (avviso spoiler: lo cambieremo in seguito).

 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' ) ); } }

Le classi che hanno un costruttore, chiamano questo metodo quando un oggetto viene istanziato. Quindi, il metodo __construct() è ottimo per qualsiasi inizializzazione che potremmo voler eseguire.

Diamo un'occhiata più da vicino alle nostre chiamate add_action() . Se in passato hai sviluppato plugin per WordPress, potresti esserti aspettato qualcosa del genere:

 add_action( 'admin_menu', 'my_plugin_prefix_add_page' );

Ma invece abbiamo:

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

Potresti essere confuso sull'uso di un array qui. Ogni volta che vogliamo passare un metodo di un oggetto istanziato come callback/callable, possiamo usare un array contenente un oggetto all'indice 0 e un nome di metodo all'indice 1.

Cos'è $questo?

È una pseudo-variabile disponibile quando un metodo viene chiamato dall'interno di un contesto di oggetto. $this è il valore dell'oggetto chiamante. In questo caso, $this è un'istanza di Pressidium_LLA_Settings_Page .

Inoltre, tutte le nostre "funzioni" sono ora metodi, racchiusi in una classe, quindi non è necessario anteporre i nomi dei nostri metodi.

Spazi dei nomi

Gli spazi dei nomi in PHP ci consentono di raggruppare classi, interfacce, funzioni, ecc. correlati, prevenendo le collisioni di nomi tra il nostro codice e le classi/funzioni interne di PHP o di terze parti.

Andiamo avanti e usiamoli, quindi non dobbiamo aggiungere il prefisso a nessuna delle nostre classi per andare avanti.

Dichiareremo uno spazio dei nomi usando la parola chiave namespace .

 namespace Pressidium;

I namespace possono essere definiti con sottolivelli.

 namespace Pressidium\Limit_Login_Attempts;

Poiché stiamo costruendo una pagina delle impostazioni, dichiareremo uno spazio dei nomi secondario "pagine" per raggruppare insieme qualsiasi cosa relativa alle pagine di amministrazione.

 namespace Pressidium\Limit_Login_Attempts\Pages;

Possiamo finalmente sbarazzarci del prefisso Pressidium_LLA_ !

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

Un altro plugin per WordPress contenente una classe Settings_Page non è più un problema, dal momento che la sua classe e la nostra classe non vivranno nello stesso spazio dei nomi.

Quando creiamo un'istanza della nostra Settings_Page all'interno dello stesso spazio dei nomi, possiamo ometterla:

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

Quando istanziamo la nostra Settings_Page al di fuori del suo spazio dei nomi, dobbiamo specificarla in questo modo:

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

In alternativa, potremmo importare la nostra classe con l'operatore use :

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

Aggiunta di richiamate Hook

Ora, dichiariamo questi add_page() e 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. } }

Il nostro metodo add_page() chiamerà semplicemente la funzione add_menu_page() di WordPress.

 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 ); }

Sembra un modo contorto di sviluppare plugin per WordPress. Sta semplicemente chiamando le funzioni di WordPress, con passaggi aggiuntivi.

Bene, non è esattamente "riutilizzabile", dovremmo comunque scrivere tutto questo codice extra per ogni menu/pagina di amministrazione che vogliamo aggiungere.

Refactoring

Andiamo avanti e refactoring un po' il nostro codice per sfruttare la programmazione orientata agli oggetti e rendere il nostro codice riutilizzabile . Inizieremo sostituendo i nostri valori hardcoded in add_page() con alcuni metodi, in questo modo:

 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 ); }

Definiremo questi metodi come protected , quindi è possibile accedervi solo all'interno della classe stessa e dalle sue classi figlio/genitore.

 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() { /* ... */ }

Grande! Ora possiamo usare questa classe come una classe generica riutilizzabile da cui estendere.

Riprogettazione

Ti avevamo detto che probabilmente sarebbe successo prima o poi. Eccoci qua, a ripensare al design della nostra classe mentre la costruiamo.

Poiché questa sarà la nostra classe base , la rinomineremo con un nome più generico, come Admin_Page . Finora, si presenta così:

 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() { /* ... */ } }

Ora possiamo creare una Settings_Page separata che estende la classe base Admin_Page .

 class Settings_Page extends Admin_Page { // ... }

Questo è un ottimo esempio di ereditarietà , uno dei concetti fondamentali della programmazione orientata agli oggetti. Quando si estende una classe, la classe figlia, Settings_Page , in questo caso, eredita tutti i metodi, le proprietà e le costanti pubblici e protetti dalla classe padre.

Possiamo utilizzarlo e impostare alcuni valori predefiniti. Ad esempio, imposteremo un'icona generica per tutte le pagine di menu, definendo il nostro metodo get_icon_url() questo modo:

 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'; } }

A meno che una classe non sostituisca questi metodi, manterranno la loro funzionalità originale. Quindi, per impostazione predefinita, tutte le classi figlie utilizzeranno quell'icona generica.

Tuttavia, se vogliamo impostare un'altra icona per una pagina di menu specifica, possiamo semplicemente sovrascrivere il metodo get_icon_url() nella nostra classe figlia, in questo modo:

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

Ci sono alcuni valori, tuttavia, che devono essere diversi per ogni classe figlio. Ad esempio, lo slug del menu, il quarto argomento di add_menu_page() , dovrebbe essere univoco per ciascuna pagina del menu.

Se dovessimo definire questo metodo nella nostra classe base Admin_Page , avremmo bisogno di un modo per assicurarci che ogni singola classe figlio sostituisca questo metodo. Bene, possiamo fare qualcosa di ancora meglio. Possiamo dichiarare la firma del metodo e saltare completamente la sua implementazione.

Inserisci metodi astratti!

Classi astratte e metodi

I metodi definiti astratti dichiarano semplicemente la firma del metodo e non possono definirne l'implementazione.

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

Qualsiasi classe che contiene almeno un metodo astratto deve anche essere astratta. Ciò significa che anche la nostra classe Admin_Page dovrebbe essere definita come astratta.

 abstract class Admin_Page { // ...

È anche importante sottolineare qui che le classi definite come astratte non possono essere istanziate. Quindi, non possiamo più creare un'istanza diretta Admin_Page .

Ecco anche una visualizzazione della classe:

Quando si eredita da una classe astratta, la classe figlia deve definire tutti i metodi contrassegnati come astratti nella dichiarazione della sua classe padre. Ciò significa che la nostra Settings_Page deve implementare il metodo get_slug() .

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

Allo stesso modo, dovremmo implementare il resto dei metodi protetti di cui add_page() bisogno.

Prima di procedere su come registreremo le sezioni e i campi della pagina di amministrazione e ne renderemo il contenuto, parliamo un po' delle impostazioni in WordPress.

L'API delle impostazioni

Daremo per scontato che tu abbia già familiarità con l'API delle impostazioni. Ma, per ogni evenienza, ecco il succo:

  • settings_fields(): restituisce i campi nonce, action e option_page per una pagina delle impostazioni. Fondamentalmente, i campi del modulo nascosti.
  • do_settings_sections() — Stampa tutte le sezioni delle impostazioni (e i relativi campi) aggiunte a una particolare pagina delle impostazioni.
  • add_settings_section() — Aggiunge una nuova sezione a una pagina delle impostazioni.
  • add_settings_field() — Aggiunge un nuovo campo a una sezione di una pagina delle impostazioni.
  • register_setting() — Registra un'impostazione e i suoi dati.

Se non hai già familiarità con questo, puoi mettere in pausa la lettura di questo articolo e controllare il nostro articolo correlato su come creare la pagina delle impostazioni per un plug-in personalizzato.

Ora che siamo sulla stessa pagina, torniamo al nostro metodo register_sections() . Ancora una volta, dobbiamo fare un passo indietro e pensare alla nostra API.

Poiché abbiamo definito il metodo add_page() nella classe Admin_Page , definiremo anche lì il metodo render() . Passeremo i valori di ritorno degli altri nostri metodi come argomenti alle funzioni di 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 } }

In questo modo, non dovremo più preoccuparci direttamente di queste funzioni di WordPress. Questo perché qualsiasi pagina di amministrazione che potremmo aggiungere in futuro verrà creata tramite una classe figlio proprio come Settings_Page , e il suo rendering verrà eseguito tramite il metodo render() ereditato della classe padre Admin_Page .

Conclusione

Grande! Abbiamo creato le classi responsabili della registrazione di un menu di amministrazione e dell'aggiunta di una pagina delle impostazioni.

Nel prossimo articolo della serie, continueremo a costruire la nostra pagina delle impostazioni e ne registreremo sezioni, campi ed elementi.

Fare clic qui per leggere la parte 6 della nostra serie di programmazione orientata agli oggetti

Guarda anche

  • WordPress e programmazione orientata agli oggetti: una panoramica
  • Parte 2 – WordPress e programmazione orientata agli oggetti: un esempio del mondo reale
  • Parte 3 – WordPress e programmazione orientata agli oggetti: Α Esempio di WordPress – Definizione dell'ambito
  • Parte 4 – WordPress e programmazione orientata agli oggetti: un esempio di WordPress – Design