Parte 7 - WordPress y programación orientada a objetos: un ejemplo de WordPress - Implementación: administración de ganchos de WordPress

Publicado: 2022-02-04

Hasta este punto, interactuar con la API del complemento significaba llamar a add_action() y add_filters() en el constructor de cada clase.

Hasta ahora, ese enfoque fue lo suficientemente bueno, ya que mantuvo las cosas simples y nos permitió concentrarnos en aprender más sobre la programación orientada a objetos con WordPress. Sin embargo, no es lo ideal.

Si un objeto registra todos sus ganchos cuando se crea, cosas como las pruebas unitarias se vuelven complicadas.

NOTA: Las pruebas unitarias deben probar cada "unidad" de forma aislada. Incluso si no está escribiendo pruebas unitarias en este momento, escribir código comprobable le ahorrará mucho tiempo de refactorización más adelante, si alguna vez decide escribir pruebas.

El administrador de ganchos

Vayamos un paso más allá e introduzcamos una nueva clase para administrar nuestros ganchos, la llamaremos Hooks_Manager . Esta clase será responsable del registro de todos nuestros ganchos. Entonces, crearemos una nueva clase con un método 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 } }

Necesitamos una interfaz para cada clase que necesite registrar ganchos para implementar.

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

Puede pensar en una interfaz como un contrato , donde una clase que implementa esa interfaz está "vinculada por contrato" para implementar todos los métodos definidos en esa interfaz.

Por ejemplo, una clase Login_Error que se enlaza con la acción login_head , debe implementar el método get_actions() de nuestra interfaz Hooks .

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

El método register() de Hooks_Manager acepta un objeto, llama a su método get_actions() y registra todas sus acciones.

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

Agreguemos un método get_filters() a nuestra interfaz, para que podamos registrar tanto acciones como filtros.

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

Volviendo a nuestra clase Login_Error , necesitamos implementar este nuevo método 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 ), ); } }

Cambiaremos el nombre del método register() de nuestro Hooks_Manager a register_actions() . También agregaremos un método register_filters() . Estos dos métodos se encargarán de registrar acciones y filtros respectivamente.

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

Ahora podemos agregar un método register() nuevamente, que simplemente llamará a register_actions() y register_filters() .

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

¿Qué pasa si una clase no necesita registrar acciones y filtros? La interfaz Hooks contiene dos métodos: get_actions() y get_filters() . Todas las clases que implementen esa interfaz se verán obligadas a implementar ambos métodos.

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

Por ejemplo, la clase Cookie_Login tiene que registrar solo acciones, pero ahora se ve obligada a implementar el método get_filters() solo para devolver una matriz vacía.

El Principio de Segregación de Interfaz (ISP) , la "I" en SOLID, establece:

“Ningún cliente debe verse obligado a depender de métodos que no utiliza”.

Lo que significa que lo que estamos haciendo ahora es exactamente lo que no deberíamos estar haciendo.

Segregación de interfaz

Podemos solucionar esto dividiendo nuestra interfaz en otras más pequeñas y específicas para que nuestras clases solo tengan que conocer los métodos que les interesan.

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

Ya no necesitamos tanto get_actions() como get_filters() , podemos implementar solo la interfaz de Actions y deshacernos de 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 ), ); } }

Por otro lado, Login_Error , que necesita acciones y filtros, solo tiene que implementar ambas interfaces. Las clases pueden implementar más de una interfaz separándolas con una coma.

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

Ahora que hemos segregado nuestra interfaz, solo tenemos que actualizar el método register() de Hooks_Manager para reflejar nuestros cambios.

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

De esa manera, llamamos condicionalmente solo a register_actions() , solo a register_filters() , o a ambos, en función de la(s) interfaz(es) que implementa el objeto especificado.

Para usar realmente el administrador de ganchos:

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

¡Eso es todo! Ahora podemos usar ese objeto para administrar ganchos en todo el código base.

Conclusión

Por supuesto, hay varias formas de administrar sus ganchos de forma orientada a objetos, solo le mostramos una de ellas. Debe experimentar y encontrar uno que se adapte a sus necesidades.

Quédese con nosotros para la última parte de esta serie, donde veremos cómo manejar las opciones de una manera orientada a objetos, hablaremos sobre encapsulación, abstracción y cómo desacoplar sus clases para crear un complemento flexible que sea fácil de extender.

Haga clic aquí para leer la Parte 8 de nuestra Serie de Programación Orientada a Objetos

Ver también

  • WordPress y la programación orientada a objetos: una descripción general
  • Parte 2 – WordPress y Programación Orientada a Objetos: Un Ejemplo del Mundo Real
  • Parte 3 – WordPress y Programación Orientada a Objetos: Α Ejemplo de WordPress – Definición del Alcance
  • Parte 4 – WordPress y Programación Orientada a Objetos: Un Ejemplo de WordPress – Diseño
  • Parte 5 – WordPress y Programación Orientada a Objetos: Un Ejemplo de WordPress – Implementación: El Menú de Administración
  • Parte 6 – WordPress y Programación Orientada a Objetos: Un Ejemplo de WordPress – Implementación: Registro de las Secciones