Teil 7 – WordPress und objektorientierte Programmierung: Ein WordPress-Beispiel – Implementierung: Verwalten von WordPress-Hooks

Veröffentlicht: 2022-02-04

Bis zu diesem Punkt bedeutete die Interaktion mit der Plugin-API den Aufruf add_action() und add_filters() im Konstruktor jeder Klasse.

Bisher war dieser Ansatz gut genug, da er die Dinge einfach hielt und es uns ermöglichte, uns darauf zu konzentrieren, mehr über die objektorientierte Programmierung mit WordPress zu lernen. Es ist jedoch nicht ideal.

Wenn ein Objekt bei seiner Erstellung alle seine Hooks registriert, werden Dinge wie Komponententests schwierig.

HINWEIS: Einheitentests sollten jede „Einheit“ isoliert testen. Selbst wenn Sie im Moment keine Unit-Tests schreiben, spart Ihnen das Schreiben von testbarem Code viel Zeit beim späteren Refactoring, falls Sie sich jemals entscheiden sollten, Tests zu schreiben.

Der Hooks-Manager

Lassen Sie uns noch einen Schritt weiter gehen und eine neue Klasse einführen, um unsere Hooks zu verwalten, wir nennen sie Hooks_Manager . Diese Klasse wird für die Registrierung aller unserer Haken verantwortlich sein. Also erstellen wir eine neue Klasse mit einer Methode register() .

 class Hooks_Manager { /** * Register the hooks of the given object. * * @param object $object */ public function register( $object ) { // Register the hooks the specified object needs } }

Wir brauchen eine Schnittstelle für jede Klasse, die Hooks zur Implementierung registrieren muss.

 interface Hooks { /** * Return the actions to register. * * @return array */ public function get_actions(); }

Sie können sich eine Schnittstelle als einen Vertrag vorstellen , bei dem eine Klasse, die diese Schnittstelle implementiert, „vertraglich gebunden“ ist, alle in dieser Schnittstelle definierten Methoden zu implementieren.

Beispielsweise muss eine Klasse Login_Error , die sich in die Aktion login_head , die Methode get_actions() unserer Hooks -Schnittstelle implementieren.

 class Login_Error implements Hooks { public function get_actions() { return array( 'login_head' => array( 'add_errors', 10, 1 ), ); } }

Die Methode register() von Hooks_Manager akzeptiert ein Objekt, ruft seine Methode get_actions() auf und registriert alle seine Aktionen.

 public function register( $object ) { $actions = $object->get_actions(); foreach ( $actions as $action_name => $action_details ) { $method = $action_details[0]; $priority = $action_details[1]; $accepted_args = $action_details[2]; add_action( $action_name, array( $object, $method ), $priority, $accepted_args ); } }

Fügen wir unserer Schnittstelle eine get_filters() -Methode hinzu, damit wir sowohl Aktionen als auch Filter registrieren können.

 interface Hooks { /** * Return the actions to register. * * @return array */ public function get_actions(); /** * Return the filters to register. * * @return array */ public function get_filters(); }

Zurück zu unserer Login_Error -Klasse müssen wir diese neue get_filters() Methode implementieren.

 class Login_Error implements Hooks { public function get_actions() { return array( 'login_head' => array( 'add_errors', 10, 1 ), ); } public function get_filters() { return array( 'authenticate' => array( 'track_credentials', 10, 3 ), 'shake_error_code' => array( 'add_error_code', 10, 1 ), 'login_errors' => array( 'format_error_message', 10, 1 ), ); } }

Wir werden die Methode register() unseres Hooks_Manager in register_actions() umbenennen. Wir fügen auch eine Methode register_filters() hinzu. Diese beiden Methoden sind für die Registrierung von Aktionen bzw. Filtern verantwortlich.

 class Hooks_Manager { /** * Register the actions of the given object. * * @param object $object */ private function register_actions( $object ) { $actions = $object->get_actions(); foreach ( $actions as $action_name => $action_details ) { $method = $action_details[0]; $priority = $action_details[1]; $accepted_args = $action_details[2]; add_action( $action_name, array( $object, $method ), $priority, $accepted_args ); } } /** * Register the filters of the given object. * * @param object $object */ private function register_filters( $object ) { $filters = $object->get_filters(); foreach ( $filters as $filter_name => $filter_details ) { $method = $filter_details[0]; $priority = $filter_details[1]; $accepted_args = $filter_details[2]; add_filter( $filter_name, array( $object, $method ), $priority, $accepted_args ); } } }

Jetzt können wir wieder eine Methode register() hinzufügen, die einfach sowohl register_actions() als auch register_filters() .

 class Hooks_Manager { /** * Register an object. * * @param object $object */ public function register( $object ) { $this->register_actions( $object ); $this->register_filters( $object ); } // ...

Was ist, wenn eine Klasse nicht sowohl Aktionen als auch Filter registrieren muss? Die Hooks -Schnittstelle enthält zwei Methoden: get_actions() und get_filters() . Alle Klassen, die diese Schnittstelle implementieren, werden gezwungen, beide Methoden zu implementieren.

 class Cookie_Login implements Hooks { public function get_actions() { return array( 'auth_cookie_bad_username' => array( 'handle_bad_username', 10, 1 ), 'auth_cookie_bad_hash' => array( 'handle_bad_hash', 10, 1 ), 'auth_cookie_valid' => array( 'handle_valid', 10, 2 ), ); } public function get_filters() { return array(); } }

Beispielsweise muss die Klasse Cookie_Login nur Aktionen registrieren, aber sie ist jetzt gezwungen, die Methode get_filters() zu implementieren, nur um ein leeres Array zurückzugeben.

Das Interface Segregation Principle (ISP) , das „I“ in SOLID, besagt:

„Kein Kunde sollte gezwungen werden, sich auf Methoden zu verlassen, die er nicht verwendet.“

Das heißt, was wir jetzt tun, ist genau das, was wir nicht tun sollten.

Schnittstellentrennung

Wir können dies beheben, indem wir unsere Schnittstelle in kleinere, spezifischere aufteilen, sodass unsere Klassen nur die Methoden kennen müssen, die sie interessieren.

 interface Actions { /** * Return the actions to register. * * @return array */ public function get_actions(); }
 interface Filters { /** * Return the filters to register. * * @return array */ public function get_filters(); }

Wir brauchen nicht mehr sowohl get_actions() als get_filters() , wir können nur die Actions -Schnittstelle implementieren und get_filters get_filters() ) loswerden.

 class Cookie_Login implements Actions { public function get_actions() { return array( 'auth_cookie_bad_username' => array( 'handle_bad_username', 10, 1 ), 'auth_cookie_bad_hash' => array( 'handle_bad_hash', 10, 1 ), 'auth_cookie_valid' => array( 'handle_valid', 10, 2 ), ); } }

Andererseits muss Login_Error , das Aktionen und Filter benötigt, nur beide Schnittstellen implementieren. Klassen können mehr als eine Schnittstelle implementieren, indem sie durch ein Komma getrennt werden.

 class Login_Error implements Actions, Filters { public function get_actions() { return array( 'login_head' => array( 'add_errors', 10, 1 ), ); } public function get_filters() { return array( 'authenticate' => array( 'track_credentials', 10, 3 ), 'shake_error_code' => array( 'add_error_code', 10, 1 ), 'login_errors' => array( 'format_error_message', 10, 1 ), ); } }

Nachdem wir unsere Schnittstelle getrennt haben, müssen wir nur noch die Methode register() von Hooks_Manager , um unsere Änderungen widerzuspiegeln.

 class Hooks_Manager { /** * Register an object. * * @param object $object */ public function register( $object ) { if ( $object instanceof Actions ) { $this->register_actions( $object ); } if ( $object instanceof Filters ) { $this->register_filters( $object ); } } // ...

Auf diese Weise rufen wir bedingt nur register_actions() , nur register_filters() oder beide auf, basierend auf der/den Schnittstelle(n), die das angegebene Objekt implementiert.

So verwenden Sie den Hooks-Manager tatsächlich:

 $hooks_manager = new Hooks_Manager(); $hooks_manager->register( $login_error ); $hooks_manager->register( $cookie_login );

Das ist es! Wir können dieses Objekt jetzt verwenden, um Hooks in der gesamten Codebasis zu verwalten.

Fazit

Natürlich gibt es mehrere Möglichkeiten, Ihre Hooks objektorientiert zu verwalten, wir haben Ihnen gerade eine davon gezeigt. Sie sollten experimentieren und eine finden, die Ihren Bedürfnissen entspricht.

Bleiben Sie für den letzten Teil dieser Serie bei uns, in dem wir sehen, wie Sie Optionen objektorientiert handhaben, über Kapselung, Abstraktion sprechen und wie Sie Ihre Klassen entkoppeln, um ein flexibles Plugin zu erstellen, das einfach zu erweitern ist!

Klicken Sie hier, um Teil 8 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
  • Teil 5 – WordPress und objektorientierte Programmierung: Ein WordPress-Beispiel – Implementierung: Das Verwaltungsmenü
  • Teil 6 – WordPress und objektorientierte Programmierung: Ein WordPress-Beispiel – Implementierung: Registrieren der Abschnitte