Część 5 – WordPress i programowanie obiektowe: przykład WordPressa – Implementacja: menu administracyjne

Opublikowany: 2022-02-04

W naszym poprzednim artykule na temat programowania obiektowego omówiliśmy projekt, który ostatecznie wymyśliliśmy dla naszej wtyczki zorientowanej obiektowo.

Teraz przejdziemy do najbardziej ekscytującej części, w której zagłębimy się w to, jak to zaimplementowaliśmy!

Przeprowadzimy Cię przez niektóre części implementacji, krok po kroku, omawiając podstawy programowania obiektowego, składnię PHP, kilka podstawowych pojęć, a nawet rzucimy okiem na zasady SOLID.

Pod koniec tego artykułu, miejmy nadzieję, lepiej zrozumiesz OOP i będziesz podekscytowany pisaniem własnych wtyczek obiektowych!

Pierwsze kroki

Zakładamy, że ogólnie znasz programowanie wtyczek do WordPressa, więc skupimy się na obiektowych aspektach naszej wtyczki. Jeśli dopiero zaczynasz tworzyć wtyczki lub potrzebujesz odświeżenia, powinieneś najpierw dowiedzieć się, jak zbudować swoją pierwszą wtyczkę WordPress.

Zacznijmy tak jak zawsze, tworząc nowy plik prsdm-limit-login-attempts.php w naszym katalogu wtyczek (tj. /wp-content/plugins/prsdm-limit-login-attempts).

Główny plik wtyczki będzie zawierał nagłówek wtyczki, który już znasz:

 /** * 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 proste stwierdzenie „jeśli”, aby uniemożliwić bezpośredni dostęp do niego.

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

To wszystko, czego na razie potrzebujemy. Wrócimy do tego pliku później!

Budowanie menu administracyjnego

Kiedy tworzysz wtyczkę, często musisz zapewnić użytkownikom możliwość jej konfiguracji. Tutaj pojawia się strona ustawień. Aby ją zbudować, dodamy menu administracyjne, które wykorzystuje API ustawień WordPress.

Zacznijmy więc myśleć o tym, jak wyglądałoby nasze zorientowane obiektowo API.

Idealnie, chcielibyśmy utworzyć instancję naszej Pressidium_LLA_Settings_Page i skończyć z tym. Aby utworzyć instancję klasy, należy użyć słowa kluczowego new .

 new Pressidium_LLA_Settings_Page();

Zastanówmy się teraz, jak wyglądałaby nasza klasa Pressidium_LLA_Settings_Page .

Zaczniemy od utworzenia nowej klasy, używając słowa kluczowego class :

 class Pressidium_LLA_Settings_Page {}

Nazwa naszej klasy musi być poprzedzona unikalnym identyfikatorem Pressidium_LLA_ , aby zapobiec kolizji nazewnictwa z innymi wtyczkami WordPress. Prefiksy uniemożliwiają innym wtyczkom nadpisywanie i/lub przypadkowe wywołanie naszych klas. Dopóki nazwy naszych klas są unikalne — lub używamy przestrzeni nazw — nie będzie żadnych konfliktów z innymi wtyczkami.

Konstruktor

Teraz podłączymy się do admin_menu i admin_init. Aby uprościć sprawę, po prostu wywołamy add_action() w naszym konstruktorze (uwaga na spoiler: zmienimy to później).

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

Klasy, które mają konstruktor, wywołują tę metodę, gdy powstaje instancja obiektu. Tak więc metoda __construct() jest świetna do każdej inicjalizacji, którą możemy chcieć wykonać.

Przyjrzyjmy się bliżej naszym wywołaniom add_action() . Jeśli w przeszłości tworzyłeś wtyczki do WordPressa, mogłeś spodziewać się czegoś takiego:

 add_action( 'admin_menu', 'my_plugin_prefix_add_page' );

Ale zamiast tego mamy:

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

Możesz być zdezorientowany co do użycia tablicy w tym miejscu. Ilekroć chcemy przekazać metodę skonkretyzowanego obiektu jako callback/wywołany, możemy użyć tablicy zawierającej obiekt o indeksie 0 i nazwę metody o indeksie 1.

Co to jest $to?

Jest to pseudozmienna, która jest dostępna, gdy metoda jest wywoływana z kontekstu obiektu. $this jest wartością obiektu wywołującego. W tym przypadku $this jest instancją Pressidium_LLA_Settings_Page .

Dodatkowo, wszystkie nasze „funkcje” są teraz metodami, opakowanymi w klasę, więc nie ma potrzeby dodawania prefiksów do naszych nazw metod.

Przestrzenie nazw

Przestrzenie nazw w PHP pozwalają nam na grupowanie powiązanych klas, interfejsów, funkcji itp., zapobiegając kolizjom nazewnictwa między naszym kodem, a wewnętrznymi klasami/funkcjami PHP lub innych firm.

Idźmy dalej i korzystajmy z nich, aby nie trzeba było prefiksować żadnej z naszych klas, idąc do przodu.

Zadeklarujemy przestrzeń nazw za pomocą słowa kluczowego namespace .

 namespace Pressidium;

Przestrzenie nazw można zdefiniować za pomocą poziomów podrzędnych.

 namespace Pressidium\Limit_Login_Attempts;

Ponieważ budujemy stronę ustawień, zadeklarujemy podprzestrzeń nazw „pages”, aby pogrupować wszystko, co dotyczy stron administracyjnych.

 namespace Pressidium\Limit_Login_Attempts\Pages;

Możemy wreszcie pozbyć się prefiksu Pressidium_LLA_ !

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

Kolejna wtyczka do WordPressa zawierająca klasę Settings_Page nie stanowi już problemu, ponieważ jej klasa i nasza klasa nie będą znajdować się w tej samej przestrzeni nazw.

Podczas tworzenia instancji naszej strony Settings_Page w tej samej przestrzeni nazw możemy ją pominąć:

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

Tworząc wystąpienie naszej strony Settings_Page poza jej przestrzenią nazw, musimy ją określić w następujący sposób:

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

Alternatywnie możemy zaimportować naszą klasę za pomocą operatora use :

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

Dodawanie wywołań zwrotnych Hook

Teraz zadeklarujmy te 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. } }

Nasza metoda add_page() po prostu wywoła funkcję 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 ); }

Wydaje się, że jest to zawiły sposób na tworzenie wtyczek WordPress. To po prostu wywoływanie funkcji WordPressa z dodatkowymi krokami.

Cóż, to nie jest dokładnie „wielokrotne użycie”, nadal musielibyśmy napisać cały ten dodatkowy kod dla każdego menu/strony administracji, które chcemy dodać.

Refaktoryzacja

Przejdźmy dalej i trochę zrefaktoryzujmy nasz kod, aby skorzystać z programowania obiektowego i uczynić nasz kod wielokrotnego użytku . Zaczniemy od zastąpienia naszych wartości zakodowanych na sztywno w add_page() kilkoma metodami, takimi jak:

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

Zdefiniujemy te metody jako protected , aby można było uzyskać do nich dostęp tylko w samej klasie i przez jej klasy podrzędne/rodzice.

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

Świetny! Możemy teraz używać tej klasy jako klasy generycznej wielokrotnego użytku, z której będziemy mogli korzystać.

Przeprojektowanie

Powiedzieliśmy ci, że prawdopodobnie w końcu to się stanie. Oto my, ponownie zastanawiając się nad projektem naszej klasy podczas jej budowania.

Ponieważ będzie to nasza klasa bazowa , zmienimy jej nazwę na bardziej ogólną, na przykład Admin_Page . Jak na razie wygląda to tak:

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

Możemy teraz utworzyć oddzielną stronę Settings_Page , która rozszerza tę klasę bazową Admin_Page .

 class Settings_Page extends Admin_Page { // ... }

To świetny przykład dziedziczenia , jednej z podstawowych koncepcji programowania obiektowego. Podczas rozszerzania klasy klasa potomna — w tym przypadku Settings_Page — dziedziczy wszystkie publiczne i chronione metody, właściwości i stałe z klasy nadrzędnej.

Możemy to wykorzystać i ustawić kilka domyślnych wartości. Na przykład ustawimy ogólną ikonę dla wszystkich stron menu, definiując naszą get_icon_url() następujący sposób:

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

O ile klasa nie zastąpi tych metod, zachowają one swoją pierwotną funkcjonalność. Tak więc domyślnie wszystkie klasy potomne będą używać tej ogólnej ikony.

Jeśli jednak chcemy ustawić inną ikonę dla określonej strony menu, możemy po prostu nadpisać get_icon_url() w naszej klasie potomnej, na przykład:

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

Istnieją jednak pewne wartości, które muszą być inne dla każdej klasy podrzędnej. Na przykład informacja o menu — czwarty argument funkcji add_menu_page() — powinna być unikalna dla każdej strony menu.

Gdybyśmy zdefiniowali tę metodę w naszej klasie bazowej Admin_Page , potrzebowalibyśmy sposobu, aby upewnić się, że każda klasa podrzędna przesłania tę metodę. Cóż, możemy zrobić coś jeszcze lepszego. Możemy zadeklarować sygnaturę metody i całkowicie pominąć jej implementację.

Wprowadź abstrakcyjne metody!

Klasy i metody abstrakcyjne

Metody zdefiniowane jako abstrakcyjne po prostu deklarują sygnaturę metody i nie mogą definiować jej implementacji.

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

Każda klasa, która zawiera co najmniej jedną metodę abstrakcyjną, również musi być abstrakcyjna. Oznacza to, że nasza klasa Admin_Page powinna być również zdefiniowana jako abstrakcyjna.

 abstract class Admin_Page { // ...

Należy również podkreślić, że klasy zdefiniowane jako abstrakcyjne nie mogą być tworzone. Tak więc nie możemy już bezpośrednio tworzyć instancji Admin_Page .

Oto również wizualizacja zajęć:

Podczas dziedziczenia z klasy abstrakcyjnej, klasa potomna musi zdefiniować wszystkie metody oznaczone jako abstrakcyjne w deklaracji swojej klasy nadrzędnej. Oznacza to, że nasza strona Settings_Page musi zaimplementować get_slug() .

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

W ten sam sposób powinniśmy zaimplementować pozostałe chronione metody, których add_page() .

Zanim przejdziemy do tego, jak zarejestrujemy sekcje i pola strony administratora oraz wyrenderujemy ich zawartość, porozmawiajmy trochę o ustawieniach w WordPressie.

Ustawienia API

Zakładamy, że znasz już interfejs Settings API. Ale na wszelki wypadek, oto sedno tego:

  • settings_fields() — Wyświetla pola nonce, action i option_page dla strony ustawień. Zasadniczo ukryte pola formularzy.
  • do_settings_sections() — Drukuje wszystkie sekcje ustawień (i ich pola) dodane do określonej strony ustawień.
  • add_settings_section() — Dodaje nową sekcję do strony ustawień.
  • add_settings_field() — Dodaje nowe pole do sekcji strony ustawień.
  • register_setting() — Rejestruje ustawienie i jego dane.

Jeśli jeszcze tego nie znasz, możesz wstrzymać czytanie tego artykułu i zapoznać się z naszym powiązanym artykułem o tym, jak zbudować stronę ustawień dla niestandardowej wtyczki.

Teraz, gdy jesteśmy na tej samej stronie, wróćmy do naszej metody register_sections() . Po raz kolejny musimy cofnąć się o krok i pomyśleć o naszym API.

Ponieważ zdefiniowaliśmy add_page() w klasie Admin_Page , zdefiniujemy również tam również metodę render() . Przekażemy wartości zwracane przez nasze inne metody jako argumenty do funkcji WordPressa.

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

W ten sposób nigdy więcej nie będziemy musieli zawracać sobie głowy tymi funkcjami WordPressa. Dzieje się tak, ponieważ każda strona administracyjna, którą możemy dodać w przyszłości, zostanie zbudowana za pomocą klasy podrzędnej, podobnie jak Settings_Page , a jej renderowanie zostanie wykonane za pomocą odziedziczonej metody render() klasy nadrzędnej Admin_Page .

Wniosek

Świetny! Stworzyliśmy klasy odpowiedzialne za rejestrację menu administracyjnego i dodanie strony ustawień.

W kolejnym artykule z serii będziemy nadal budować naszą stronę ustawień i rejestrować jej sekcje, pola i elementy.

Kliknij tutaj, aby przeczytać część 6 w naszej serii programowania zorientowanego na obiekt

Zobacz też

  • WordPress i programowanie obiektowe – przegląd
  • Część 2 – WordPress i programowanie obiektowe: przykład ze świata rzeczywistego
  • Część 3 – WordPress i programowanie obiektowe: Α Przykład WordPress – definiowanie zakresu
  • Część 4 – WordPress i programowanie obiektowe: przykład WordPressa – projektowanie