Técnicas Ajax Mejoradas para WordPress: Programación Orientada a Objetos
Spanish (Español) translation by Nadia Castelli (you can also view the original English article)
En el post anterior de esta serie, tratamos el tema de trabajar con Ajax en WordPress. Finalmente, la meta es mejorar una serie previa que salió en este sitio hace unos años atrñas.
Reiterando, no es que las técnicas enseñadas en la serie original fueran incorrectas, pero el software cambia a lo largo del tiempo, por ende siempre es bueno revisar conceptos cubiertos años atrás e intentar actualizarlos a algo más nuevo y más resiliente para nuestros esfuerzos aplicados al desarrollo.
Repasando el post anterior, le echamos un vistazo al siguiente comentario perteneciente a la serie original:
Vamos a dar una idea muy breve de lo que es Ajax, cómo funciona, cómo configurarlo, y un entendimiento de los ganchos (hooks) que provee WordPress. Además construiremos un pequeño proyecto para poner en práctica la teoría. Avanzaremos a lo largo del código fuente, y nos aseguraremos de que esté disponible en GitHub.
En ese mismo post, vimos algunas maneras avanzadas de integrar la API Ajax para WordPress en nuestros proyectos, empleando programación por procedimientos. En este post, tomaremos el código escrito en la primera parte de esta serie y lo refactorizaremos de forma tal que aplique un enfoque orientado a objetos.
Finalmente, la meta no es explicar por qué un paradigma debería elegirse por sobre el otro; en cambio, es mostrar cómo puedes lograr la misma funcionalidad independientemente del enfoque que elijas al construir tus plugins.
Planificando el Plugin
Antes de empezar a refactorear el código, necesitamos considerar cómo vamos a organizar los archivos. Después de todo, parte del proceso de comenzar un nuevo proyecto—o incluso retomar uno viejo—es planificar cómo se realizará el trabajo.
Para este plugin en particular, necesitaremos lo siguiente:
- un archivo inicial responsable de inicializar la clase principal y el plugin
- una clase responsable de cargar dependencias tales como JavaScript
- una clase que sirva como la clase principal del plugin
Como puedes ver, no necesitamos hacerle demasiado al plugin. También reorganizaremos algunos de los archivos para lograr una estructura de directorio consistente, y nos aseguraremos de documentar apropiadamente todo el código para que siga los Estándares de Código de WordPress.
Dicho esto, comencemos.
Organizando los Archivos
Antes de escribir código, hagamos lo siguiente:
- Crear un directorio
assets. - Crear un directorio
jsubicado en el directorioassets. - Mover
frontend.jsal directoriojs.



El motivo de esto es que estamos migrando a un estilo de programación orientado a objetos. Parte de esto implica organizar nuestros archivos para que sigan convenciones frecuentemente consideradas paquetes.
En nuestro caso, el directorio assets incluye todo lo necesario para hacer correr el programa. Para algunos plugins, esto podría ser JavaScript, CSS, imágenes, fuentes, entre otros. En este caso, tenemos un único archivo JavaScript.
El Cargador de Dependencias
Ahora, necesitamos introducir una clase responsable de cargar las dependencias de nuestro proyecto. Para este plugin en particular, la única dependencia que tenemos es el archivo JavaScript que ubicamos en el directorio assets.
Parte de la programación orientada a objetos implica asegurarse de que cada clase cumple un propósito específico. En este caso, la clase que estamos por introducir será responsable de cargar el archivo JavaScript mediante la API de WordPress.
Comencemos creando la estructura básica de la clase:
1 |
<?php
|
2 |
|
3 |
/**
|
4 |
* Loads and enqueues dependencies for the plugin.
|
5 |
*
|
6 |
* @since 1.0.0
|
7 |
*
|
8 |
* @package WPA/includes
|
9 |
*/
|
10 |
class Dependency_Loader { |
11 |
|
12 |
}
|
Luego, añadiremos un método que será responsable de encolar el archivo JavaScript según la API de WordPress.
1 |
<?php
|
2 |
/**
|
3 |
* Loads and registers dependencies.
|
4 |
*
|
5 |
* @since 1.0.0
|
6 |
*
|
7 |
* @package WPA
|
8 |
* @author Tom McFarlin
|
9 |
* @license https://www.gnu.org/licenses/gpl-2.0.txt
|
10 |
* @link https://tommcfarlin.com/
|
11 |
*/
|
12 |
|
13 |
/**
|
14 |
* Loads and enqueues dependencies for the plugin.
|
15 |
*
|
16 |
* @package WPA
|
17 |
* @subpackage WPA/includes
|
18 |
* @since 1.0.0
|
19 |
* @author Tom McFarlin
|
20 |
* @license http://www.gnu.org/licenses/gpl-2.0.txt
|
21 |
* @link https://tommcfarlin.com/
|
22 |
*/
|
23 |
class Dependency_Loader { |
24 |
|
25 |
/**
|
26 |
* Initializes the plugin by enqueuing the necessary dependencies.
|
27 |
*
|
28 |
* @since 1.0.0
|
29 |
*/
|
30 |
public function initialize() { |
31 |
$this->enqueue_scripts(); |
32 |
}
|
33 |
|
34 |
/**
|
35 |
* Enqueues the front-end scripts for getting the current user's information
|
36 |
* via Ajax.
|
37 |
*
|
38 |
* @access private
|
39 |
*
|
40 |
* @since 1.0.0
|
41 |
*/
|
42 |
private function enqueue_scripts() { |
43 |
|
44 |
wp_enqueue_script( |
45 |
'ajax-script', |
46 |
plugin_dir_url( dirname( __FILE__ ) ) . 'assets/js/frontend.js', |
47 |
array( 'jquery' ) |
48 |
);
|
49 |
|
50 |
wp_localize_script( |
51 |
'ajax-script', |
52 |
'sa_demo', |
53 |
array( 'ajax_url' => admin_url( 'admin-ajax.php' ) ) |
54 |
);
|
55 |
|
56 |
}
|
57 |
}
|
Hecho esto, necesitamos tomar las funciones responsables de enviar las peticiones Ajax y devolver las respuestas, y agregarlas a la clase. Como estarán dentro del contexto de una clase, necesitamos agregar una nueva función que las registrará con WordPress.
Crearemos una función llamada setup_ajax_handlers. Luce así:
1 |
<?php |
2 |
|
3 |
/** |
4 |
* Registers the callback functions responsible for providing a response |
5 |
* to Ajax requests setup throughout the rest of the plugin. |
6 |
* |
7 |
* @since 1.0.0 |
8 |
*/ |
9 |
public function setup_ajax_handlers() {
|
10 |
|
11 |
add_action( |
12 |
'wp_ajax_get_current_user_info', |
13 |
array( $this, 'get_current_user_info' ) |
14 |
); |
15 |
|
16 |
add_action( |
17 |
'wp_ajax_nopriv_get_current_user_info', |
18 |
array( $this, 'get_current_user_info' ) |
19 |
); |
20 |
|
21 |
} |
Luego, debemos mover las funciones dentro de esta clase. Notarás que las funciones que originalmente llevaban prefijo _sa ya no están nombradas así. Dado que están dentro del contexto de la clase, podemos reemplazar dicho prefijo y también el guión bajo, por la palabra reservada private.
1 |
<?php
|
2 |
|
3 |
public function get_current_user_info() { |
4 |
|
5 |
$user_id = get_current_user_id(); |
6 |
|
7 |
if ( $this->user_is_logged_in( $user_id ) && $this->user_exists( $user_id ) ) { |
8 |
|
9 |
wp_send_json_success( |
10 |
wp_json_encode( get_user_by( 'id', $user_id ) ) |
11 |
);
|
12 |
|
13 |
}
|
14 |
|
15 |
}
|
16 |
|
17 |
private function user_is_logged_in( $user_id ) { |
18 |
|
19 |
$is_logged_in = true; |
20 |
|
21 |
if ( 0 === $user_id ) { |
22 |
|
23 |
wp_send_json_error( |
24 |
new WP_Error( '-2', 'The visitor is not currently logged into the site.' ) |
25 |
);
|
26 |
|
27 |
$is_logged_in = false; |
28 |
|
29 |
}
|
30 |
|
31 |
return $is_logged_in; |
32 |
|
33 |
}
|
34 |
|
35 |
private function user_exists( $user_id ) { |
36 |
|
37 |
$user_exists = true; |
38 |
|
39 |
if ( false === get_user_by( 'id', $user_id ) ) { |
40 |
|
41 |
wp_send_json_error( |
42 |
new WP_Error( '-1', 'No user was found with the specified ID [' . $user_id . ']' ) |
43 |
);
|
44 |
|
45 |
$user_exists = false; |
46 |
|
47 |
}
|
48 |
|
49 |
return $user_exists; |
50 |
|
51 |
}
|
Luego, guardaremos este archivo en un directorio includes ubicado en el directorio raíz del plugin. El directorio includes frecuentemente contiene código utilizado a lo largo de todo un proyecto. Podríamos decir más acerca de este directorio particular, pero sería contenido para un post más extenso.
La versión final de esta clase debería verse así:
1 |
<?php
|
2 |
/**
|
3 |
* Loads and registers dependencies.
|
4 |
*
|
5 |
* @since 1.0.0
|
6 |
*
|
7 |
* @package WPA
|
8 |
* @author Tom McFarlin
|
9 |
* @license http://www.gnu.org/licenses/gpl-2.0.txt
|
10 |
* @link https://tommcfarlin.com/
|
11 |
*/
|
12 |
|
13 |
/**
|
14 |
* Loads and enqueues dependencies for the plugin.
|
15 |
*
|
16 |
* @package WPA
|
17 |
* @subpackage WPA/includes
|
18 |
* @since 1.0.0
|
19 |
* @author Tom McFarlin
|
20 |
* @license http://www.gnu.org/licenses/gpl-2.0.txt
|
21 |
* @link https://tommcfarlin.com/
|
22 |
*/
|
23 |
class Dependency_Loader { |
24 |
|
25 |
/**
|
26 |
* Initializes the plugin by enqueuing the necessary dependencies.
|
27 |
*
|
28 |
* @since 1.0.0
|
29 |
*/
|
30 |
public function initialize() { |
31 |
$this->enqueue_scripts(); |
32 |
}
|
33 |
|
34 |
/**
|
35 |
* Enqueues the front-end scripts for getting the current user's information
|
36 |
* via Ajax.
|
37 |
*
|
38 |
* @access private
|
39 |
*
|
40 |
* @since 1.0.0
|
41 |
*/
|
42 |
private function enqueue_scripts() { |
43 |
|
44 |
wp_enqueue_script( |
45 |
'ajax-script', |
46 |
plugin_dir_url( dirname( __FILE__ ) ) . 'assets/js/frontend.js', |
47 |
array( 'jquery' ) |
48 |
);
|
49 |
|
50 |
wp_localize_script( |
51 |
'ajax-script', |
52 |
'sa_demo', |
53 |
array( 'ajax_url' => admin_url( 'admin-ajax.php' ) ) |
54 |
);
|
55 |
|
56 |
}
|
57 |
|
58 |
/**
|
59 |
* Registers the callback functions responsible for providing a response
|
60 |
* to Ajax requests setup throughout the rest of the plugin.
|
61 |
*
|
62 |
* @since 1.0.0
|
63 |
*/
|
64 |
public function setup_ajax_handlers() { |
65 |
|
66 |
add_action( |
67 |
'wp_ajax_get_current_user_info', |
68 |
array( $this, 'get_current_user_info' ) |
69 |
);
|
70 |
|
71 |
add_action( |
72 |
'wp_ajax_nopriv_get_current_user_info', |
73 |
array( $this, 'get_current_user_info' ) |
74 |
);
|
75 |
|
76 |
}
|
77 |
|
78 |
/**
|
79 |
* Retrieves information about the user who is currently logged into the site.
|
80 |
*
|
81 |
* This function is intended to be called via the client-side of the public-facing
|
82 |
* side of the site.
|
83 |
*
|
84 |
* @since 1.0.0
|
85 |
*/
|
86 |
public function get_current_user_info() { |
87 |
|
88 |
$user_id = get_current_user_id(); |
89 |
|
90 |
if ( $this->user_is_logged_in( $user_id ) && $this->user_exists( $user_id ) ) { |
91 |
|
92 |
wp_send_json_success( |
93 |
wp_json_encode( get_user_by( 'id', $user_id ) ) |
94 |
);
|
95 |
|
96 |
}
|
97 |
|
98 |
}
|
99 |
|
100 |
/**
|
101 |
* Determines if a user is logged into the site using the specified user ID. If not,
|
102 |
* then the following error code and message will be returned to the client:
|
103 |
*
|
104 |
* -2: The visitor is not currently logged into the site.
|
105 |
*
|
106 |
* @access private
|
107 |
* @since 1.0.0
|
108 |
*
|
109 |
* @param int $user_id The current user's ID.
|
110 |
*
|
111 |
* @return bool $is_logged_in Whether or not the current user is logged in.
|
112 |
*/
|
113 |
private function user_is_logged_in( $user_id ) { |
114 |
|
115 |
$is_logged_in = true; |
116 |
|
117 |
if ( 0 === $user_id ) { |
118 |
|
119 |
wp_send_json_error( |
120 |
new WP_Error( '-2', 'The visitor is not currently logged into the site.' ) |
121 |
);
|
122 |
|
123 |
$is_logged_in = false; |
124 |
|
125 |
}
|
126 |
|
127 |
return $is_logged_in; |
128 |
|
129 |
}
|
130 |
|
131 |
/**
|
132 |
* Determines if a user with the specified ID exists in the WordPress database. If not, then will
|
133 |
* the following error code and message will be returned to the client:
|
134 |
*
|
135 |
* -1: No user was found with the specified ID [ $user_id ].
|
136 |
*
|
137 |
* @access private
|
138 |
* @since 1.0.0
|
139 |
*
|
140 |
* @param int $user_id The current user's ID.
|
141 |
*
|
142 |
* @return bool $user_exists Whether or not the specified user exists.
|
143 |
*/
|
144 |
private function user_exists( $user_id ) { |
145 |
|
146 |
$user_exists = true; |
147 |
|
148 |
if ( false === get_user_by( 'id', $user_id ) ) { |
149 |
|
150 |
wp_send_json_error( |
151 |
new WP_Error( '-1', 'No user was found with the specified ID [' . $user_id . ']' ) |
152 |
);
|
153 |
|
154 |
$user_exists = false; |
155 |
|
156 |
}
|
157 |
|
158 |
return $user_exists; |
159 |
|
160 |
}
|
161 |
}
|
La Clase Principal
Ahora estamos listos para escribir la clase principal para el plugin. Esta clase particular residirá en el directorio raíz del plugin, y su estructura básica se verá así:
1 |
<?php
|
2 |
|
3 |
/**
|
4 |
* Loads and enqueues dependencies for the plugin.
|
5 |
*
|
6 |
* @since 1.0.0
|
7 |
*
|
8 |
* @package WPA
|
9 |
*/
|
10 |
class WP_Simple_Ajax { |
11 |
|
12 |
}
|
Luego, agregaremos un par de propiedades que setearemos al instanciar la clase:
1 |
<?php
|
2 |
|
3 |
class WP_Simple_Ajax { |
4 |
|
5 |
private $version; |
6 |
|
7 |
private $loader; |
8 |
|
9 |
}
|
Hecho esto, crearemos un constructor y una función de inicialización que se usará para poner el plugin en marcha:
1 |
<?php
|
2 |
/**
|
3 |
* The primary class for the plugin
|
4 |
*
|
5 |
* Stores the plugin version, loads and enqueues dependencies
|
6 |
* for the plugin.
|
7 |
*
|
8 |
* @since 1.0.0
|
9 |
*
|
10 |
* @package WPA
|
11 |
* @author Tom McFarlin
|
12 |
* @license http://www.gnu.org/licenses/gpl-2.0.txt
|
13 |
* @link https://tommcfarlin.com/
|
14 |
*/
|
15 |
|
16 |
/**
|
17 |
* Stores the plugin version, loads and enqueues dependencies
|
18 |
* for the plugin.
|
19 |
*
|
20 |
* @package WPA
|
21 |
* @author Tom McFarlin
|
22 |
* @license http://www.gnu.org/licenses/gpl-2.0.txt
|
23 |
* @link https://tommcfarlin.com/
|
24 |
*/
|
25 |
class WP_Simple_Ajax { |
26 |
|
27 |
/**
|
28 |
* Represents the current version of this plugin.
|
29 |
*
|
30 |
* @access private
|
31 |
* @since 1.0.0
|
32 |
* @var string
|
33 |
*/
|
34 |
private $version; |
35 |
|
36 |
/**
|
37 |
* A reference to the Dependency Loader.
|
38 |
*
|
39 |
* @access private
|
40 |
* @since 1.0.0
|
41 |
* @var Dependency_Loader
|
42 |
*/
|
43 |
private $loader; |
44 |
|
45 |
/**
|
46 |
* Initializes the properties of the class.
|
47 |
*
|
48 |
* @access private
|
49 |
* @since 1.0.0
|
50 |
*/
|
51 |
public function __construct() { |
52 |
|
53 |
$this->version = '1.0.0'; |
54 |
$this->loader = new Dependency_Loader(); |
55 |
|
56 |
}
|
57 |
|
58 |
/**
|
59 |
* Initializes this plugin and the dependency loader to include
|
60 |
* the JavaScript necessary for the plugin to function.
|
61 |
*
|
62 |
* @access private
|
63 |
* @since 1.0.0
|
64 |
*/
|
65 |
public function initialize() { |
66 |
|
67 |
$this->loader->initialize(); |
68 |
$this->loader->setup_ajax_handlers(); |
69 |
|
70 |
}
|
71 |
}
|
En el código anterior, el contructor determina las propiedades e instancia las dependencias necesarias para hacer funcionar el plugin.
Cuando se llama al método initialize, el plugin empezará a correr y llamará al método de inicialización de la clase de la dependencia creada antes en este tutorial.
El Iniciador
Lo último que nos queda por hacer es tomar nuestro archivo principal, utilizar la funcionalidad include de PHP, y asegurarnos que esté atento a los archivos PHP necesarios.
1 |
<?php
|
2 |
|
3 |
/**
|
4 |
* Loads and registers dependencies.
|
5 |
*/
|
6 |
include_once( 'includes/class-dependency-loader.php' ); |
7 |
|
8 |
/**
|
9 |
* The primary class for the plugin
|
10 |
*/
|
11 |
include_once( 'class-wp-simple-ajax.php' ); |
Luego de eso, tenemos que definir un método que inicialice el archivo principal del plugin y ponga todo en movimiento.
1 |
<?php
|
2 |
|
3 |
/**
|
4 |
* Instantiates the main class and initializes the plugin.
|
5 |
*/
|
6 |
function wpa_start_plugin() { |
7 |
|
8 |
$plugin = new WP_Simple_Ajax(); |
9 |
$plugin->initialize(); |
10 |
|
11 |
}
|
La versión final de este archivo iniciador debería verse así:
1 |
<?php
|
2 |
/**
|
3 |
* This plugin demonstrates how to use the WordPress Ajax APIs.
|
4 |
*
|
5 |
* @package WPA
|
6 |
*
|
7 |
* @wordpress-plugin
|
8 |
* Plugin Name: Simple Ajax Demo
|
9 |
* Description: A simple demonstration of the WordPress Ajax APIs.
|
10 |
* Version: 1.0.0
|
11 |
* Author: Tom McFarlin
|
12 |
* Author URI: https://tommcfarlin.com/
|
13 |
* License: GPL-2.0+
|
14 |
* License URI: http://www.gnu.org/licenses/gpl-2.0.txt
|
15 |
*/
|
16 |
|
17 |
// If this file is called directly, abort.
|
18 |
if ( ! defined( 'WPINC' ) ) { |
19 |
die; |
20 |
}
|
21 |
|
22 |
/**
|
23 |
* Loads and registers dependencies.
|
24 |
*/
|
25 |
include_once( 'includes/class-dependency-loader.php' ); |
26 |
|
27 |
/**
|
28 |
* The primary class for the plugin
|
29 |
*/
|
30 |
include_once( 'class-wp-simple-ajax.php' ); |
31 |
|
32 |
/**
|
33 |
* Instantiates the main class and initializes the plugin.
|
34 |
*/
|
35 |
function wpa_start_plugin() { |
36 |
|
37 |
$plugin = new WP_Simple_Ajax(); |
38 |
$plugin->initialize(); |
39 |
|
40 |
}
|
41 |
wpa_start_plugin(); |
Primero, el archivo comprueba si está siendo accedido directamente, chequeando si ha sido definida una constante WordPress. Si no, se detiene la ejecución.
Luego, incluye las clases varias que creamos en este tutorial. Finalmente, define una función llamada cuando WordPress carga el plugin que inicializar el plugin y pone todo en funcionamiento.
Conclusión
Esto nos trae al final de esta serie de dos partes. Espero que hayas aprendido no sólo algunas de las mejores prácticas para incorporar Ajax en tus proyectos WordPress, sino también un poco acerca de cómo documentar tanto código por procedimientos como orientado a objetos, así como notar la diferencia en la presentación del código.
En un post futuro, retomaré algunos de los conceptos orientados a objetos aquí presentados, y los cubriré con mucho más detalle. Por ahora, sin embargo, échale un vistazo al plugin utilizando el link a GitHub ubicado en la barra lateral de esta página.
Recuerda, puedes ver todos mis cursos y tutoriales en mi perfil, y puedes seguirme en mi blog y/o Twitter (@tommcfarlin), donde escribo sobre desarrollo de software en el contexto de WordPress.
Como siempre, por favor no dudes en dejar preguntas o comentarios en el espacio debajo de este post, e intentaré responder cada uno de ellos.
¡Sé el primero en conocer las nuevas traducciones–sigue @tutsplus_es en Twitter!



