Teil 5 – WordPress und objektorientierte Programmierung: Ein WordPress-Beispiel – Implementierung: Das Verwaltungsmenü
Veröffentlicht: 2022-02-04In unserem vorherigen Artikel über objektorientierte Programmierung haben wir das Design besprochen, das wir schließlich für unser objektorientiertes Plugin entwickelt haben.
Jetzt kommen wir zum aufregendsten Teil, in dem wir uns eingehender mit der Implementierung befassen!
Wir führen Sie Schritt für Schritt durch einige Teile der Implementierung, sprechen über die Grundlagen der objektorientierten Programmierung, die PHP-Syntax, einige Kernkonzepte und werfen sogar einen Blick auf die SOLID-Prinzipien.
Am Ende dieses Artikels haben Sie hoffentlich ein besseres Verständnis von OOP und sind begeistert, Ihre eigenen objektorientierten Plugins zu schreiben!
Einstieg
Wir gehen davon aus, dass Sie mit der Entwicklung von WordPress-Plugins im Allgemeinen vertraut sind, daher konzentrieren wir uns auf die objektorientierten Aspekte unseres Plugins. Wenn Sie neu in der Plugin-Entwicklung sind oder eine Auffrischung benötigen, sollten Sie zuerst lernen, wie Sie Ihr erstes WordPress-Plugin erstellen.
Beginnen wir wie immer, indem wir eine neue prsdm-limit-login-attempts.php-Datei in unserem Plugin-Verzeichnis erstellen (dh /wp-content/plugins/prsdm-limit-login-attempts).
Die Haupt-Plugin-Datei enthält den Plugin-Header, mit dem Sie bereits vertraut sind:
/** * 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 */
Und eine einfache if-Anweisung, um den direkten Zugriff darauf zu verhindern.
if ( ! defined( 'ABSPATH' ) ) { exit; }
Das ist alles, was wir jetzt brauchen. Wir werden uns diese Datei später noch einmal ansehen!
Erstellen eines Verwaltungsmenüs
Wenn Sie ein Plug-in entwickeln, müssen Sie Ihren Benutzern häufig eine Möglichkeit bieten, es zu konfigurieren. Hier kommt eine Einstellungsseite ins Spiel. Um eine zu erstellen, fügen wir ein Verwaltungsmenü hinzu, das die WordPress-Einstellungs-API verwendet.
Beginnen wir also damit, darüber nachzudenken, wie unsere objektorientierte API aussehen würde.
Idealerweise möchten wir unsere Pressidium_LLA_Settings_Page
instanziieren und damit fertig sein. Um eine Instanz einer Klasse zu erstellen, muss das Schlüsselwort new
verwendet werden.
new Pressidium_LLA_Settings_Page();
Lassen Sie uns nun darüber nachdenken, wie unsere Klasse Pressidium_LLA_Settings_Page
aussehen würde.
Wir beginnen mit der Erstellung einer neuen Klasse mit dem Schlüsselwort class
:
class Pressidium_LLA_Settings_Page {}
Unser Klassenname muss mit einem eindeutigen Bezeichner, Pressidium_LLA_
, vorangestellt werden, um Namenskollisionen mit anderen WordPress-Plugins zu vermeiden. Präfixe verhindern, dass andere Plugins unsere Klassen überschreiben und/oder versehentlich aufrufen. Solange unsere Klassennamen eindeutig sind – oder wir Namespaces verwenden – wird es keine Konflikte mit anderen Plugins geben.
Der Konstrukteur
Jetzt werden wir uns in admin_menu und admin_init einklinken. Um die Dinge einfach zu halten, rufen wir einfach add_action() in unserem Konstruktor auf (Spoiler-Alarm: Wir werden dies später ändern).
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' ) ); } }
Klassen, die einen Konstruktor haben, rufen diese Methode auf, wenn ein Objekt instanziiert wird. Daher eignet sich die Methode __construct()
hervorragend für jede Initialisierung, die wir möglicherweise durchführen möchten.
Schauen wir uns unsere add_action()
Aufrufe genauer an. Wenn Sie in der Vergangenheit WordPress-Plugins entwickelt haben, haben Sie vielleicht so etwas erwartet:
add_action( 'admin_menu', 'my_plugin_prefix_add_page' );
Aber stattdessen haben wir:
add_action( 'admin_menu', array( $this, 'add_page' ) );
Sie könnten hier über die Verwendung eines Arrays verwirrt sein. Wann immer wir eine Methode eines instanziierten Objekts als Callback/Callable übergeben möchten, können wir ein Array verwenden, das ein Objekt bei Index 0 und einen Methodennamen bei Index 1 enthält.
Was ist $das?
Es ist eine Pseudovariable, die verfügbar ist, wenn eine Methode aus einem Objektkontext aufgerufen wird. $this
ist der Wert des aufrufenden Objekts. In diesem Fall ist $this
eine Instanz von Pressidium_LLA_Settings_Page
.
Außerdem sind alle unsere „Funktionen“ jetzt Methoden, die in eine Klasse eingeschlossen sind, sodass wir unseren Methodennamen kein Präfix voranstellen müssen.
Namensräume
Namespaces in PHP ermöglichen es uns, verwandte Klassen, Schnittstellen, Funktionen usw. zu gruppieren, wodurch Namenskollisionen zwischen unserem Code und internen Klassen/Funktionen von PHP oder Drittanbietern vermieden werden.
Lassen Sie uns fortfahren und sie verwenden, damit wir in Zukunft keiner unserer Klassen ein Präfix voranstellen müssen.
Wir deklarieren einen Namensraum mit dem Schlüsselwort namespace
.
namespace Pressidium;
Namensräume können mit Unterebenen definiert werden.
namespace Pressidium\Limit_Login_Attempts;
Da wir eine Einstellungsseite erstellen, deklarieren wir einen Unternamensraum „Seiten“, um alles, was mit Verwaltungsseiten zu tun hat, zusammenzufassen.
namespace Pressidium\Limit_Login_Attempts\Pages;
Wir können endlich das Prefix Pressidium_LLA_
loswerden!
namespace Pressidium\Limit_Login_Attempts\Pages; class Settings_Page { // ...
Ein weiteres WordPress-Plugin, das eine Settings_Page
-Klasse enthält, ist kein Problem mehr, da seine Klasse und unsere Klasse nicht im selben Namespace leben.
Wenn wir unsere Settings_Page
innerhalb desselben Namespace instanziieren, können wir sie weglassen:
namespace Pressidium\Limit_Login_Attempts\Pages; $settings_page = new Settings_Page();
Wenn wir unsere Settings_Page
außerhalb ihres Namensraums instanziieren, müssen wir sie wie folgt angeben:
namespace Another\Namespace; $settings_page = new \Pressidium\Limit_Login_Attempts\Pages\Settings_Page();
Alternativ könnten wir unsere Klasse mit dem use
-Operator importieren:
use Pressidium\Limit_Login_Attempts\Pages\Settings_Page; $settings_page = new Settings_Page();
Hook-Callbacks hinzufügen
Lassen Sie uns nun diese add_page()
und register_sections()
deklarieren.
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. } }
Unsere Methode add_page() ruft einfach die WordPress-Funktion add_menu_page() auf.
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 ); }
Das scheint ein komplizierter Weg zu sein, WordPress-Plugins zu entwickeln. Es ruft einfach WordPress-Funktionen mit zusätzlichen Schritten auf.
Nun, das ist nicht gerade „wiederverwendbar“, wir müssten immer noch all diesen zusätzlichen Code für jedes Verwaltungsmenü/jede Seite schreiben, die wir hinzufügen möchten.
Refactoring
Lassen Sie uns fortfahren und unseren Code ein wenig umgestalten, um die Vorteile der objektorientierten Programmierung zu nutzen und unseren Code wiederverwendbar zu machen. Wir beginnen damit, unsere fest codierten Werte in add_page()
durch ein paar Methoden zu ersetzen, etwa so:
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 ); }
Wir definieren diese Methoden als protected
, sodass auf sie nur innerhalb der Klasse selbst und durch ihre untergeordneten/übergeordneten Klassen zugegriffen werden kann.
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() { /* ... */ }
Groß! Wir können diese Klasse jetzt als wiederverwendbare, generische Klasse verwenden, um sie zu erweitern.
Neugestaltung
Wir haben Ihnen gesagt, dass dies wahrscheinlich irgendwann passieren würde. Hier sind wir und überdenken das Design unserer Klasse, während wir sie bauen.
Da dies unsere Basisklasse sein wird, benennen wir sie in einen allgemeineren Namen wie Admin_Page
. Bisher sieht es so aus:
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() { /* ... */ } }
Wir können jetzt eine separate Settings_Page
erstellen, die diese Admin_Page
-Basisklasse erweitert .
class Settings_Page extends Admin_Page { // ... }
Das ist ein großartiges Beispiel für Vererbung , eines der Kernkonzepte der objektorientierten Programmierung. Beim Erweitern einer Klasse erbt die untergeordnete Klasse – in diesem Fall Settings_Page
– alle öffentlichen und geschützten Methoden, Eigenschaften und Konstanten von der übergeordneten Klasse.
Wir können dies nutzen und einige Standardwerte festlegen. Beispielsweise legen wir ein generisches Symbol für alle Menüseiten fest, indem wir unsere Methode get_icon_url()
wie folgt definieren:
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'; } }
Sofern eine Klasse diese Methoden nicht überschreibt, behalten sie ihre ursprüngliche Funktionalität bei. Standardmäßig verwenden also alle untergeordneten Klassen dieses generische Symbol.
Wenn wir jedoch ein anderes Symbol für eine bestimmte Menüseite festlegen möchten, können wir einfach die Methode get_icon_url get_icon_url()
in unserer untergeordneten Klasse wie folgt überschreiben:
class Settings_Page extends Admin_Page { protected function get_icon_url() { return 'dashicons-shield-alt'; } }
Es gibt jedoch einige Werte, die für jede untergeordnete Klasse unterschiedlich sein müssen. Beispielsweise sollte der Menü-Slug – das vierte Argument von add_menu_page()
– für jede Menüseite eindeutig sein.
Wenn wir diese Methode in unserer Admin_Page
definieren würden, müssten wir sicherstellen, dass jede einzelne untergeordnete Klasse diese Methode überschreibt. Nun, wir können noch etwas besser machen. Wir können die Signatur der Methode deklarieren und ihre Implementierung vollständig überspringen.
Geben Sie abstrakte Methoden ein!
Abstrakte Klassen und Methoden
Als abstrakt definierte Methoden deklarieren einfach die Signatur der Methode und können ihre Implementierung nicht definieren.
/** * Return page slug. * * @return string */ abstract protected function get_slug();
Jede Klasse, die mindestens eine abstrakte Methode enthält, muss auch abstrakt sein. Das heißt, unsere Admin_Page-Klasse sollte auch als abstrakt definiert werden.
abstract class Admin_Page { // ...
Es ist auch wichtig darauf hinzuweisen, dass Klassen, die als abstrakt definiert sind, nicht instanziiert werden können. Daher können wir Admin_Page
nicht mehr direkt instanziieren.
Hier ist auch eine Visualisierung der Klasse:
Beim Erben von einer abstrakten Klasse muss die untergeordnete Klasse alle Methoden definieren, die in der Deklaration ihrer übergeordneten Klasse als abstrakt gekennzeichnet sind. Das bedeutet, dass unsere Settings_Page
die Methode get_slug()
implementieren muss.
class Settings_Page extends Admin_Page { // ... protected function get_slug() { return 'prsdm_limit_login_attempts_settings'; } // ... }
Auf die gleiche Weise sollten wir die restlichen geschützten Methoden implementieren, die add_page()
benötigt.
Bevor wir fortfahren, wie wir die Abschnitte und Felder der Admin-Seite registrieren und ihren Inhalt rendern, lassen Sie uns ein wenig über die Einstellungen in WordPress sprechen.
Die Einstellungs-API
Wir gehen davon aus, dass Sie bereits mit der Einstellungs-API vertraut sind. Aber für alle Fälle, hier ist das Wesentliche:
- settings_fields() — Gibt nonce-, action- und option_page-Felder für eine Einstellungsseite aus. Grundsätzlich die versteckten Formularfelder.
- do_settings_sections() — Druckt alle Einstellungsabschnitte (und ihre Felder) aus, die einer bestimmten Einstellungsseite hinzugefügt wurden.
- add_settings_section() — Fügt einen neuen Abschnitt zu einer Einstellungsseite hinzu.
- add_settings_field() — Fügt einem Abschnitt einer Einstellungsseite ein neues Feld hinzu.
- register_setting() — Registriert eine Einstellung und ihre Daten.
Wenn Sie damit noch nicht vertraut sind, können Sie das Lesen dieses Artikels unterbrechen und unseren verwandten Artikel zum Erstellen der Einstellungsseite für ein benutzerdefiniertes Plugin lesen.
Da wir uns nun auf derselben Seite befinden, kehren wir zu unserer Methode register_sections()
zurück. Wieder einmal müssen wir einen Schritt zurücktreten und über unsere API nachdenken.
Da wir die Methode add_page()
in der Klasse Admin_Page
definiert haben, definieren wir dort auch die Methode render()
. Wir übergeben die Rückgabewerte unserer anderen Methoden als Argumente an die WordPress-Funktionen.
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 } }
Auf diese Weise müssen wir uns nie wieder direkt mit diesen WordPress-Funktionen beschäftigen. Das liegt daran, dass jede Admin-Seite, die wir in Zukunft hinzufügen, über eine untergeordnete Klasse erstellt wird, genau wie die Settings_Page
, und ihre Darstellung erfolgt über die geerbte render()
-Methode der übergeordneten Admin_Page
-Klasse.
Fazit
Groß! Wir haben die Klassen erstellt, die für die Registrierung eines Verwaltungsmenüs und das Hinzufügen einer Einstellungsseite verantwortlich sind.
Im nächsten Artikel der Serie werden wir unsere Einstellungsseite weiter aufbauen und ihre Abschnitte, Felder und Elemente registrieren.
Klicken Sie hier, um Teil 6 unserer Serie zur objektorientierten Programmierung zu lesen
Siehe auch
- WordPress und objektorientierte Programmierung – Ein Überblick
- Teil 2 – WordPress und objektorientierte Programmierung: Ein Beispiel aus der Praxis
- Teil 3 – WordPress und objektorientierte Programmierung: Α WordPress-Beispiel – Definition des Geltungsbereichs
- Teil 4 – WordPress und objektorientierte Programmierung: ein WordPress-Beispiel – Design