Część 7 – WordPress i programowanie obiektowe: przykład WordPressa – Implementacja: Zarządzanie hookami WordPressa

Opublikowany: 2022-02-04

Do tego momentu interakcja z API wtyczki oznaczała wywołanie add_action() i add_filters() w konstruktorze każdej klasy.

Jak dotąd to podejście było wystarczająco dobre, ponieważ zapewniało prostotę i pozwalało nam skupić się na nauce programowania obiektowego za pomocą WordPressa. Jednak to nie jest idealne.

Jeśli obiekt rejestruje wszystkie swoje zaczepy podczas tworzenia, rzeczy takie jak testowanie jednostkowe stają się trudne.

UWAGA: Testy jednostkowe powinny testować każdą „jednostkę” oddzielnie. Nawet jeśli w tej chwili nie piszesz testów jednostkowych, pisanie testowalnego kodu zaoszczędzi Ci wiele czasu na refaktoryzację później, jeśli kiedykolwiek zdecydujesz się na pisanie testów.

Menedżer haczyków

Pójdźmy o krok dalej i wprowadźmy nową klasę do zarządzania naszymi hookami, nazwiemy ją Hooks_Manager . Ta klasa będzie odpowiedzialna za rejestrację wszystkich naszych hooków. Stworzymy więc nową klasę za pomocą metody 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 } }

Potrzebujemy interfejsu dla każdej klasy, która do zaimplementowania musi zarejestrować zaczepy.

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

Możesz myśleć o interfejsie jako o kontrakcie , w którym klasa, która implementuje ten interfejs, jest „związana umownie”, aby zaimplementować wszystkie metody zdefiniowane w tym interfejsie.

Na przykład klasa Login_Error , która przechwytuje akcję login_head , musi implementować get_actions() naszego interfejsu Hooks .

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

Metoda register() Hooks_Manager akceptuje obiekt, wywołuje jego get_actions() i rejestruje wszystkie jego akcje.

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

Dodajmy do naszego interfejsu metodę get_filters() , dzięki czemu będziemy mogli rejestrować zarówno akcje, jak i filtry.

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

Wracając do naszej klasy Login_Error , musimy zaimplementować tę nową get_filters() .

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

Zmienimy nazwę metody register() naszego Hooks_Manager na register_actions() . Dodamy również metodę register_filters() . Te dwie metody będą odpowiedzialne odpowiednio za rejestrowanie akcji i filtrów.

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

Teraz możemy ponownie dodać metodę register() , która po prostu wywoła zarówno register_actions() , jak i register_filters() .

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

Co zrobić, jeśli klasa nie musi rejestrować zarówno akcji, jak i filtrów? Interfejs Hooks zawiera dwie metody: get_actions() i get_filters() . Wszystkie klasy, które implementują ten interfejs, będą zmuszone do zaimplementowania obu metod.

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

Na przykład klasa Cookie_Login musi rejestrować tylko akcje, ale jest teraz zmuszona do zaimplementowania metody get_filters() tylko po to, aby zwrócić pustą tablicę.

Zasada segregacji interfejsów (ISP) , „I” w SOLID, stanowi:

„Żaden klient nie powinien być zmuszany do polegania na metodach, których nie używa”.

Oznacza to, że to, co robimy teraz, jest dokładnie tym, czego nie powinniśmy robić.

Segregacja interfejsów

Możemy to naprawić, dzieląc nasz interfejs na mniejsze, bardziej szczegółowe, dzięki czemu nasze klasy będą musiały wiedzieć tylko o metodach, które ich interesują.

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

Nie potrzebujemy już jednocześnie get_actions() i get_filters() , możemy zaimplementować tylko interfejs Actions i pozbyć się get_filters()

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

Z drugiej strony Login_Error , który wymaga działań i filtrów, musi tylko zaimplementować oba interfejsy. Klasy mogą implementować więcej niż jeden interfejs, oddzielając je przecinkami.

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

Teraz, gdy mamy posegregowany interfejs, musimy tylko zaktualizować metodę register() w Hooks_Manager , aby odzwierciedlić nasze zmiany.

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

W ten sposób warunkowo wywołujemy tylko register_actions() , tylko register_filters() , lub oba, w oparciu o interfejsy, które implementuje określony obiekt.

Aby faktycznie użyć menedżera haków:

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

Otóż ​​to! Możemy teraz użyć tego obiektu do zarządzania hookami w całej bazie kodu.

Wniosek

Oczywiście istnieje kilka sposobów zarządzania hookami w sposób obiektowy, właśnie pokazaliśmy jeden z nich. Powinieneś poeksperymentować i znaleźć taki, który odpowiada Twoim potrzebom.

Zostań z nami do ostatniej części tej serii, gdzie zobaczymy, jak obsługiwać opcje w sposób obiektowy, porozmawiamy o enkapsulacji, abstrakcji i jak rozdzielić klasy, aby stworzyć elastyczną wtyczkę, którą łatwo rozszerzać!

Kliknij tutaj, aby przeczytać część 8 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
  • Część 5 – WordPress i programowanie obiektowe: przykład WordPressa – Implementacja: menu administracyjne
  • Część 6 – WordPress i programowanie obiektowe: przykład WordPressa – Implementacja: rejestracja sekcji