Parte 5 – WordPress y Programación Orientada a Objetos: Un Ejemplo de WordPress – Implementación: El Menú de Administración

Publicado: 2022-02-04

En nuestro artículo anterior sobre Programación Orientada a Objetos, discutimos el diseño que finalmente se nos ocurrió para nuestro complemento orientado a objetos.

¡Ahora, entraremos en la parte más emocionante donde profundizaremos en cómo lo implementamos!

Lo guiaremos a través de algunas partes de la implementación, un paso a la vez, hablando sobre los conceptos básicos de la programación orientada a objetos, la sintaxis de PHP, algunos conceptos básicos e incluso echaremos un vistazo a los principios de SOLID.

Al final de este artículo, con suerte tendrá una mejor comprensión de OOP y estará entusiasmado por escribir sus propios complementos orientados a objetos.

Empezando

Suponemos que está familiarizado con el desarrollo de complementos de WordPress en general, por lo que nos centraremos en los aspectos orientados a objetos de nuestro complemento. Si es nuevo en el desarrollo de complementos o necesita un repaso, primero debe aprender a crear su primer complemento de WordPress.

Comencemos como siempre lo hacemos, creando un nuevo archivo prsdm-limit-login-attempts.php, en nuestro directorio de complementos (es decir, /wp-content/plugins/prsdm-limit-login-attempts).

El archivo principal del complemento incluirá el encabezado del complemento con el que ya está familiarizado:

 /** * 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 */

Y una declaración if simple para evitar el acceso directo a ella.

 if ( ! defined( 'ABSPATH' ) ) { exit; }

Eso es todo lo que necesitamos por ahora. ¡Revisaremos este archivo más tarde!

Creación de un menú de administración

Cuando está desarrollando un complemento, a menudo necesita proporcionar a sus usuarios una forma de configurarlo. Ahí es donde entra en juego una página de configuración. Para crear una, agregaremos un menú de administración que utilice la API de configuración de WordPress.

Entonces, comencemos a pensar en cómo se vería nuestra API orientada a objetos .

Idealmente, nos gustaría instanciar nuestra Pressidium_LLA_Settings_Page y terminar con ella. Para crear una instancia de una clase, se debe usar la new palabra clave.

 new Pressidium_LLA_Settings_Page();

Ahora, pensemos en cómo se vería nuestra clase Pressidium_LLA_Settings_Page .

Comenzaremos creando una nueva clase, usando la palabra clave class :

 class Pressidium_LLA_Settings_Page {}

Nuestro nombre de clase debe tener un prefijo con un identificador único, Pressidium_LLA_ para evitar colisiones de nombres con otros complementos de WordPress. Los prefijos evitan que otros complementos sobrescriban y/o llamen accidentalmente a nuestras clases. Mientras nuestros nombres de clase sean únicos, o usemos espacios de nombres, no habrá ningún conflicto con otros complementos.

el constructor

Ahora, nos conectaremos a admin_menu y admin_init. Para simplificar las cosas, llamaremos a add_action() en nuestro constructor (alerta de spoiler: cambiaremos esto más adelante).

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

Las clases que tienen un constructor, llaman a este método cuando se crea una instancia de un objeto. Por lo tanto, el método __construct() es excelente para cualquier inicialización que queramos realizar.

Echemos un vistazo más de cerca a nuestras llamadas add_action() . Si ha desarrollado complementos de WordPress en el pasado, es posible que haya esperado algo como esto:

 add_action( 'admin_menu', 'my_plugin_prefix_add_page' );

Pero en cambio, tenemos:

 add_action( 'admin_menu', array( $this, 'add_page' ) );

Es posible que esté confundido acerca del uso de una matriz aquí. Cada vez que queramos pasar un método de un objeto instanciado como devolución de llamada/invocable, podemos usar una matriz que contenga un objeto en el índice 0 y un nombre de método en el índice 1.

¿Qué es $esto?

Es una pseudovariable que está disponible cuando se llama a un método desde el contexto de un objeto. $this es el valor del objeto que llama. En este caso, $this es una instancia de Pressidium_LLA_Settings_Page .

Además, todas nuestras "funciones" ahora son métodos, envueltos en una clase, por lo que no hay necesidad de anteponer los nombres de nuestros métodos.

Espacios de nombres

Los espacios de nombres en PHP nos permiten agrupar clases, interfaces, funciones, etc. relacionadas, evitando colisiones de nombres entre nuestro código y PHP interno o clases/funciones de terceros.

Avancemos y usémoslos, para que no tengamos que prefijar ninguna de nuestras clases en el futuro.

Declararemos un espacio de nombres usando la palabra clave de namespace de nombres.

 namespace Pressidium;

Los espacios de nombres se pueden definir con subniveles.

 namespace Pressidium\Limit_Login_Attempts;

Dado que estamos creando una página de configuración, declararemos un subespacio de nombres de "páginas" para agrupar todo lo relacionado con las páginas de administración.

 namespace Pressidium\Limit_Login_Attempts\Pages;

¡Finalmente podemos deshacernos del prefijo Pressidium_LLA_ !

 namespace Pressidium\Limit_Login_Attempts\Pages; class Settings_Page { // ...

Otro complemento de WordPress que contiene una clase Settings_Page ya no es un problema, ya que su clase y nuestra clase no vivirán en el mismo espacio de nombres.

Al instanciar nuestra página de Settings_Page dentro del mismo espacio de nombres, podemos omitirla:

 namespace Pressidium\Limit_Login_Attempts\Pages; $settings_page = new Settings_Page();

Al instanciar nuestra página de Settings_Page fuera de su espacio de nombres, tenemos que especificarlo así:

 namespace Another\Namespace; $settings_page = new \Pressidium\Limit_Login_Attempts\Pages\Settings_Page();

Alternativamente, podríamos importar nuestra clase con el operador use :

 use Pressidium\Limit_Login_Attempts\Pages\Settings_Page; $settings_page = new Settings_Page();

Agregar devoluciones de llamada de enlace

Ahora, declaremos estos add_page() y register_sections() .

 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. } }

Nuestro método add_page() simplemente llamará a la función de WordPress add_menu_page().

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

Esa parece una forma complicada de desarrollar complementos de WordPress. Es simplemente llamar a las funciones de WordPress, con pasos adicionales.

Bueno, eso no es exactamente "reutilizable", aún tendríamos que escribir todo este código adicional para cada menú/página de administración que queramos agregar.

refactorización

Avancemos y refactoricemos un poco nuestro código para aprovechar la programación orientada a objetos y hacer que nuestro código sea reutilizable . Comenzaremos reemplazando nuestros valores codificados en add_page() con algunos métodos, así:

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

Definiremos estos métodos como protected , por lo que solo se puede acceder a ellos dentro de la propia clase y por sus clases secundarias/principales.

 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() { /* ... */ }

¡Excelente! Ahora podemos usar esta clase como una clase genérica reutilizable para extender.

Rediseño

Les dijimos que esto probablemente sucedería eventualmente. Aquí estamos, repensando el diseño de nuestra clase mientras la construimos.

Dado que esta será nuestra clase base , le cambiaremos el nombre a un nombre más genérico, como Admin_Page . Hasta ahora, se ve así:

 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() { /* ... */ } }

Ahora podemos crear una Settings_Page separada que amplíe esa clase base Admin_Page .

 class Settings_Page extends Admin_Page { // ... }

Ese es un gran ejemplo de herencia , uno de los conceptos centrales de la programación orientada a objetos. Al extender una clase, la clase secundaria, Settings_Page , en este caso, hereda todos los métodos, propiedades y constantes públicos y protegidos de la clase principal.

Podemos hacer uso de esto y establecer algunos valores predeterminados. Por ejemplo, estableceremos un ícono genérico para todas las páginas del menú, definiendo nuestro método get_icon_url() de esta manera:

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

A menos que una clase anule esos métodos, conservarán su funcionalidad original. Entonces, de manera predeterminada, todas las clases secundarias usarán ese ícono genérico.

Sin embargo, si queremos establecer otro ícono para una página de menú específica, simplemente podemos anular el método get_icon_url() en nuestra clase secundaria, así:

 class Settings_Page extends Admin_Page { protected function get_icon_url() { return 'dashicons-shield-alt'; } }

Sin embargo, hay algunos valores que deben ser diferentes para cada clase secundaria. Por ejemplo, el slug del menú, el cuarto argumento de add_menu_page() , debe ser único para cada página de menú.

Si definiéramos este método en nuestra clase base Admin_Page , necesitaríamos una forma de asegurarnos de que cada clase secundaria sobrescriba este método. Bueno, podemos hacer algo aún mejor. Podemos declarar la firma del método y omitir por completo su implementación.

Introduzca métodos abstractos!

Clases abstractas y métodos

Los métodos definidos como abstractos simplemente declaran la firma del método y no pueden definir su implementación.

 /** * Return page slug. * * @return string */ abstract protected function get_slug();

Cualquier clase que contenga al menos un método abstracto también debe ser abstracto. Eso significa que nuestra clase Admin_Page también debe definirse como abstracta.

 abstract class Admin_Page { // ...

También es importante señalar aquí que las clases definidas como abstractas no se pueden instanciar. Por lo tanto, ya no podemos instanciar Admin_Page directamente.

Aquí hay también una visualización de la clase:

Al heredar de una clase abstracta, la clase secundaria debe definir todos los métodos marcados como abstractos en la declaración de su clase principal. Lo que significa que nuestra página de Settings_Page tiene que implementar el método get_slug() .

 class Settings_Page extends Admin_Page { // ... protected function get_slug() { return 'prsdm_limit_login_attempts_settings'; } // ... }

De la misma manera, deberíamos implementar el resto de los métodos protegidos que necesita add_page() .

Antes de continuar con cómo registraremos las secciones y los campos de la página de administración y mostraremos su contenido, hablemos un poco sobre la configuración en WordPress.

La API de configuración

Asumiremos que ya está familiarizado con la API de configuración. Pero, por si acaso, aquí está la esencia de esto:

  • settings_fields() — Muestra los campos nonce, action y option_page para una página de configuración. Básicamente, los campos de formulario ocultos.
  • do_settings_sections() — Imprime todas las secciones de configuración (y sus campos) agregadas a una página de configuración en particular.
  • add_settings_section() — Agrega una nueva sección a una página de configuración.
  • add_settings_field() — Agrega un nuevo campo a una sección de una página de configuración.
  • register_setting() — Registra una configuración y sus datos.

Si aún no está familiarizado con esto, puede pausar la lectura de este artículo y consultar nuestro artículo relacionado sobre cómo crear la página de configuración para un complemento personalizado.

Ahora que estamos en la misma página, volvamos a nuestro método register_sections() . Una vez más, tenemos que dar un paso atrás y pensar en nuestra API.

Como hemos definido el método add_page() en la clase Admin_Page , también definiremos allí el método render() . Pasaremos los valores de retorno de nuestros otros métodos como argumentos a las funciones de WordPress.

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

De esa manera, no tendremos que molestarnos directamente con estas funciones de WordPress nunca más. Esto se debe a que cualquier página de administración que agreguemos en el futuro se creará a través de una clase secundaria como la página de Settings_Page , y su representación se realizará a través del método heredado render() de la clase principal Admin_Page .

Conclusión

¡Excelente! Creamos las clases que se encargan de registrar un menú de administración y agregar una página de configuración.

En el próximo artículo de la serie, seguiremos construyendo nuestra página de configuración y registrando sus secciones, campos y elementos.

Haga clic aquí para leer la Parte 6 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