Superposición del sitio

Usando Ajax en WordPress

Qué mejor forma de inaugurar el blog con una entrada en la que hablaré sobre cómo utilizar Ajax en WordPress. De esta forma podréis haceros una idea sobre cómo van a ser los tutoriales que iré publicando más adelante. Antes que nada, si tenéis cualquier duda o sugerencia no dudéis en comentármelo. 😉

¿Por qué debería usar Ajax en WordPress?

Últimamente se ha convertido en una moda el tema de utilizar interfaces dinámicas sin la necesidad de tener que estar refrescando tu página web. Es cierto que para un desarrollador es muy cómodo recargar la página con nuevos atributos en la URI. Pero hay que tener en cuenta que con esta práctica estás volviendo a cargar todos los elementos de tu página. Cuando en realidad tan solo te interesa actualizar una parte. Otro punto a favor de las consultas Ajax, es que, si te curras un buen diseño consigues una interfaz usable y eso ayuda a que el usuario final entienda mejor tu web.

¿Qué haré en este tutorial?

Te ayudaré a crear un pequeño buscador que encontrará las entradas por categorías donde los resultados serán totalmente dinámicos, sin la necesidad de que la página tenga que refrescarse. Y con ayuda de un shortocode podremos poner el buscador dónde queramos para poder probarlo.

El objetivo del tutorial será hacer este bonito buscador 🙂

Preparando el plugin…

Para poder preparar el shortcode dónde se mostrará nuestro buscador prepararemos la siguiente estructura de archivos vacíos en nuestra carpeta de plugins:

Una vez montada la estructura de fichero, rellenamos cada archivo con el siguiente código…:

fmateo-ajax\init.php

<?php
/**
 * @package FMateo_Ajax
 * @version 1.0
 */
/*
Plugin Name: FMateo Ajax
Plugin URI: https://fmateo.es
Description: Plugin de ejemplo dónde podrás ver Ajax en funcionamiento.
Author: Francisco Mateo
Version: 1.0
Author URI: https://fmateo.es
 */

!defined('FM_AJAX_PATH') && define('FM_AJAX_PATH', plugin_dir_path(__FILE__));
!defined('FM_AJAX_VERSION') && define('FM_AJAX_VERSION', '1.0.0');

if (!function_exists('Init_FM_Ajax_Main')) {

    function Init_FM_Ajax_Main()
    {
        require_once FM_AJAX_PATH . 'includes/class.fm-ajax-main.php';
        return FM_Ajax_Main::instance();
    }
}
Init_FM_Ajax_Main();

fmateo-ajax\includes\class.fm-ajax-main.php

<?php
if (!class_exists('FM_Ajax_Main')) {
    class FM_Ajax_Main
    {
        protected static $_instance = null;
        public function __construct()
        {
            $require = array(
                'common' => array(
                    'includes/class.fm-ajax-shortcodes.php',
                ),
            );
            $this->_require($require);

            add_action('init', array('FM_Ajax_Shortcodes', 'init'));
        }

        protected function _require($require_classes)
        {
            foreach ($require_classes as $section => $classes) {
                foreach ($classes as $class) {
                    if ('common' == $section || ('frontend' == $section && !is_admin() || (wp_doing_ajax())) || ('admin' == $section && is_admin()) && file_exists(FM_AJAX_PATH . $class)) {
                        require_once FM_AJAX_PATH . $class;
                    }
                }
            }
        }
        public static function instance()
        {
            if (is_null(self::$_instance)) {
                self::$_instance = new self();
            }
            return self::$_instance;
        }
    }
}

fmateo-ajax\includes\class.fm-ajax-shortcodes.php

<?php
if (!class_exists('FM_Ajax_Shortcodes')) {

    class FM_Ajax_Shortcodes
    {

        public static function init()
        {
            $shortcodes = array(
                'ajax_search' => __CLASS__ . '::ajax_search',
            );

            foreach ($shortcodes as $shortcode => $function) {
                add_shortcode($shortcode, $function);
            }
        }

        public static function ajax_search()
        {

        }
    }
}

Ya tenemos nuestro shortcode preparado para que pueda mostrar lo que queramos. De hecho puedes probarlo ahora, si vamos a la clase FM_Ajax_Shortcodes y agregamos el siguiente código a la función ajax_search():

fmateo-ajax\includes\class.fm-ajax-shortcodes.php:ajax_search()

public static function ajax_search()
        {
			echo '<h1> Hola que tal!!! Soy colosal!!!</h1>'; 
        }

Cada vez que usemos el shortcode [ajax_search] se mostrará el texto:

Ponemos el shortcode donde queremos. Yo por ejemplo lo he puesto en un widget.
Vemos que funciona bien! 🙂

Preparando la interfaz

Ya tenemos nuestro plugin listo y preparado para empezar a trabajar con él. Ahora tendremos que crear el buscador que se encargará de hacer la búsqueda por categorías. Para ello crearemos las siguientes carpetas y archivos en nuestro plugin…:

Teniendo esto preparado empezamos a escribir el siguiente código en cada fichero:

fmateo-ajax\templates\ajax-search.php

<?php
?>
<div class="ajax_search_panel">
    <div class="ajax_search_box">
        <input type="text" class="ajax_search_input" placeholder="Buscar..."/>
        <button class="button ajax_search_button">Buscar</button>
    </div>
    <div class="ajax_search_results">
        <ul class="ajax_search_result_list">
                        
        </ul>
    </div>
</div>

fmateo-ajax\assets\css\style-shortcode.css

.ajax_search_panel, .ajax_search_box, .ajax_search_results {
    float: left;
    width: 100%;
}
.ajax_search_panel .ajax_search_input{
    float: left; 
    width: 70%;
}
.ajax_search_button {
    background-color: #333;
    float: left;
    width: 30%;
}
.ajax_search_results {
    border: 1px solid #b7b7b7;
    min-height: 36px;
    margin-top: 0.5em;
    padding: 0.5em;
}

fmateo-ajax\assets\js\script-shortcode.js

jQuery(document).ready(function ($) {

    init_ajax_search();

    function init_ajax_search() {
        refresh_to_empty_message();
    }

    function refresh_to_empty_message() {
        $('.ajax_search_result_list').text(fm_ajax_data.messages.empty);
    }

});

Lo que acabamos de hacer ha sido crear la plantilla que mostrará el HTML de nuestro buscador. Hemos creado un pequeño CSS para que no se vea tan feo. Y no puede faltar el javascript que hará la consulta Ajax. Todavía no hemos enlazado esta plantilla al shortcode, para ello tendremos que ir a la función ajax_search() ubicada dentro de nuestra clase class.fm-ajax-shortcodes.php quitamos el echo que habíamos usado antes y agregamos el siguiente código:

fmateo-ajax\includes\class.fm-ajax-shortcodes.php:ajax_search()

public static function ajax_search()
        {
            wp_register_style('fm-ajax-shortcode-style', plugins_url('/..', __FILE__) . '/assets/css/style-shortcode.css');
            wp_enqueue_style('fm-ajax-shortcode-style');

            wp_register_script('fm-ajax-shortcode-script', plugins_url('/..', __FILE__) . '/assets/js/script-shortcode.js', array('jquery'));

            $data_to_js = array(
                'ajax_url' => admin_url('admin-ajax.php'),
                'messages' => array(
                    'empty' => 'Rellena el texto para empezar a buscar...',
                    'searching' => 'Buscando...',
                    'not_found' => 'No se ha encontrado ningún resultado...',
                    'error' => 'Ha habido algún error...',
                ),
            );

            wp_localize_script('fm-ajax-shortcode-script', 'fm_ajax_data', $data_to_js);
            wp_enqueue_script('fm-ajax-shortcode-script');

            load_template(FM_AJAX_PATH . 'templates/ajax-search.php');

        }

Habrás visto que estoy utilizando la variable $data_to_js. El core de WordPress está pensado para que ejecutemos las consultas de Ajax en el admin, también nos permite utilizarlas en frontend si cogemos la ruta de su ubicación con admin_url(). También estoy metiendo un array con un grupo de mensajes messages que utilizará nuestro script más adelante. Para que ajax_url y messages puedan ser utilizados hacemos uso del método wp_localize_script().

Ahora si vamos al lugar donde pusimos nuestro shortcode podemos comprobar que nuestro buscador se carga bien:

El estilo puede variar según el tema que tengas activado.

Ya tenemos nuestro plugin con una base montada y una plantilla que se muestra en el entorno dónde queremos. Pero claro, nuestro buscador no funciona, no hace absolutamente nada. Ahora toca darle caña de la buena y empezar con el tutorial de verdad!!!

Implementando Ajax en WordPress

Para implementar las consultas de Ajax en WordPress debes tener en cuenta tres puntos importantes…:

  1. Tu fichero js debe tener la URL del admin-ajax.php.
  2. Además del valor a buscar, tu consulta deberá tener un valor llamado action. Si no, WordPress no podrá saber que consulta realizar.
  3. Enlazar el action con el hook de Ajax de WordPress.

El primer punto ya lo hicimos en class.fm-ajax-shortcodes.php dentro del método ajax_search(). Obtenemos la URL con la función admin_url(). Y al estar almacenada en la variable $data_to_js se la enviamos a nuestro js con wp_localize_script().

Para el punto dos, tendremos que usar $.ajax y poner el action dentro del parámetro data junto al valor a buscar. Para ello, vamos a nuestro script-shortcode.js e implementamos nuestra consulta Ajax de la siguiente forma:

fmateo-ajax\assets\js\script-shortcode.js

jQuery(document).ready(function ($) {

    init_ajax_search();

    function init_ajax_search() {
        refresh_to_empty_message();
        $('.ajax_search_button').on('click', function () {
            $ajax_search_input = $('.ajax_search_input').val();

            if (0 >= $ajax_search_input.length) {
                refresh_to_empty_message();
            } else {
                start_search($ajax_search_input);
            }
        });
    }

    function start_search(to_search) {

        $.ajax({
            url: fm_ajax_data.ajax_url,
            type: 'POST',
            data: {
                action: 'fm_ajax_search_action',
                to_search: to_search
            },
            beforeSend: function () {
                $('.ajax_search_result_list').text(fm_ajax_data.messages.searching);
            },
            success: function (results) {
                if (0 <= results.length) {

                    $('.ajax_search_result_list').text('');

                    results.forEach(function (result) {
                        $list_item = $('<li>');
                        $a_item = $('<a>');

                        $a_item.attr('href', result.permalink);
                        $a_item.text(result.title);

                        $list_item.append($a_item);
                        $('.ajax_search_result_list').append($list_item);
                    });
                }
            }
        });
    }

    function refresh_to_empty_message() {
        $('.ajax_search_result_list').text(fm_ajax_data.messages.empty);
    }

});

Si nos fijamos dentro del método init_ajax_search() he añadido un evento click al botón de búsqueda $(‘.ajax_search_button’).on(‘click’…. Si hay texto empiezo la búsqueda con start_search($ajax_search_input);. Este método será el responsable de hacer la consulta con la función $.ajax que JQuery tiene preparada para estas ocasiones. $.ajax tiene una serie de parámetros que debemos rellenar:

  • url Será la URL que le pasamos a nuestro js con ayuda de admin_url() y wp_localize_script(). Le pasaremos el valor fm_ajax_data.ajax_url.
  • type Enviaremos la consulta mediante POST.
  • data Aquí pondremos la acción con la que WordPress sabrá que hacer action: ‘fm_ajax_search_action’. Y el valor a buscar to_search: to_search.
  • beforeSend Establecemos lo que queremos hacer antes de realizar la consulta. Por ejemplo poner un texto de «Buscando…» para hacer más sencilla la experiencia con el usuario.
  • success De salir bien la consulta aquí recogeremos los datos que nos devuelve. En nuestro caso la consulta devolverá el título y la url de las entradas encontradas de esa categoría. Por lo que las mostraremos a modo de lista.

Para el tercer punto, WordPress tiene dos hooks que pueden gestionar las consultas Ajax:

add_action(‘wp_ajax_<js_action>’, ‘<name_function>’);

add_action(‘wp_ajax_nopriv_<js_action>’, ‘<name_function>’);

La principal diferencia entre ambos es que wp_ajax_nopriv_<js_action> permite hacer las consultas para usuarios que no estén logueados en nuestra página. Mientras que wp_ajax_<js_action> solo permite hacerlo si estás conectado en el site. Se tratan de hooks dinámicos done en la parte de <js_action> pondremos el action que definimos en nuestro js. En nuestro caso al ser fm_ajax_search_action quedaría de esta forma: wp_ajax_fm_ajax_search_action y wp_ajax_nopriv_fm_ajax_search_action. Teniendo esto en cuenta, iremos a class.fm-ajax-main.php y añadimos los hooks:

fmateo-ajax\includes\class.fm-ajax-main.php

<?php
if (!class_exists('FM_Ajax_Main')) {
    class FM_Ajax_Main
    {
        protected static $_instance = null;
        public function __construct()
        {
            $require = array(
                'common' => array(
                    'includes/class.fm-ajax-shortcodes.php',
                ),
            );
            $this->_require($require);

            add_action('init', array('FM_Ajax_Shortcodes', 'init'));

            add_action('wp_ajax_fm_ajax_search_action', array($this, 'search_field_action'));
            add_action('wp_ajax_nopriv_fm_ajax_search_action', array($this, 'search_field_action'));
        }

        public function search_field_action()
        {
            $results = array();

            if (isset($_POST['to_search']) && !empty($_POST['to_search'])) {
                $terms_args = array(
                    'name' => $_POST['to_search'],
                    'number' => 1
                );
                $term_object = get_terms('category', $terms_args);
                
                if(!empty($term_object)){
                    $args = array(
                        'posts_per_page' => -1,
                        'cat' => $term_object[0]->term_id
                    );
                    $posts = get_posts($args);
    
                    foreach ($posts as $post) {
                        $results[] = array(
                            'title' => get_the_title($post),
                            'permalink' => get_permalink($post),
                        );
                    }
                }                
            }
            wp_send_json($results);
        }

        protected function _require($require_classes)
        {
            foreach ($require_classes as $section => $classes) {
                foreach ($classes as $class) {
                    if ('common' == $section || ('frontend' == $section && !is_admin() || (wp_doing_ajax())) || ('admin' == $section && is_admin()) && file_exists(FM_AJAX_PATH . $class)) {
                        require_once FM_AJAX_PATH . $class;
                    }
                }
            }
        }
        public static function instance()
        {
            if (is_null(self::$_instance)) {
                self::$_instance = new self();
            }
            return self::$_instance;
        }
    }
}

En constructor de nuestra clase principal, hemos añadido los hooks de Ajax. Para definir el método que debe realizar el hook y al estar en una clase. Tenemos que usar el formato array(<class_object>, ‘<class_method>’). En nuestro caso <class_object> será la propia clase donde estamos $this y <class_method> la función que hemos hecho para hacer la consulta search_field_action.

Una vez dentro del método search_field_action() buscamos la categoría gracias get_terms(). En el caso de encontrarla le pedimos a get_posts() que nos devuelva todas las entradas que tengan la categoría. Si hay entradas, las guardamos en el results[]. Una vez recolectado todos los datos se los devolvemos a nuestro js con wp_send_json(). Este método convierte cualquier array de PHP en un JSon listo para ser leído en javascript.

Comprobando que funciona

Si ves que tu buscador no hace nada, te recomiendo que habilites el modo debug de WordPress y mires en la consola del navegador. Es muy probable que te falte alguna parte o hayas puesto algo mal. No te preocupes, tómatelo con calma, es muy normal que tengas algún pequeño fallo. Repasa los tres puntos, probablemente alguno no lo estés haciendo bien. Para comprobar que funciona, simplemente vamos al lugar donde está nuestro buscador y empezamos a escribir categorías.

Si quieres puedes descargarte el plugin desde mi repositorio de GitHub. Y así puedes probar todo lo que quieras sin la necesidad de estar haciéndolo desde cero. 🙂