Filtrar entradas de WordPress con Ajax, sin plugins

Chano Vera

Desarrollador
12 diciembre, 2023

Crear un filtro en WordPress sin recargar la página mediante Ajax no es algo difícil de hacer. Muchos desarrolladores suelen instalar rápidamente otro plugin como FacetWP o YITH para realizar este tipo de acciones. Pero en realidad, es bastante sencillo, ya que WordPress cuenta con funcionalidades Ajax por defecto. Desde mi perspectiva como desarrollador front-end, la parte más complicada es crear la WP_Query en si misma si estamos ampliando este método para filtros combinados en WooCommerce.

¡Pronto se presentará un filtro Ajax más avanzado para WooCommerce! Comencemos primero con lo básico. Un ejemplo en vivo de filtros Ajax en WordPress se puede encontrar en el siguiente proyecto.

Configuración de WordPress para entradas y tipos de entrada personalizados

Filtrar entradas, páginas o tipos de entrada personalizados con ajax funciona de la misma manera. Supongo que tienes un conocimiento básico de JavaScript y PHP para la sintaxis, así como de WordPress para saber cómo hacer una consulta. Puedes filtrar entradas y tipos de entrada por categoría, etiquetas y taxonomías personalizadas.

En este ejemplo, te mostraré cómo podemos filtrar nuestro tipo de entrada personalizado ‘proyectos’ por categoría, basándonos en el slug de la categoría. Hay muchas formas de realizar la consulta de las entradas. Daré un breve resumen de todos los argumentos posibles para que puedas ver cuáles funcionan mejor en tu proyecto.

Argumentos Disponibles para WP_Query

Parámetros de Categorías para WP_Query

  • cat: use category ID – int
  • category_name: use category slug – string
  • category__and: use category IDs – array
  • category__in: use category IDs – array
  • category__not_in: use category IDs – array

Parámetros de Etiquetas para WP_Query

  • tag: use tag slug – string
  • tag_id: use tag ID – int
  • tag__and: use tag IDs – array
  • tag__in: use tag IDs – array
  • tag__not__in: use tag IDs – array
  • tag_slug__and: use tag slugs – array
  • tag_slug__in: use tag slugs – array

Configuración de Páginas en WordPress

Para comenzar, primero necesitamos una página que consulte todos nuestros proyectos y los muestre en esa página. También necesitamos realizar consultas sobre nuestras categorías para poder mostrar todas las categorías disponibles. Un ejemplo en vivo de los filtros se puede encontrar en el sitio web de mis clientes; casi cualquier sitio web con un archivo de publicaciones necesita filtros, ¿verdad?

Entonces, ¿cómo empezamos con los filtros de WordPress? Primero, mostremos nuestras categorías disponibles:

<?php
$categories = get_categories();
echo '
<ul class="categories-list">
    <li><a class="category-list_item active" href="#!" data-slug="">' . esc_html__('Todo', 'kenko') . '</a></li>';

foreach($categories as $category) :
    echo '
    <li>
    <a class="category-list_item" href="#!" data-slug="' . $category->slug . '">' .
        $category->name . '
    </a>
    </li>';
endforeach;
echo '
</ul>';     

Ten en cuenta que nuestro primer elemento de la lista no está dentro del bucle. Usaremos esto para eliminar los filtros y mostrar todas las entradas nuevamente. En nuestros enlaces, verás href=”#!”. Necesitamos esto porque quiero usar etiquetas ‘a’ para los filtros, pero las etiquetas de anclaje requieren una etiqueta href para ser un elemento HTML válido.

También ten en cuenta que estoy usando data-slug en los enlaces de mis elementos de lista. Necesitaremos esto más tarde en nuestras funciones para filtrar según el slug de la categoría.

Realizando consultas sobre las entradas de WordPress y los tipos de entrada personalizados

<?php 
$projects = new WP_Query([
'post_type' => 'post',
'posts_per_page' => -1,
'order_by' => 'date',
'order' => 'desc',
]);


if($projects->have_posts()):
echo '
<div class="posts">';
    while($projects->have_posts()) : $projects->the_post();
    get_template_part( 'templates/content', 'archive' ); 
    endwhile;
echo '
</div>';
    wp_reset_postdata();
endif;

Esta es una consulta básica de WP_Query para extraer todas nuestras entradas del tipo de entrada ‘proyectos’. Asegúrate de que nuestros elementos individuales de la lista provengan de un archivo PHP separado. Puedes usar include como hice yo, o utilizar get_template_part(). Lo que prefieras.

El archivo incluido es simplemente un elemento <li> individual, por lo que puedes darle el marcado que desees. No lo incluiré en este tutorial, porque vamos directo al punto: el filtrado.

No dudes en ponerte en contacto conmigo o dejar un comentario en caso de que desees ver una configuración completa, que también explique el archivo project-list-item.php. Para probar tu código, puedes agregar the_title(); en este archivo por ahora.

¡El filtro de WordPress con JavaScript!

Para mí, como desarrollador front-end, ¡obviamente esta es la parte más divertida! Ejecutaremos una función de filtro cuando hagamos clic en un enlace de categoría que hayamos creado anteriormente. Haremos dos cosas:

  1. Hacer que la categoría clicada sea el nuevo filtro activo.
  2. Mostrar solo los proyectos que pertenecen a esa categoría.

FÁCIL. Pero no celebremos demasiado pronto. Esto es solo la parte de JavaScript; más adelante necesitaremos un poco de código PHP personalizado para que todo funcione.

jQuery(document).ready(function($) {
    $('.category-list_item').on('click', function() {
        $('.category-list_item').removeClass('active');
        $(this).addClass('active');

        $.ajax({
            type: 'POST',
            url: '/wp-admin/admin-ajax.php',
            dataType: 'html',
            data: {
                action: 'filter_projects',
                category: $(this).data('slug'),
            },
            success: function(res) {
                $('.posts').html(res);
            }
        });
    });
});

No parece demasiado complicado, ¿verdad? Hacemos clic en nuestros enlaces de categorías y creamos una nueva llamada $.ajax. Enviaremos nuestros datos a una función personalizada llamada ‘filter_projects’. También necesitamos enviar el slug que queremos filtrar, y lo hacemos pasando nuestro filtro a nuestros datos como ‘category’.

La URL de AJAX es la predeterminada en WordPress: /wp-admin/admin-ajax.php

NOTA: Si la URL relativa /wp-admin/admin-ajax.php no funciona para ti y devuelve un error 404 en su lugar, prueba usando la URL absoluta.

El filtro de PHP para WordPress, ¡el horror!

Es solo una broma. Las consultas Ajax en WordPress con PHP suenan como una pesadilla, ¡pero todo es diversión y juegos! Debido a que estamos utilizando la URL predeterminada /wp-admin/admin-ajax.php, podemos escribir esta función en algún lugar del archivo functions.php.

// ajax para el blog
function filter_projects() {
    $catSlug = $_POST['category'];

    $ajaxposts = new WP_Query([
        'post_type' => 'post',
        'posts_per_page' => -1,
        'category_name' => $catSlug,
        'orderby' => 'menu_order', 
        'order' => 'desc',
    ]);
    $response = '';

    if($ajaxposts->have_posts()) {
        while($ajaxposts->have_posts()) : $ajaxposts->the_post();
        $response .= get_template_part( 'templates/content', 'archive' ); 
        endwhile;
    } else {
        $response = 'empty';
    }

    echo $response;
    exit;
}
add_action('wp_ajax_filter_projects', 'filter_projects');
add_action('wp_ajax_nopriv_filter_projects', 'filter_projects');

Al llamar a la función ‘filter_projects’, verificaremos si hemos enviado por POST nuestro ‘category-slug’. Si es una cadena vacía, la consulta no se filtrará y devolverá todas nuestras entradas (lo usamos cuando hacemos clic en el filtro ‘mostrar todos los proyectos’).

A continuación, ejecutamos nuestra WP_Query nuevamente, pero esta vez con un parámetro adicional para el nombre de la categoría. Ironicamente, está utilizando el SLUG y no el nombre de la categoría. Después, simplemente recorremos los resultados de la consulta y agregamos la parte de la plantilla como una cadena a nuestros $results.

No olvides registrar nuestra función, para que WordPress conozca la existencia de nuestra función ‘filter_projects’. De lo contrario, recibiremos un error cuando intentemos hacer nuestra llamada Ajax.

Filtrar tipos de entrada personalizados dinámicos con Ajax

Si estás utilizando varios tipos de entrada que están utilizando las mismas taxonomías, también podemos pasar dinámicamente nuestro post_type a nuestra función de filtro de Ajax. De esta manera, podemos reutilizar nuestra configuración anterior para todos los tipos de entradas en nuestra página.

Para poder hacer esto, tomamos nuestra configuración de lista en PHP del ejemplo anterior y agregamos un nuevo atributo de datos ‘data-type’ para también agregar el tipo de entrada:

Los siguientes fragmentos de código muestran solo las partes que cambiaron de los ejemplos anteriores. Si algo no funciona correctamente, asegúrate de usar el código de arriba para configurar todos los desencadenantes y funciones.

<?php
echo '
<li><a class="category-list_item" href="#!" data-slug="' . $category->slug . '" data-type="post">' . $category->name . '</a></li>';

Nuestro data-type también puede ser dinámico, por supuesto, si estás utilizando el mismo componente de filtro en todas las páginas. Lo he mantenido estático en mi ejemplo por simplicidad. Ahora puedes crear múltiples listas para cada tipo de entrada y cambiar el atributo data-type al tipo de entrada que deseas filtrar.

En nuestra función de JavaScript, también agregamos un parámetro adicional en nuestra función de filtro de Ajax:

$.ajax({
  type: 'POST',
  url: '/wp-admin/admin-ajax.php',
  dataType: 'html',
  data: {
    action: 'posts',
    category: $(this).data('slug'),
    type: $(this).data('type'),
  },
  success: function(res) {
    $('.posts').html(res);
  }
});

Y finalmente, debemos asegurarnos de que nuestro código PHP utilice nuestro argumento dinámico de tipo de entrada para filtrar nuestros tipos de entrada. Actualiza el código de la función PHP en functions.php para que se vea así:

function filter_projects() {
  $postType = $_POST['type'];
  $catSlug = $_POST['category'];

  $ajaxposts = new WP_Query([
    'post_type' => $postType,
    'posts_per_page' => -1,
    'category_name' => $catSlug,
    'orderby' => 'menu_order', 
    'order' => 'desc',
  ]);
  $response = '';

  if($ajaxposts->have_posts()) {
    while($ajaxposts->have_posts()) : $ajaxposts->the_post();
      $response .= get_template_part('templates/content', 'archive');
    endwhile;
  } else {
    $response = 'empty';
  }

  echo $response;
  exit;
}
add_action('wp_ajax_filter_projects', 'filter_projects');
add_action('wp_ajax_nopriv_filter_projects', 'filter_projects');

Filtrar taxonomías personalizadas en WordPress con Ajax

Cuando filtramos las entradas utilizando una taxonomía personalizada en lugar de las etiquetas o categorías regulares, necesitamos modificar nuestra consulta un poco. La forma en que filtramos por taxonomías personalizadas es ligeramente diferente. Necesitamos agregar una tax_query a nuestra consulta normal.

Una tax_query es una matriz que consta de matrices. Esto nos lleva naturalmente a crear un filtro de Ajax que admita múltiples filtros a la vez. Pero empecemos simplemente mostrando todas las entradas para la taxonomía personalizada seleccionada.

Nuestra consulta se verá así:

$projects = new WP_Query([
   'post_type' => 'post',
   'posts_per_page' => -1,
   'tax_query' => [
      [
         'taxonomy' => 'product_cat',
         'field'    => 'term_id',
         'terms'    => $termIds, // example of $termIds = [4,5]
         'operator' => 'IN'
      ],
   ]
]);

Ten en cuenta que nuestra consulta de taxonomía es un array de arrays. $termIds también es un array, con todos los termIds en los que quiero filtrar. Gracias a que la consulta de taxonomía es su propio argumento, podemos combinar filtros de taxonomía con otros filtros de categoría o etiquetas.

Combinando múltiples filtros de WordPress con Ajax

¡Vamos a combinar múltiples filtros al mismo tiempo! La imagen muestra un ejemplo de cómo funciona y cómo se verá. Es algo similar a crear los filtros regulares que hicimos anteriormente, pero puede volverse un poco complicado. Así que voy a separar nuestros filtros de Ajax en un archivo separado llamado filters.js. La parte de PHP seguirá estando en functions.php, pero puedes colocarla en tu propia carpeta de plugins de WordPress si lo prefieres. No voy a cubrir la creación de plugins de WordPress, así que mi solución estará directamente en functions.php 🙂

Para comenzar con un filtro como el de arriba, creé una barra lateral personalizada con todos mis diferentes filtros:

<div class="filter--sidebar">
  <input type="hidden" id="filters-category" />
  <input type="hidden" id="filters-creators" />

  <?php 
    // or use include_once('parts/filter-pricerange.php');
    get_template_part('parts/filter','pricerange');

    get_template_part('parts/filter','categories');
  
    get_template_part('parts/filter','creators');
  ?>
</div>

Ten en cuenta los dos campos ocultos para nuestras categorías y creadores. Los completaremos con JavaScript y luego enviaremos esos valores a nuestra consulta de Ajax. ¡Más sobre esto más adelante!

Puedes agregar cada parte del filtro que desees de esta manera. Dado que cubrí las categorías anteriormente, primero abordaré los creadores. Este es un campo de relación con el plugin ACF.

Nuestro archivo parts/filter-creators.php muestra una lista de todos nuestros creadores/marcas. Esto permite a los usuarios filtrar por la marca que prefieran. Este archivo simplemente recorre todas nuestras marcas y las muestra en una lista:

<?php 
$get_creators = new WP_Query([
  'post_type' => 'creators',
  'posts_per_page' => -1,
]);

if ($get_creators->have_posts()): ?>
  <h4 class="filter-title"><?= _e('Creators','weichie'); ?></h4>
  <ul class="list-filters">
    <?php while ($get_creators->have_posts()):$get_creators->the_post(); ?>
      
      <li>
        <a href="javascript:;" class="filter-link" data-type="creators" data-id="<?= get_the_ID(); ?>">
          <?= esc_html(get_the_title()); ?>
          <span class="remove"><i class="fas fa-times"></i></span>
        </a>
      </li>

    <?php endwhile; ?>
  </ul>
  <?php wp_reset_postdata(); ?>
<?php endif; ?>

Hasta ahora todo bien, ¿verdad? No creo que haya nada especial aquí. Creamos una nueva WP_Query para nuestro tipo de entrada ‘autores’ y recorremos la lista. Necesitarás ocultar el span y eliminar el elemento con CSS. Este lo mostraremos solo cuando alguien haya seleccionado ese filtro.

Filtro de campo ACF para creadores

Quizás también sea interesante mostrarte cómo he vinculado nuestros creadores a los productos. Estoy utilizando el plugin ACF Pro para crear los campos y colocarlos como un campo de relación en nuestros productos. ‘Creadores’ es un tipo de entrada personalizado por sí mismo.

Para cada producto en nuestra tienda, puedo seleccionar una o más marcas que están ‘relacionadas con’ este producto. Sería bueno que los usuarios pudieran filtrar ahora nuestros productos según esta marca.

Siguiente paso: ¡nuestro archivo filter.js! No es obligatorio, pero me gusta mantener las cosas claras. El archivo de filtro contendrá todo lo relacionado con el filtro en JavaScript. Así que si algo falla o necesitamos cambiar o agregar algo, siempre es fácil de encontrar.

¿Recuerdas nuestros campos ocultos en nuestros filtros de PHP? Ahora es el momento de usarlos. Al hacer clic en un creador, pondremos los IDs de nuestras marcas seleccionadas en el campo de entrada.

$('.filter-link').on('click', function(e) {
    e.preventDefault();
    $(this).toggleClass('activeFilter');

    editFilterInputs($('#filters-' + $(this).data('type')), $(this).data('id'));
    filterProducts();
  });

Así que cada vez que hacemos clic en un enlace de filtro, alternamos nuestra clase ‘activeFilter’ (para mostrar/ocultar el icono de eliminación en el filtro), editamos los valores en los campos de entrada ocultos y llamamos a nuestra función filterProducts() para filtrar realmente nuestros productos.

Para colocar nuestros valores en nuestros campos ocultos, debemos asegurarnos de que no estamos agregando un filtro dos veces. Hacer clic en un filtro agregará el ID a nuestro campo de entrada oculto. Hacer clic nuevamente lo eliminará del campo de entrada oculto:

function editFilterInputs(inputField, value) {
  const currentFilters = inputField.val().split(',');
  const newFilter = value.toString();

  if (currentFilters.includes(newFilter)) {
    const i = currentFilters.indexOf(newFilter);
    currentFilters.splice(i, 1);
    inputField.val(currentFilters);
  } else {
    inputField.val(inputField.val() + ',' + newFilter);
  }
}

La función editFilterInputs toma un campo de entrada, el que queremos utilizar para colocar nuestros filtros. Por supuesto, nuestros IDs de creadores deben ir al campo de entrada oculto de creadores. Luego, comprobamos si el ID ya existe en el campo de entrada o no. Si es así, eliminaremos el ID; de lo contrario, lo agregaremos.

Nuestro campo de entrada tiene un valor de cadena. Estoy convirtiendo el valor actual en nuestro campo de entrada en un array para facilitar la comprobación de si el valor ya existe. Si es así, lo eliminamos; de lo contrario, lo agregamos.

Ahora nuestra función principal filterProducts():

function filterProducts() {
  const catIds = $('#filters-category').val().split(',');
  const creatorIds = $('#filters-creators').val().split(',');
  
  $.ajax({
    type: 'POST',
    url: '/wp-admin/admin-ajax.php',
    dataType: 'json',
    data: {
      action: 'filter_products',
      catIds,
      creatorIds,
    },
    success: function(res) {
      $('#result-count').html(res.total);
      $('#main-product-list').html(res.html);
    },
    error: function(err) {
      console.error(err);
    }
  })
}

La parte de JavaScript en realidad se parece mucho a un filtro de una sola categoría/etiqueta/taxonomía. Pero este se usará para todos nuestros filtros en el futuro. En este ejemplo, funcionará tanto para creadores como para categorías. Puedes agregar todos los demás filtros de exactamente la misma manera.

Ten en cuenta que nuevamente estoy convirtiendo los campos de entrada ocultos en un array antes de enviarlos a nuestro script de PHP para realizar la consulta y devolver nuestra respuesta.

Múltiples Filtros Ajax – Parte en PHP

La parte en PHP es un poco diferente de lo habitual. No sé si te estoy explicando todo claramente, pero por favor, ten paciencia conmigo. Estamos casi allí y espero que también puedas hacer que funcione en tu proyecto.

Para comenzar en el archivo PHP, crearemos nuestro filtro filter_products(), recibiremos nuestro valor $_POST de los creadores y categorías, y comenzaremos con nuestra consulta básica (sin filtros) por ahora.

function filter_products() {
	$catIds = $_POST['catIds'];
	$creatorIds = $_POST['creatorIds'];

	$args = [
		'post_type' => 'product',
		'posts_per_page' => -1,
		'post_status'  => 'publish',
		'orderby'        => 'date',
                'order'          => 'desc',
	];
}
add_action('wp_ajax_filter_products', 'filter_products');
add_action('wp_ajax_nopriv_filter_products', 'filter_products');

¿Todo sigue bien hasta aquí? Obtenemos los valores POST y creamos un array $args con nuestros valores básicos de consulta. Estamos utilizando una variable $args separada para facilitar la adición de nuestros múltiples filtros al array de consulta.

A continuación, agregaremos nuestras categorías de productos al array $args. Este utiliza la taxonomía predeterminada de WordPress ‘product_cat’ para filtrar por una categoría de producto: agrega esta parte justo debajo de nuestros $args anteriores, en la misma función filter_projects().

// Product Category
if (count($catIds) > 1) {
	$args['tax_query'][] = [
		'taxonomy'      => 'product_cat',
		'field'		=> 'term_id',
		'terms'         => $catIds,
		'operator'      => 'IN'
	];
}

Tampoco parece demasiado complicado, ¿verdad? Toma nuestros $catIds de nuestro $_POST anterior. Al usar $args[‘tax_query’][] agregamos este array al array $args ya existente de antes. No estamos sobrescribiendo nada, estamos agregando un array ‘tax_query’ a nuestro array anterior.

Agregaremos nuestro filtro de creadores al array de la misma manera. Sin embargo, los Creadores son un Tipo de Entrada Personalizado y no una Taxonomía. Por lo tanto, en lugar de usar tax_query, necesitamos usar meta_query para este campo:

// Product creator - Every creator needs a seperate meta_query
if(count($creatorIds) > 1) {
	$args['meta_query']['relation'] = 'OR';

	foreach($creatorIds as $creator) :
		if ($creator != '') :
			$args['meta_query'][] = [
				'key' => 'product_creator',
				'value' => '"' . $creator . '"',
				'compare' => 'LIKE',
			];
		endif;
	endforeach;
}

Cada creador necesita su propio array [‘meta_query’]. Solo si hay un creador seleccionado, agregaremos este filtro a nuestro $args.

Ahora, nuestro filtro $args tiene la consulta predeterminada para nuestros productos, una tax_query para nuestras categorías y una meta_query para nuestras consultas de relación ACF

Ahora es el momento de la consulta principal. Esta es exactamente la misma que las consultas individuales de los filtros anteriores, pero cambié un poco la respuesta para que recibamos nuestro total de consultas (para ver cuántos productos hay en el filtro) y luego nuestro resultado $html.

Coloca este código en la misma función filter_products(), justo debajo de nuestras funciones $args anteriores para las categorías y los tipos de entrada personalizados:

if ( $ajaxproducts->have_posts() ) {
	ob_start();
	while ( $ajaxproducts->have_posts() ) : $ajaxproducts->the_post();
		$response .= wc_get_template_part( 'content', 'product-dibbz' );
	endwhile;
	$output = ob_get_contents();
	ob_end_clean();
} else {
	echo __( 'No products found' );
}
	
$result = [
	'total' => $counter,
	'html' => $output,
];

echo json_encode($result);
wp_reset_postdata();
exit;

Resumen del Filtro Ajax de WordPress

Esto es todo lo que necesitamos para hacer funcionar los filtros de Ajax en WordPress. Avísame si los filtros de Ajax fueron más fáciles de lo que pensabas o si aún tienes dificultades.

Un filtro de Ajax más avanzado y dinámico para WooCommerce seguirá en algún momento del próximo mes. Todavía tengo algunas dificultades con la filtración de productos de comercio electrónico con ajax para hacerlo súper rápido. El artículo estará listo cuando termine de lidiar con ello. 😉

Edición 2021: Recibo muchas reacciones sobre hacer un tutorial con filtros de Ajax en las entradas predeterminadas de WordPress (la página de índice de WordPress y category.php), pero con Ajax. Quiero escribir un tutorial sobre ese tema también, pero en realidad funciona de la misma manera que este artículo. Excepto que WordPress maneja la primera consulta de entradas por nosotros de forma predeterminada.

Por favor, hazme saber si este tutorial se volvió demasiado avanzado y preferirías una guía más básica utilizando las páginas de archivos de WordPress con filtros de entradas con ajax.

Post de referencia.

Deja un comentario

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