Como crear un sistema de favoritos que funciona para invitados con cookies en WordPress

Contents

Introducción

Este tutorial explica paso a paso cómo implementar un sistema de favoritos en WordPress que funcione para usuarios invitados (no registrados) mediante cookies. La idea es permitir que visitantes marquen/desmarquen entradas como favoritas en su navegador. La información de favoritos se almacena en una cookie JSON en el cliente y, opcionalmente, el servidor mantiene un contador por entrada en post meta para mostrar el número total de favoritos (con las limitaciones que explico más abajo).

Requisitos y consideraciones

  • WordPress (tema hijo o plugin personalizado donde puedas añadir código PHP/JS/CSS).
  • Conocimientos básicos de PHP, JavaScript y cómo encolar scripts en WordPress.
  • Limitaciones: una solución basada en cookies es por navegador/dispositivo. No es fiable para consenso global (varios visitantes pueden inflar contadores) y es vulnerable a manipulaciones del lado cliente. Para mejorar la integridad habría que añadir controles adicionales en servidor o exigir inicio de sesión.

Resumen del flujo

  1. Al renderizar cada post, el servidor lee la cookie (si existe) y marca el botón como activo si el ID del post está en la cookie.
  2. El visitante hace clic en el enlace Favorito (ancla). El JavaScript actualiza la cookie (añade o quita el ID) y cambia el aspecto del enlace.
  3. Opcional: al mismo tiempo se envía una petición AJAX al servidor para incrementar/decrementar un contador en post meta (solo para mostrar un número global, con las advertencias mencionadas).

Paso 1 — Renderizar el enlace de favoritos y leer cookie en PHP

Coloca este código en el template de tu loop (por ejemplo, single.php o content.php) o en una función que añada el enlace en los lugares deseados. El ejemplo lee la cookie wp_favorites (JSON) y marca el enlace con la clase is-active si el post está en la lista.

lt?php
// Dentro del loop
post_id = get_the_ID()

// Leer cookie y decodificarla
fav_cookie = isset(_COOKIE[wp_favorites]) ? wp_unslash( _COOKIE[wp_favorites] ) : 
favs = array()

if ( fav_cookie ) {
    decoded = json_decode( fav_cookie, true )
    if ( is_array( decoded ) ) {
        favs = array_map( intval, decoded )
    }
}

is_fav = in_array( post_id, favs, true )

// Output: usamos un enlace  porque las etiquetas button no están permitidas aquí.
// data-post-id servirá para JS y para la petición AJAX.
?>
 data-post-id= aria-pressed=>
    

Paso 2 — Encolar scripts y pasar variables desde PHP a JS

En functions.php de tu tema hijo o en un plugin, encola el script de JavaScript e inyecta variables necesarias como admin-ajax.php y un nonce para seguridad.

lt?php
function wpfav_enqueue_assets() {
    // Encola script principal (crea un archivo js/fav.js en tu tema o plugin)
    wp_enqueue_script( wpfav-main, get_stylesheet_directory_uri() . /js/fav.js, array( jquery ), 1.0, true )

    // Localizar datos para AJAX y nonce
    wp_localize_script( wpfav-main, WPFavData, array(
        ajax_url   =gt admin_url( admin-ajax.php ),
        nonce      =gt wp_create_nonce( wp_fav_nonce ),
        cookieName =gt wp_favorites,
        cookieDays =gt 365,
    ) )
}
add_action( wp_enqueue_scripts, wpfav_enqueue_assets )

Paso 3 — JavaScript: gestionar cookie y enviar AJAX

Este script maneja los clics en los enlaces, modifica la cookie (JSON con array de IDs) y envía una petición AJAX al backend para actualizar el contador de favoritos. En el ejemplo uso jQuery por compatibilidad con WordPress.

(function(){
    use strict

    // Helpers: leer/escribir cookie con JSON
    function getCookie(name) {
        var match = document.cookie.match(new RegExp((?:^ )   name.replace(/([.?{}()[]/ ^])/g, 1)   =([^])))
        return match ? decodeURIComponent(match[1]) : null
    }

    function setCookie(name, value, days) {
        var expires = 
        if (days) {
            var date = new Date()
            date.setTime(date.getTime()   (days2460601000))
            expires =  expires=   date.toUTCString()
        }
        document.cookie = name   =   encodeURIComponent(value  )   expires    path=/
    }

    function parseFavs(cookieName) {
        var raw = getCookie(cookieName)
        if (!raw) return []
        try {
            var arr = JSON.parse(raw)
            if (Array.isArray(arr)) return arr.map(function(i){ return parseInt(i,10) }).filter(Boolean)
        } catch(e){
            // cookie corrupta: limpiar
            return []
        }
        return []
    }

    function saveFavs(cookieName, arr, days) {
        setCookie(cookieName, JSON.stringify(arr), days)
    }

    // Toggle favorito
    (document).on(click, .fav-toggle, function(e){
        e.preventDefault()
        var el = (this)
        var postId = parseInt(el.data(post-id), 10)
        if (!postId) return

        var cookieName = (typeof WPFavData !== undefined  WPFavData.cookieName) ? WPFavData.cookieName : wp_favorites
        var cookieDays = (typeof WPFavData !== undefined  WPFavData.cookieDays) ? parseInt(WPFavData.cookieDays,10) : 365

        var favs = parseFavs(cookieName)
        var index = favs.indexOf(postId)
        var action = 

        if (index === -1) {
            // añadir
            favs.push(postId)
            action = add
            el.addClass(is-active).attr(aria-pressed,true).text(Favorito)
        } else {
            // quitar
            favs.splice(index, 1)
            action = remove
            el.removeClass(is-active).attr(aria-pressed,false).text(Añadir a favoritos)
        }

        saveFavs(cookieName, favs, cookieDays)

        // Petición AJAX opcional para actualizar contador en el servidor
        if (typeof WPFavData !== undefined  WPFavData.ajax_url) {
            .post(WPFavData.ajax_url, {
                action: wpfav_toggle,
                post_id: postId,
                op: action,
                nonce: WPFavData.nonce
            }, function(response){
                // response handling opcional
                // console.log(response)
            }, json)
        }
    })

})(jQuery)

Paso 4 — Handler AJAX en PHP (invitados y registrados)

Este handler recibe la operación (add/remove) y actualiza un contador simple en post meta llamado _fav_count. Se registran las acciones para usuarios anónimos con wp_ajax_nopriv. Usa un nonce para evitar peticiones CSRF desde otros orígenes.

lt?php
function wpfav_ajax_toggle() {
    // Validar nonce
    check_ajax_referer( wp_fav_nonce, nonce )

    post_id = isset( _POST[post_id] ) ? intval( _POST[post_id] ) : 0
    op = isset( _POST[op] ) ? sanitize_text_field( _POST[op] ) : 

    if ( ! post_id  ! in_array( op, array( add, remove ), true ) ) {
        wp_send_json_error( array( message =gt Parámetros inválidos ), 400 )
    }

    // Obtener contador actual
    count = intval( get_post_meta( post_id, _fav_count, true ) )
    if ( op === add ) {
        count  
    } else {
        count = max( 0, count - 1 )
    }

    update_post_meta( post_id, _fav_count, count )

    wp_send_json_success( array( count =gt count ) )
}
add_action( wp_ajax_wpfav_toggle, wpfav_ajax_toggle )
add_action( wp_ajax_nopriv_wpfav_toggle, wpfav_ajax_toggle )

Paso 5 — Estilos (CSS)

Unos estilos simples para mostrar el estado visual del favorito.

.fav-toggle {
    display: inline-block
    padding: 6px 10px
    background: #f2f2f2
    color: #333
    text-decoration: none
    border-radius: 4px
    font-size: 14px
    margin-right: 6px
}
.fav-toggle.is-active {
    background: #ffefef
    color: #d33
    font-weight: bold
}
.fav-toggle:hover {
    opacity: 0.9
}

Consideraciones de seguridad y limitaciones

  • Una cookie del lado cliente puede ser manipulada por el usuario. No confíes en ella para sistemas críticos.
  • El contador en post meta es opcional y puede ser inflado por usuarios maliciosos (click farms, scripts automatizados). Para mitigar: añadir verificación por IP, rate-limiting o exigir inicio de sesión para afectar el contador real.
  • Las cookies son por navegador/dispositivo. Si el usuario borra cookies o cambia de dispositivo, perderá la lista de favoritos.
  • Si necesitas sincronización entre dispositivos, guarda la lista en user meta para usuarios logueados y haz un merge en el servidor al iniciar sesión (ej. al hacer login, enviar contenido de la cookie al servidor y fusionarlo con user meta).

Mejoras opcionales

  • Persistir favoritos para usuarios registrados: al iniciar sesión, enviar la cookie al servidor y guardar en user meta (merge).
  • Usar signed cookies (HMAC) o tokens para comprobar que la cookie no fue manipulada fácilmente.
  • Implementar rate-limiting por IP y/o checks anti-bot para que el contador en post meta sea más confiable.
  • Ofrecer una UI para ver la lista de favoritos leyendo la cookie y mostrando las entradas (usar WP REST API para obtener títulos/previews).

Checklist de archivos y dónde colocar el código

  • functions.php (tema hijo) o plugin: encolar scripts (Paso 2) y handler AJAX (Paso 4).
  • Template del loop (single.php / content.php): añadir el enlace de favoritos (Paso 1).
  • js/fav.js: código JavaScript (Paso 3).
  • css/fav.css o style.css: estilos (Paso 5).

Notas finales

Esta solución es práctica y rápida de implementar para añadir favoritos a visitantes sin registro mediante cookies. Tiene limitaciones inherentes a su naturaleza cliente-centrica para proyectos donde la integridad del dato sea crítica conviene combinarlo con almacenamiento en servidor para usuarios autenticados y medidas anti-abuso.



Acepto donaciones de BAT's mediante el navegador Brave 🙂



Deja una respuesta

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