Widgets de WordPress: Refactorización, Parte 3
En términos de actualización de WordPress Widget Boilerplate (todo lo cual se rastrea en la rama de desarrollo ), hemos recorrido un largo camino en términos de refactorización de cómo está organizado.
Hasta ahora, hemos:
- observó cómo la API de widgets de WordPress proporciona un ejemplo de programación orientada a objetos,
- cómo podemos usar esta API para determinar la programación orientada a objetos en otras áreas de WordPress,
- herramientas instaladas para ayudarnos a evaluar la calidad del código,
- encontró errores que existen actualmente en el código en términos de estándares de programación modernos,
- y comenzó a reorganizar el código base para que se ajuste a las prácticas más modernas.
Ahora estamos listos para comenzar a refactorizar este código de una manera mucho más orientada a objetos.
Entonces, si aún tiene que ponerse al día con las publicaciones anteriores (cualquiera de ellas, en realidad), le recomiendo que lo haga porque llevará un poco de tiempo actualizar esto. Hay mucho código para escribir una explicación.
Empecemos.
El modelo de widget de WordPress: refactorización, parte 3
Podría decirse que el mayor problema con Boilerplate es que todo está encapsulado dentro de una sola clase.
Claro, hay algunas cosas buenas como mantener nuestras vistas separadas de la lógica del lado del servidor, pero eso es todo.
Otros problemas que existen con solo mirar el código incluyen:
- agregando acciones y filtros en el constructor,
- tener métodos para hacer más de una cosa,
- no tener clases responsables de implementar cosas como registrar dependencias,
- y así.
En esta publicación, vamos a comenzar el proceso de creación de abstracciones que eventualmente implementaremos para romper la naturaleza de tipo dios del Boilerplate tal como está.
Esto se dividirá en varias publicaciones para que pueda brindar una explicación sólida de por qué estamos haciendo ciertas cosas y explicar los ejemplos detrás de esto.
Si lo hago de otra manera, la serie omite demasiada información valiosa que es aplicable a otras prácticas de programación orientada a objetos.
¿Qué es un Suscriptor?
El sistema de ganchos de WordPress, es decir, las acciones y los filtros que tenemos disponibles, se basan en un patrón de diseño basado en eventos. Esto significa que cada vez que sucede algo, un evento, WordPress activará cualquier otro código que se haya suscrito a dicho evento.
Entonces, cuando registramos una función con un gancho, nos estamos suscribiendo al evento. Con ese fin, soy fanático de crear suscriptores para cualquier gancho que necesitemos.
Además de eso, los suscriptores generalmente siguen un formato consistente. Esto significa que es muy fácil crear una clase abstracta que implemente algunas de las funciones coherentes y luego permita que la clase que implementa la clase abstracta se centre únicamente en la lógica empresarial.
Una de las formas más fáciles de demostrar esto es a través de la suscripción de archivos CSS y archivos JavaScript porque son dos de las cosas más comunes que usamos cuando creamos complementos.
Crear una clase abstracta
Antes de implementar la clase abstracta, expongamos exactamente qué es lo que vamos a hacer para crear esto.
- Necesitamos una propiedad que represente el evento al que nos estamos suscribiendo.
- Necesitamos una función para activar cada vez que WordPress activa el gancho. Otra forma de pensar en esto es que necesitamos una función para implementar cada vez que WordPress active una acción o filtro determinado.
- Necesitamos clases para implementar la abstracción.
Primero, definamos clases abstractas. Directamente del manual de PHP, leemos:
Las clases definidas como abstractas no se pueden instanciar, y cualquier clase que contenga al menos un método abstracto también debe ser abstracta. Los métodos definidos como abstractos simplemente declaran la firma del método; no pueden definir la implementación.
En resumen, esto significa que en realidad no podemos crear una instancia de una clase abstracta. Solo podemos instanciar clases que definen la implementación.
Sin embargo, esto no significa que la clase abstracta no pueda implementar cosas concretas (como la definición de un gancho). Pero significa que hay ciertos métodos que no tienen implementación.
De lo contrario, nos quedamos con una clase básica.
¿Tener sentido? Vamos a ver.
Crear una clase abstracta
Para esta publicación, vamos a crear una clase abstracta específicamente para archivos CSS y JavaScript.
Recuerde, debido a que esta es una clase abstracta, los suscriptores concretos pueden llamarse de alguna manera que identifique lo que hacen (es decir, pueden llamarse a sí mismos de cualquier forma que represente su propósito). Y llegaremos a eso.
Pero primero, la clase abstracta. Compartiré el código y luego explicaré exactamente qué está pasando con él:
<?php
/*
* This file is part of the WordPress Widget Boilerplate
*
* (c) Tom McFarlin <tom@tommcfarlin.com>
*
* This source file is subject to the GPL license that is bundled
* with this source code in the file LICENSE.
*/
namespace WordPressWidgetBoilerplateSubscriber;
/**
* An abstract implementation of a subscriber that requires a hook and the ability to
* start the class.
*/
abstract class AbstractSubscriber
{
/**
* @var string a reference to the hook to which the subscriber should be registered
*/
protected $hook;
/**
* @param string $hook the hook to which the subscriber is registered
*/
public function __construct(string $hook)
{
$this->hook = $hook;
}
/**
* @return string the hook to which the subscriber is registered
*/
public function getHook(): string
{
return $this->hook;
}
/**
* Implements the domain logic for the concrete class implementating this subcriber.
*/
abstract public function load();
}
- Observe que la propiedad que hemos creado está protegida. Esto es para que las clases de implementación puedan acceder a él, pero nada fuera de él no puede.
- Creé una función para recuperar el gancho que se hará evidente más adelante. Por lo general, odio implementar una funcionalidad que no es inmediatamente obvia, pero esto es algo que es importante dado el lugar al que nos dirigimos.
- Hay una función abstracta llamada load. Aquí es donde cualquier clase que implemente esta función alojará su lógica de negocio, como veremos en un momento.
- Me gusta documentar el propósito de las funciones cuando sea necesario para que estén en un solo lugar y luego deje que las clases de implementación proporcionen la documentación que necesitan para su implementación. Verás esto momentáneamente, también.
Ahora que tenemos la clase abstracta en su lugar, lo último que debemos hacer es asegurarnos de que esté ubicada en el directorio adecuado y con el espacio de nombres. Si ha seguido desde la publicación anterior, es probable que haya podido adivinar dónde residirá en función del espacio de nombres en el código.
Y si no, no te preocupes. Descubrir los espacios de nombres y lo que no puede llevar un poco de tiempo. Entonces, a través de estas publicaciones de membresía y estos ejemplos, espero que se aclare con el tiempo.
Creación de clases concretas
Ahora implementemos esta clase en particular para agregar las hojas de estilo y las fuentes de JavaScript que tenemos. Sin embargo, lo que vas a notar es que son muy similares.
Lo único que difiere es la implementación de la funcionalidad de carga, que es exactamente cómo debería funcionar.
hojas de estilo
Dada la clase abstracta anterior, ahora necesitamos crear una clase para registrar hojas de estilo. Como tenemos dos hojas de estilo, vamos a crear dos clases:
- la primera clase será responsable de registrar la hoja de estilo para el tablero y específicamente para la página del widget de WordPress,
- la segunda clase será responsable de registrar el complemento para el blog real.
Llamemos a cada uno de estos AdminStylesheetSubscriber y PublicStylesheetSubscriber respectivamente.
Primero, el suscriptor de la hoja de estilo del administrador :
<?php
/*
* This file is part of the WordPress Widget Boilerplate
*
* (c) Tom McFarlin <tom@tommcfarlin.com?
*
* This source file is subject to the GPL license that is bundled
* with this source code in the file LICENSE.
*/
namespace WordPressWidgetBoilerplateSubscriber;
/**
* The subscriber responsible for loading the stylesheet on the Widget administration page.
*/
class AdminStyleAssetSubscriber extends AbstractSubscriber
{
/**
* {@inheritdoc}
*/
public function __construct(string $hook)
{
parent::__construct($hook);
}
/**
* Adds the administrative stylesheet to the widget administration page.
*/
public function load()
{
if ('widgets' !== get_current_screen()->id) {
return;
}
wp_enqueue_style(
'wordpress-widget-boilerplate',
plugin_dir_url(dirname(__DIR__)).'assets/css/admin.css'
);
}
}
Tenga en cuenta que esto usa la función get_current_screen() que he usado en publicaciones anteriores para asegurarnos de que estamos agregando dependencias solo donde sea necesario.
Ahora, el suscriptor público de JavaScript. Esto usa la función is_admin() para asegurarse de que no estamos en el área administrativa de WordPress.
<?php
/*
* This file is part of the WordPress Widget Boilerplate
*
* (c) Tom McFarlin <tom@tommcfarlin.com>
*
* This source file is subject to the GPL license that is bundled
* with this source code in the file LICENSE.
*/
namespace WordPressWidgetBoilerplateSubscriber;
/**
* The subscriber responsible for loading the stylesheet on the blog.
*/
class PublicStyleAssetSubscriber extends AbstractSubscriber
{
/**
* {@inheritdoc}
*/
public function __construct(string $hook)
{
parent::__construct($hook);
}
/**
* Adds the stylesheet to the public-facing side of the site.
*/
public function load()
{
if (is_admin()) {
return;
}
wp_enqueue_style(
'wordpress-widget-boilerplate',
plugin_dir_url(dirname(__DIR__)).'assets/css/widget.css'
);
}
}
Obviamente, todavía tenemos que instanciar estas clases. Eso vendrá más adelante en la serie.
JavaScript
Los suscriptores de JavaScript no son muy diferentes, como habrás adivinado. Los separaremos según el área de la aplicación en la que se centren y los nombraremos apropiadamente.
Primero, el suscriptor de JavaScript administrador :
<?php
/*
* This file is part of the WordPress Widget Boilerplate
*
* (c) Tom McFarlin <tom@tommcfarlin.com>
*
* This source file is subject to the GPL license that is bundled
* with this source code in the file LICENSE.
*/
namespace WordPressWidgetBoilerplateSubscriber;
/**
* The subscriber responsible for loading the JavaScript on the Widget's adminsitration page.
*/
class AdminScriptAssetSubscriber extends AbstractSubscriber
{
/**
* {@inheritdoc}
*/
public function __construct(string $hook)
{
parent::__construct($hook);
}
/**
* Adds the administrative JavaScript to the widget administration page.
*/
public function load()
{
if ('widgets' !== get_current_screen()->id) {
return;
}
wp_enqueue_script(
'wordpress-widget-boilerplate',
plugin_dir_url(dirname(__DIR__)).'assets/js/admin.js'
);
}
}
Y el suscriptor público de JavaScript:
<?php
/*
* This file is part of the WordPress Widget Boilerplate
*
* (c) Tom McFarlin <tom@tommcfarlin.com>
*
* This source file is subject to the GPL license that is bundled
* with this source code in the file LICENSE.
*/
namespace WordPressWidgetBoilerplateSubscriber;
/**
* The subscriber responsible for loading the JavaScript on the blog.
*/
class PublicScriptAssetSubscriber extends AbstractSubscriber
{
/**
* {@inheritdoc}
*/
public function __construct(string $hook)
{
parent::__construct($hook);
}
/**
* Adds the JavaScript to the public-facing side of the site.
*/
public function load()
{
if (is_admin()) {
return;
}
wp_enqueue_script(
'wordpress-widget-boilerplate',
plugin_dir_url(dirname(__DIR__)).'assets/js/widget.js'
);
}
}
Una vez más, estas clases aún no se pueden instanciar, pero nos centraremos en eso en una próxima publicación.
Abstracciones e Interfaces
Recuerde que las abstracciones y las interfaces son diferentes pero se confunden fácilmente. Las interfaces contienen absolutamente cero implementación. En su lugar, proporcionan una garantía de que cualquier clase que implemente la interfaz implementará todos los métodos.
Las clases abstractas, por otro lado, pueden tener alguna funcionalidad implementada en la clase abstracta mientras se deja el código específico del dominio, como cargar hojas de estilo y JavaScript, al método apropiado.
Esto se hará evidente, si no lo ha sido ya, cuanto más avancemos en esta serie. Mientras tanto, y como de costumbre, no olvide consultar la rama de desarrollo para ver dónde estamos con el código.


