1. Code
  2. WordPress
  3. Plugin Development

Técnicas Ajax Mejoradas para WordPress: Programación Orientada a Objetos

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.
Scroll to top

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:

  1. Crear un directorio assets.
  2. Crear un directorio js ubicado en el directorio assets.
  3. Mover frontend.js al directorio js.
The assets directoryThe assets directoryThe assets directory

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!