Partea 5 – WordPress și programare orientată pe obiecte: Un exemplu WordPress – Implementare: Meniul de administrare

Publicat: 2022-02-04

În articolul nostru anterior despre programarea orientată pe obiecte am discutat despre designul pe care l-am creat pentru pluginul nostru orientat pe obiecte.

Acum, vom intra în partea cea mai interesantă în care vom aprofunda modul în care am implementat-o!

Vă vom prezenta câteva părți ale implementării, pas câte un pas, vorbind despre elementele de bază ale programării orientate pe obiecte, sintaxa PHP, câteva concepte de bază și chiar vom arunca o privire peste principiile SOLID.

Până la sfârșitul acestui articol, sperăm că veți înțelege mai bine OOP și veți fi încântat să vă scrieți propriile pluginuri orientate pe obiecte!

Noțiuni de bază

Presupunem că sunteți familiarizat cu dezvoltarea pluginului WordPress în general, așa că ne vom concentra pe aspectele orientate pe obiecte ale pluginului nostru. Dacă sunteți nou în dezvoltarea de pluginuri sau aveți nevoie de o actualizare, ar trebui să învățați mai întâi cum să vă creați primul plugin WordPress.

Să începem așa cum facem întotdeauna, prin crearea unui nou fișier prsdm-limit-login-attempts.php, în directorul nostru de plugin (adică /wp-content/plugins/prsdm-limit-login-attempts).

Fișierul principal al pluginului va include antetul pluginului cu care ești deja familiarizat:

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

Și o declarație if simplă pentru a preveni accesul direct la ea.

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

Asta e tot ce ne trebuie pentru moment. Vom revizui acest fișier mai târziu!

Crearea unui meniu de administrare

Când dezvoltați un plugin, adesea trebuie să oferiți utilizatorilor o modalitate de a-l configura. Aici intervine o pagină de setări. Pentru a crea una, vom adăuga un meniu de administrare care utilizează API-ul de setări WordPress.

Deci, să începem să ne gândim la cum ar arăta API-ul nostru orientat pe obiecte .

În mod ideal, am dori să instanțiem Pressidium_LLA_Settings_Page și să terminăm cu ea. Pentru a crea o instanță a unei clase, trebuie folosit new cuvânt cheie.

 new Pressidium_LLA_Settings_Page();

Acum, să ne gândim la cum ar arăta clasa noastră Pressidium_LLA_Settings_Page .

Vom începe prin a crea o nouă clasă, folosind cuvântul cheie class :

 class Pressidium_LLA_Settings_Page {}

Numele clasei noastre trebuie să fie prefixat cu un identificator unic, Pressidium_LLA_ , pentru a preveni orice coliziuni de denumire cu alte pluginuri WordPress. Prefixele împiedică alte plugin-uri să suprascrie și/sau să apeleze accidental cursurile noastre. Atâta timp cât numele claselor noastre sunt unice – sau folosim spații de nume – nu vor exista conflicte cu alte plugin-uri.

Constructorul

Acum, ne vom conecta la admin_menu și admin_init. Pentru a menține lucrurile simple, vom apela doar add_action() în constructorul nostru (alertă spoiler: o vom schimba mai târziu).

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

Clasele care au un constructor apelează această metodă atunci când un obiect este instanțiat. Deci, metoda __construct() este excelentă pentru orice inițializare pe care am dori să o efectuăm.

Să aruncăm o privire mai atentă la apelurile noastre add_action() . Dacă ați dezvoltat pluginuri WordPress în trecut, s-ar putea să vă fi așteptat la ceva de genul acesta:

 add_action( 'admin_menu', 'my_plugin_prefix_add_page' );

Dar, în schimb, avem:

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

S-ar putea să fii confuz cu privire la utilizarea unei matrice aici. Ori de câte ori dorim să transmitem o metodă a unui obiect instanțiat ca callback/callable, putem folosi o matrice care conține un obiect la indexul 0 și un nume de metodă la indexul 1.

Ce este $asta?

Este o pseudo-variabilă care este disponibilă atunci când o metodă este apelată din contextul unui obiect. $this este valoarea obiectului apelant. În acest caz, $this este o instanță a Pressidium_LLA_Settings_Page .

În plus, toate „funcțiile” noastre sunt acum metode, împachetate într-o clasă, deci nu este nevoie să prefixăm numele metodelor noastre.

Spații de nume

Spațiile de nume din PHP ne permit să grupăm clase, interfețe, funcții etc. înrudite, prevenind coliziunile de denumire între codul nostru și clase/funcții interne PHP sau terțe.

Să mergem mai departe și să le folosim, astfel încât să nu fie nevoie să prefixăm niciuna dintre clasele noastre înainte.

Vom declara un namespace folosind cuvântul cheie namespace .

 namespace Pressidium;

Spațiile de nume pot fi definite cu sub-niveluri.

 namespace Pressidium\Limit_Login_Attempts;

Deoarece construim o pagină de setări, vom declara un sub-spațiu de nume „pagini” pentru a grupa tot ce are legătură cu paginile de administrare.

 namespace Pressidium\Limit_Login_Attempts\Pages;

În sfârșit putem scăpa de prefixul Pressidium_LLA_ !

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

Un alt plugin WordPress care conține o clasă Settings_Page nu mai este o problemă, deoarece clasa sa și clasa noastră nu vor locui în același spațiu de nume.

Când instanțiem pagina noastră de Settings_Page în același spațiu de nume, o putem omite:

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

Când instanțiem pagina noastră de Settings_Page în afara spațiului de nume, trebuie să o specificăm astfel:

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

Alternativ, am putea importa clasa noastră cu operatorul de use :

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

Adăugarea de apeluri înapoi

Acum, să declarăm aceste add_page() și 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. } }

Metoda noastră add_page() va apela doar funcția 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 ); }

Pare o modalitate complicată de a dezvolta pluginuri WordPress. Este pur și simplu apelarea funcțiilor WordPress, cu pași suplimentari.

Ei bine, asta nu este tocmai „reutilizabil”, ar trebui totuși să scriem tot acest cod suplimentar pentru fiecare meniu/pagină de administrare pe care dorim să o adăugăm.

Refactorizarea

Să mergem mai departe și să refactorăm puțin codul nostru pentru a profita de programarea orientată pe obiecte și a face codul nostru reutilizabil . Vom începe prin a înlocui valorile noastre codificate în add_page() cu câteva metode, cum ar fi:

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

Vom defini aceste metode ca fiind protected , astfel încât să poată fi accesate numai în cadrul clasei în sine și de către clasele sale copil/părinte.

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

Grozav! Acum putem folosi această clasă ca o clasă reutilizabilă, generică de la care să ne extindem.

Reproiectare

Ți-am spus că probabil că asta se va întâmpla în cele din urmă. Iată-ne, regândind designul clasei noastre în timp ce o construim.

Deoarece aceasta va fi clasa noastră de bază , o vom redenumi cu un nume mai generic, cum ar fi Admin_Page . Până acum, arată aș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() { /* ... */ } }

Acum putem crea o pagină de Settings_Page separată care extinde acea clasă de bază Admin_Page .

 class Settings_Page extends Admin_Page { // ... }

Acesta este un exemplu grozav de moștenire , unul dintre conceptele de bază ale programarii orientate pe obiecte. Când extindeți o clasă, clasa copil— Settings_Page , în acest caz—moștenește toate metodele, proprietățile și constantele publice și protejate din clasa părinte.

Putem folosi acest lucru și putem seta câteva valori implicite. De exemplu, vom seta o pictogramă generică pentru toate paginile de meniu, definind metoda noastră get_icon_url() astfel:

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

Cu excepția cazului în care o clasă înlocuiește aceste metode, acestea își vor păstra funcționalitatea inițială. Deci, în mod implicit, toate clasele copil vor folosi acea pictogramă generică.

Cu toate acestea, dacă dorim să setăm o altă pictogramă pentru o anumită pagină de meniu, putem pur și simplu să suprascriem metoda get_icon_url() din clasa noastră copil, astfel:

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

Există însă câteva valori care trebuie să fie diferite pentru fiecare clasă de copil. De exemplu, meniul slug — al patrulea argument al add_menu_page() ar trebui să fie unic pentru fiecare pagină de meniu.

Dacă am defini această metodă în clasa noastră de bază Admin_Page , am avea nevoie de o modalitate de a ne asigura că fiecare clasă copil suprascrie această metodă. Ei bine, putem face ceva și mai bun. Putem declara semnătura metodei și să omitem complet implementarea acesteia.

Introduceți metode abstracte!

Clase și metode abstracte

Metodele definite ca abstracte declară pur și simplu semnătura metodei și nu pot defini implementarea acesteia.

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

Orice clasă care conține cel puțin o metodă abstractă trebuie să fie, de asemenea, abstractă. Aceasta înseamnă că clasa noastră Admin_Page ar trebui definită și ca abstractă.

 abstract class Admin_Page { // ...

De asemenea, este important să subliniem aici că clasele definite ca abstracte nu pot fi instanțiate. Deci, nu mai putem instanția direct Admin_Page .

Iată și o vizualizare a clasei:

Când moștenește dintr-o clasă abstractă, clasa copil trebuie să definească toate metodele marcate abstract în declarația clasei părinte. Adică, pagina noastră de Settings_Page trebuie să implementeze metoda get_slug() .

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

În același mod, ar trebui să implementăm restul metodelor protejate de care are nevoie add_page() .

Înainte de a continua cu privire la modul în care vom înregistra secțiunile și câmpurile paginii de administrare și vom reda conținutul acestora, să vorbim puțin despre setările din WordPress.

API-ul Setări

Vom presupune că sunteți deja familiarizat cu API-ul Setări. Dar, pentru orice eventualitate, iată esența:

  • settings_fields() — Afișează câmpurile nonce, action și option_page pentru o pagină de setări. Practic, câmpurile de formular ascunse.
  • do_settings_sections() — Tipărește toate secțiunile de setări (și câmpurile acestora) adăugate la o anumită pagină de setări.
  • add_settings_section() — Adaugă o nouă secțiune la o pagină de setări.
  • add_settings_field() — Adaugă un câmp nou la o secțiune a unei pagini de setări.
  • register_setting() — Înregistrează o setare și datele acesteia.

Dacă nu sunteți deja familiarizat cu acest lucru, puteți întrerupe citirea acestui articol și consultați articolul nostru conex despre cum să creați pagina de setări pentru un plugin personalizat.

Acum că suntem pe aceeași pagină, să revenim la metoda noastră register_sections() . Încă o dată, trebuie să facem un pas înapoi și să ne gândim la API-ul nostru.

Deoarece am definit metoda add_page() în clasa Admin_Page , vom defini și metoda render() acolo. Vom transmite valorile returnate ale celorlalte metode ca argumente funcțiilor 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 } }

În acest fel, nu va trebui să ne deranjez niciodată direct cu aceste funcții WordPress. Acest lucru se datorează faptului că orice pagină de administrare pe care o putem adăuga în viitor va fi construită printr-o clasă copil, la fel ca Settings_Page , iar redarea acesteia se va face prin metoda moștenită render() a clasei părinte Admin_Page .

Concluzie

Grozav! Am creat clasele care sunt responsabile pentru înregistrarea unui meniu de administrare și adăugarea unei pagini de setări.

În următorul articol al seriei, vom continua să construim pagina noastră de setări și să înregistrăm secțiunile, câmpurile și elementele acesteia.

Faceți clic aici pentru a citi partea 6 din seria noastră de programare orientată pe obiecte

Vezi si

  • WordPress și programarea orientată pe obiecte – O prezentare generală
  • Partea 2 – WordPress și programarea orientată pe obiecte: un exemplu din lumea reală
  • Partea 3 – WordPress și programare orientată pe obiecte: Un exemplu WordPress – Definirea domeniului de aplicare
  • Partea 4 – WordPress și programare orientată pe obiecte: un exemplu WordPress – Design