Construye un plugin para contar los clics a los enlaces de tu web

Este pequeño plugin cuenta los clics que se pulsan en una web, almacenando la dirección del enlace y las veces que se ha pulsado.  Se puede mejorar el plugin contando sólo los enlaces que estén marcados por una clase que definas, pero, de momento, es algo muy básico. Prácticamente una prueba de concepto para trabajar con AJAX y la base de datos.

Tienes el código actualizado en https://github.com/kungfupress/kfp-clickcount aunque la versión en que se basa este tutorial está disponible en el tag https://github.com/kungfupress/kfp-clickcount/tree/v1.0.0

Cualquier sugerencia, corrección o colaboración es muy, pero que muy, bien recibida e incluso mencionada en los créditos del plugin 😉

Por cierto, aquí tienes el plugin para contar enlaces en acción en esta misma web.

En este tutorial he seguido los estándares definidos en el WordPress Coding Standards Handbook con la ayuda de PHP Code Sniffer y Visual Studio Code. Quizás el plugin es un pelín más complejo ahora, pero también más seguro. Seguir el estándar es algo valioso por si mismo, pero además, será imprescindible si quieres publicar tú plugin en el repositorio de WordPress.

He intentando utilizar las mejores prácticas que conozco, en estos momentos, para el desarrollo de un plugin, usando programación procedimental. Espero que se note y te insisto en que me corrijas si crees que puedo hacer algo mejor.

¿Cómo surge la idea de desarrollar este plugin?

En uno de los foros de WordPress, un usuario preguntaba si existía algún plugin para contar las veces que se pulsaba un botón determinado en una web. Le contestaban que no conocían de ningún plugin específico que lo hiciera, quizás uno un poco antiguo hacía algo parecido pero era matar moscas a cañonazos. Pensé que no sería algo muy difícil de implementar usando AJAX, así que me puse manos a la obra.

Este plugin sólo hace una cosa: contar cada vez que alguien pulsa un enlace (cualquier enlace) de tu web y almacenarlo en una tabla con un contador. No querría que el plugin se complicará mucho más. Quizás agregar la opción de modificar algún parámetro para que sólo cuente algunos enlaces específicos, algo que seguro que podrías implementar fácilmente modificando un poco el código javascript actual.

Diseñando el plugin

La primera decisión importante era usar una tabla específica del plugin o un tipo de post personalizado (CPT). Aquí he optado por la primera solución pero no descarto migrar a la segunda en versiones futuras. 

Así que la parte de almacenaje consta de una tabla que almacena el enlace, un contador de clics, la fecha del primer clic y la fecha del último clic sobre ese enlace.

Como siempre te digo, si quieres hacer el tutorial a medida que lo lees, te recomiendo ir tecleando el código en tu editor, en lugar de usar el cortar y pegar. Es algo más lento pero te ayudará a interiorizar y entender mejor cada instrucción.

Comienza a crear el plugin

Crea la cabecera del plugin, de momento con unos mínimos, comprueba que la constante ABSPATH está definida para evitar que alguien intente acceder a este script sin pasar por WordPress y luego define tu propia constante para tener la ruta absoluta del plugin e irla usando para la carga de otros ficheros, como el require_once que carga el script de creación de tablas en estas primeras líneas.

<?php
/**
 * Plugin Name:   KFP Click Count
 * Plugin Author: KungFuPress
 * Description:   Cuenta los clics en enlaces o botones de tu web
 * Plugin URI:    https://github.com/kungfupress/kfp-clickcount
 * Version:       0.1.3
 *
 * @package KFP ClickCount
 */

defined( 'ABSPATH' ) || die();
define( 'KFP_CLICKCOUNT_DIR', plugin_dir_path( __FILE__ ) );
define( 'KFP_CLICKCOUNT_VERSION', '0.1.3' );

// Crea la tabla para los enlaces al activar el plugin.
require_once KFP_CLICKCOUNT_DIR . 'include/create-table.php';

Crea la tabla para almacenar los datos

Este es el fichero include/create-table.php que llama a register_activation_hook() para lanzar la función kfp_clickcount_create_table()encargada de lanzar la consulta que crea la tabla del plugin.

Utiliza la variable global $wpdb para coger el prefijo para tablas usado en la instalación actual de WordPress y la función dbDelta() para lanzar la consulta. Para ejecutar esta función necesitas incluir el fichero donde se encuentra definida porque WordPress no lo carga siempre. Aunque sólo hay una consulta prefiero definir la variable $sql como array por si más adelante necesitara crear otra tabla o ejecutar otras sentencias SQL.

<?php
/**
* File: kfp-clickcount/include/create-table.php
*
* @package KFP ClickCount
*/

defined( 'ABSPATH' ) || die();

register_activation_hook( KFP_CLICKCOUNT_PLUGIN_FILE, 'kfp_clickcount_create_table' );
/**
* Crea las tablas necesarias durante la activación del plugin
*
* @return void
*/

function kfp_clickcount_create_table() {
    global $wpdb;
    $sql = array();
    $table = $wpdb->prefix . 'kfp_clickcount';
    $charset_collate = $wpdb->get_charset_collate();

    // Consulta para crear las tablas
    // Mas adelante utiliza dbDelta, si la tabla ya existe no la crea sino que la
    // modifica con los posibles cambios y sin pérdida de datos.
    $sql[] = "CREATE TABLE $table (
    id mediumint(9) UNSIGNED NOT NULL AUTO_INCREMENT,
    link varchar(250) NOT NULL UNIQUE,
    clicks int(11) UNSIGNED,
    date_first_click datetime,
    date_last_click datetime,
    PRIMARY KEY (id)
    ) $charset_collate";

    include_once ABSPATH . 'wp-admin/includes/upgrade.php';
    dbDelta( $sql );
}

Con esto puedes activar el plugin y comprobar si se crea la tabla correctamente. Recuerda tener activado el modo «debug» en tu sitio de desarrollo para que te advierta de cualquier error que se produzca.

Cargando el fichero JavaScript

Ahora toca preparar el código JavaScript que capturará la pulsación de enlaces en nuestra web y llamará al código PHP para registrar el evento.

Abre de nuevo el fichero principal del plugin y añade lo siguiente para cargar el script PHP que a su vez cargará el fichero JavaScript.

require_once KFP_CLICKCOUNT_DIR . 'include/enqueue-scripts.php';

Crea el fichero include/enqueue-scripts.php y teclea el código para cargar correctamente el fichero JavaScript con los parámetros que va a necesitar (la URL a la que el script llamará y un nonce como medida de seguridad). Parece un poco complejo para cargar un simple fichero pero me gusta la manera que tiene WordPress de pasar información entre PHP y JavaScript  , usando wp_localize_script() y la posibilidad que ofrece de cargar el script en el momento adecuado.

<?php
/**
 * File: kfp-clickcount/include/enqueue-scripts.php
 *
 * @package kfp_clickcount
 */

// Agrega el código JavaScript que estará vigilando nuestra web.
add_action( 'wp_enqueue_scripts', 'kfp_clickcount_enqueue_scripts' );
/**
 * Agrega el fichero JavaScript a la cola
 *
 * @return void
 */
function kfp_clickcount_enqueue_scripts() {
	wp_enqueue_script(
		'kfp-clickcount',
		plugins_url( 'js/clickcount.js', KFP_CLICKCOUNT_PLUGIN_FILE ),
		array( 'jquery' ),
		KFP_CLICKCOUNT_VERSION,
		true
	);
	wp_localize_script(
		'kfp-clickcount',
		'AjaxParams',
		array(
			'adminAjaxUrl' => admin_url( 'admin-ajax.php' ),
			'nonce'        => wp_create_nonce( 'clickcount-nonce' ),
		)
	);
}

Comienza la magia

El fichero JavaScript será el encargado de capturar todos los clics que se produzcan en el fronted de la web, observa que no captura los clics de enlaces del escritorio (backend o admin) porque tendrías que haber usado también el hook admin_enqueue_scripts además (o en lugar) del hook wp_enqueue_scripts utilizado en el código anterior (línea 8).

Fíjate también que estás usando jQuery, por lo que en la función wp_enqueue_script() pasaste «jquery» en el array de dependencias del script. En el artículo Diferencia entre wp_register_script y wp_enqueue_script de este blog hablo un poco más de cómo funciona todo esto.

Observa que el nombre del hook wp_enqueue_scripts va en plural, pero en las funciones script va siempre en singular. Para acordarte piensa que el hook es para todos los scripts pero las funciones son para un sólo script.

/** js/clickcount.js */

jQuery(document).ready(function ($) {
    $('body').on('click', 'a', function (event) {
        event.preventDefault();
        var link = $(this).attr('href');
        $.post(AjaxParams.adminAjaxUrl,
            {
                action: 'kfp-clickcount-link',
                link: link,
                nonce: AjaxParams.nonce
            },
            function (response) {
                document.location.href = response;
            });
        return false;
    });
});

Atento ahora porque este script lo que hace es:

  • Captura el evento click en cualquier enlace (a) de la página
  • Bloquea el salto con event.preventDefault()
  • Captura el enlace en la variable link
  • Envía esa variable mediante una petición AJAX al fichero del core de WordPress admin-ajax.php que pasas al script de JavaScript cuando utilizas la función wp_localize_script()
  • Envía el nonce que también se definió en wp_localize_script() como medida de seguridad
  • Si todo va bien entonces redirige al usuario al enlacé que pulsó, mediante la variable response que viene de vuelta.
  • Si algo falla también redirige al enlace, pero en este caso utilizando la variable link original. Puedes cambiar este comportamiento para detectar errores más fácilmente, por ejemplo, lanzando un mensaje de error en la consola y no redirigiendo. Depende del uso que le quieras dar a este plugin.

Procesando la petición AJAX

En WordPress las peticiones AJAX se procesan llamando al script del core admin-ajax.php, pero, para que este script sepa qué tiene que hacer cuando le llegue la petición, tienes que definir el parámetro action. El valor de este parámetro tienes que asociarlo a un hook al que bautizarás como 'wp_ajax_{$_REQUEST[‘action’]}' por ejemplo, en este caso 'wp_ajax_kfp_clickount' Con esto podrán realizar peticiones los usuarios logueados, para procesar también los no logeados vas a llamar al hook 'wp_ajax_nopriv_{$_REQUEST[‘action’]}' por ejemplo, en este caso 'wp_ajax_nopriv_kfp_clickount'. En esta versión del plugin he asociado ambos hooks a la misma función: 'kfp_clickcount_procesa_click', pero podrías tener funciones separadas para actuar de distinta manera según el usuario esté o no logeado.

Vas a crear el fichero procesa_click.php que contendrá la llamada a los hooks y la función que se carga, pero antes recuerda llamar a este fichero desde el archivo principal del plugin (que da mucho coraje cuando se te olvida hacerlo y luego no entiendes porqué no se procesan los clics, hablo por experiencia propia).

En el fichero principal del plugin agrega la línea:

require_once KFP_CLICKCOUNT_DIR . 'include/procesa-click.php';

Y ahora crea el fichero include/procesa-click.php:

<?php
/**
 * File: kfp-clickcount/include/procesa-click.php
 * Añade un action hook para capturar las llamadas AJAX que utilicen este hook.
 *
 * @package kfp_clickcount
 */

defined( 'ABSPATH' ) || die();
// Para usuarios autenticados.
add_action( 'wp_ajax_kfp-clickcount-link', 'kfp_clickcount_procesa_click' );
// Para usuarios NO autenticados.
add_action( 'wp_ajax_nopriv_kfp-clickcount-link', 'kfp_clickcount_procesa_click' );
/**
 * Busca si el link está dado de alta en la tabla kfp_click_count
 * Crea un nuevo registro con contador = 1 o incrementa el contador 
 * si el registro ya existía.
 *
 * @return void
 */
function kfp_clickcount_procesa_click() {
	global $wpdb;
	$table = $wpdb->prefix . 'kfp_clickcount';
	if ( ! isset( $_POST['link'] ) ) {
		die();
	}
	$link = esc_url_raw( wp_unslash( $_POST['link'] ) );
	// Muy importantes los acentos graves en el nombre de la tabla !
	// Igual de importante que no poner comillas a los nombres de campo !
	$row = $wpdb->get_row(
		$wpdb->prepare(
			"SELECT id, clicks FROM `{$wpdb->prefix}kfp_clickcount` 
				WHERE link = %s",
			array( $link )
		)
	);
	// Si el link está en la tabla incrementa contador, si no, agrégalo.
	if ( $row ) {
		$id     = $row->id;
		$clicks = $row->clicks + 1;
		$data   = array(
			'clicks'          => $clicks,
			'date_last_click' => date( 'Y-m-d H:i:s' ),
		); // actualiza esto.
		$where  = array( 'id' => $id ); // cuando se cumpla esto.
		$wpdb->update( $table, $data, $where );
	} else {
		$wpdb->insert(
			$table,
			array(
				'link'             => $link,
				'clicks'           => 1,
				'date_first_click' => date( 'Y-m-d H:i:s' ),
				'date_last_click'  => date( 'Y-m-d H:i:s' ),
			)
		);
	}
	echo esc_url_raw( $link ); // devuelve el link al script AJAX
	wp_die(); // imprescindible en AJAX.
}

Creo que en los comentarios está todo explicado, en esencia lo que hace esta función es atender la petición de admin-ajax.php a partir del action correspondiente, comprueba si en enlace (link) ya está dado de alta en la tabla para incrementar el contador y si no crea una nueva fila con el contador a 1. Por último devuelve el link.

Aquí podrías aprovechar para modificar de alguna manera el link, agregando un parámetro adicional, o reenviar a otro enlace si el contador ha superado un número máximo. Habría que retocar el código claro.

Mostrando los datos capturados en el escritorio

Tabla con los enlaces y los contadores en el panel de administración del plugin

Por último vas a mostrar los datos recogidos en la tabla desde un panel de administración. Crea el fichero include/admin-click-list.php y teclea el código necesario para crear el menú de administración y la tabla con los datos.

<?php
/**
 * File: kfp-clickcount/include/admin-click-list.php
 *
 * @package kfp_clickcount
 */

defined( 'ABSPATH' ) || die();

add_action( 'admin_menu', 'kfp_clickcount_menu' );
/**
 * Agrega el menú del plugin al formulario de WordPress
 *
 * @return void
 */
function kfp_clickcount_menu() {
	add_menu_page(
		'Click Count',
		'Click Count',
		'manage_options',
		'kfp_clickcount_menu',
		'kfp_clickcount_admin',
		'dashicons-feedback',
		75
	);
}

/**
 * Pinta la tabla con los enlaces registrados y sus contadores
 *
 * @return void
 */
function kfp_clickcount_admin() {
	global $wpdb;
	$clickcounts = $wpdb->get_results(
		"SELECT * FROM {$wpdb->prefix}kfp_clickcount"
	); // db call ok; no-cache ok.
	echo '<div class="wrap"><h1>Lista de Enlaces</h1>';
	echo '<table class="wp-list-table widefat fixed striped">';
	echo '<thead><tr><th>Enlaces</th><th>Clics</th>
		<th>Primer Click</th><th>Último Click</th>';
	echo '</tr></thead>';
	echo '<tbody id="the-list">';
	foreach ( $clickcounts as $clickcount ) {
		echo '<tr>';
		echo '<td><a href="' . esc_url_raw( $clickcount->link ) . '">'
			. esc_textarea( $clickcount->link ) . '</a></td>';
		echo '<td>' . (int) $clickcount->clicks . '</td>';
		echo '<td>' . esc_textarea( $clickcount->date_first_click ) . '</td>';
		echo '<td>' . esc_textarea( $clickcount->date_last_click ) . '</td>';
		echo '</tr>';
	}
	echo '</tbody></table></div>';
}

No olvides tampoco llamar a este fichero desde el fichero principal del plugin:

require_once KFP_CLICKCOUNT_DIR . 'include/admin-click-list.php';

Muestra los datos en el frontend

Si quieres mostrar los datos públicamente lo más sencillo será crear un shortcode que podrás colocar en cualquier página o entrada de tu web.

El código para construir la tabla es muy parecido al que has usado para mostrar los datos en el escritorio, pero, ten cuidado que no te pase lo que a mi, en un shortcode no debes usar echo, print(), ni nada que imprima en pantalla, la función que implementa el shortcode debe retornar una cadena de texto, en tu caso un fragmento de HTML.

<?php
/**
 * File: kfp-clickcount/include/public-click-list.php
 *
 * @package kfp_clickcount
 */

defined( 'ABSPATH' ) || die();

add_shortcode( 'kfp-clickcount-public-list', 'kfp_clickcount_public_list' );
/**
 * Muestra la lista de enlaces con los contadores en el fronted
 *
 * @return string
 */
function kfp_clickcount_public_list() {
	global $wpdb;
	$html = '';
	$clickcounts = $wpdb->get_results(
		"SELECT * FROM {$wpdb->prefix}kfp_clickcount"
	);

	$html .= '<div class="wrap"><h1>Lista de Enlaces</h1>';
	$html .= '<table class="wp-list-table widefat fixed striped">';
	$html .= '<thead><tr><th>Enlaces</th><th>Clics</th>';
	$html .= '<th>Primer Clic</th><th>Último Clic</th>';
	$html .= '</tr></thead>';
	$html .= '<tbody id="the-list">';

	foreach ( $clickcounts as $clickcount ) {
		$html .= '<tr>';
		$html .= '<td><a href="' . esc_url_raw( $clickcount->link ) . '">';
		$html .= esc_textarea( $clickcount->link ) . '</a></td>';
		$html .= '<td>' . (int) $clickcount->clicks . '</td>';
		$html .= '<td>' . esc_textarea( $clickcount->date_first_click ) . '</td>';
		$html .= '<td>' . esc_textarea( $clickcount->date_last_click ) . '</td>';
		$html .= '</tr>';
	}
	$html .= '</tbody></table></div>';

	return $html;
}

Si quieres ver este shortcode en acción puedes visitar la página Informe ClickCount en este mismo blog.

Y esto es todo. Espero ir mejorando el plugin y subiré las sucesivas versiones al repositorio de GitHub. La versión que has visto aquí es la etiquetada como v1.0.0, la más actual siempre estará en la rama master. Igual me planteo subirlo más adelante al repositorio oficial de WordPress si veo que puede ser de utilidad para alguien más.

Espero que hayas disfrutando leyendo, o mejor aún, implementando este tutorial. Déjame algún comentario si tienes alguna duda, se te ocurre alguna mejora para el plugin o quieres plantear que desarrolle algún otro.

Recuerda que tus comentarios son para este blog como las flores de loto en un estanque, flotan sobre las aguas del conocimiento y lo embellecen. 😉

Referencias y recursos recomendados

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *