Búsqueda avanzada en WordPress

Búsquedas en WordPressExisten multitud de formas de ampliar y personalizar las búsquedas de WordPress. Los plugins nos pueden ayudar, pero muchas veces no cubren nuestras necesidades o tienen demasiadas opciones. En este tutorial veremos cómo ajustar la búsqueda de WordPress exactamente a lo que necesitamos.

Debemos saber que WordPress busca en los campos título y contenido de las entradas, pero las entradas pueden tener más información asociada, como por ejemplo las taxonomías (categorías, tags y personalizadas) y campos personalizados. Veamos algunas variantes de búsquedas incluyendo estos campos.

Podemos ajustar la búsqueda de WordPress exactamente a lo que necesitamos

Buscar en taxonomias y campos personalizados

A veces disponemos de una estructura compleja de taxonomías y de campos personalizados. En este caso nos puede interesar extender la búsqueda a estos campos.

Tan sólo debemos utilizar estos filter hook:

add_filter('posts_join', 'childorbit_search_join');

function childorbit_search_join($join){
	global $wpdb;
	
	if ( is_search() ) {
		$join .= "INNER JOIN {$wpdb->term_relationships} tr ON {$wpdb->posts}.ID = tr.object_id INNER JOIN {$wpdb->term_taxonomy} tt ON tt.term_taxonomy_id=tr.term_taxonomy_id INNER JOIN {$wpdb->terms} t ON t.term_id = tt.term_id INNER JOIN {$wpdb->postmeta} pm ON {$wpdb->posts}.ID = pm.post_id ";
	}
	return $join;
}

add_filter('posts_where','childorbit_search_where');

function childorbit_search_where($where){
	global $wpdb;
	
	if ( is_search() ) {
		$where .= " OR (t.name LIKE '%".get_search_query()."%') ";
		$where .= " OR (pm.meta_value LIKE '%".get_search_query()."%') ";
	}
	return $where;
}

add_filter('posts_groupby', 'childorbit_search_groupby');

function childorbit_search_groupby($groupby){
    global $wpdb;

    // we need to group on post ID
    $groupby_id = "{$wpdb->posts}.ID";
    if(!is_search() || strpos($groupby, $groupby_id) !== false) return $groupby;

    // groupby was empty, use ours
    if(!strlen(trim($groupby))) return $groupby_id;

    // wasn't empty, append ours
    return $groupby.", ".$groupby_id;
}   

Recuerda usar este código en el fichero functions.php de tu plantilla, plugin o child theme.

Con el hook posts_join añadimos a la consulta de la base de datos las tablas de taxonomías ( wp_term_relationships, wp_term_taxonomy, wp_terms ) y la tabla de campos personalizados ( wp_postmeta )

Con el hook posts_where buscamos en los terminos de las taxonomías ( wp_terms -> name ) y en los valores de los campos personalizados ( wp_postmeta -> meta_value )

Y con el hook posts_groupby nos aseguramos de que el resultado salga agrupado por entradas, así evitamos que puedan salir entradas repetidas.

Filtrando por categorías

También nos puede interesar filtrar por categoría. En este caso el formulario podría tener la opción de seleccionar una categoría. Bastaría con usar la función wp_dropdown_categories que nos genera una etiqueta select con todos las categorías.

<form method="get" id="searchform" action="<?php echo esc_url( home_url( '/' ) ); ?>" style="background-color: red;">

    <input type="text" class="field" name="s" id="s" size="10" placeholder="<?php _e( 'search here &hellip;' ); ?>" />

    <?php wp_dropdown_categories(); ?>

    <input type="submit" class="submit" name="submit" id="searchsubmit" value="<?php _e( 'Go' ); ?>" />

</form>   

El formulario queda así

Formulario Busqueda Categorias

Filtrando por taxonomias personalizadas

Si tenemos taxonomías personalizadas (En este tutorial puedes ver cómo crear taxonomías en WordPress ) también las podemos filtrar. Por ejemplo, si queremos una búsqueda donde podamos seleccionar los terminos tutoriales, proyectos o trucos de la taxonomía seccion, podemos crear un formulario como el siguiente:

<form method="get" id="searchform" action="<?php echo esc_url( home_url( '/' ) ); ?>" style="background-color: red;">

    <input type="text" class="field" name="s" id="s" size="10" placeholder="<?php _e( 'search here &hellip;' ); ?>" />

    <select name="seccion">
        <option value="tutoriales">Tutoriales</option>
        <option value="proyectos">Proyectos</option>
        <option value="trucos">Trucos</option>
    </select>

    <input type="submit" class="submit" name="submit" id="searchsubmit" value="<?php _e( 'Go' ); ?>" />

</form>

Debemos tener en cuenta que sólo funciona si usamos los slug de los terminos y de la taxonomía.

Las solución anterior tiene un problema, si añadimos nuevos términos a la taxonomía seccion, tendremos que actualizar el formulario. Para hacerlo correctamente, debemos crear una función que genere de forma dinámica un select con todos los términos de la taxonomía. Podemos llamarla get_terms_dropdown

function get_terms_dropdown( $taxonomy, $args = array() ){
    $myterms = get_terms( $taxonomy, $args );
    $output = '<select name="' . $taxonomy . '">';
    foreach( $myterms as $term ){
        $output .="<option value='".$term->slug."'>".$term->name."</option>";
    }
    $output .="</select>";
    echo $output;
}   

Y en el formulario realizamos la llamada a la función:

<form method="get" id="searchform" action="<?php echo esc_url( home_url( '/' ) ); ?>" style="background-color: red;">

    <input type="text" class="field" name="s" id="s" size="10" placeholder="<?php _e( 'search here &hellip;' ); ?>" />

    <?php get_terms_dropdown( 'seccion' ); ?>

    <input type="submit" class="submit" name="submit" id="searchsubmit" value="<?php _e( 'Go' ); ?>" />

</form>

Más búsquedas avanzadas

Otra búsqueda que nos puede interesar sería utilizar diferentes palabras para cada campo. Por ejemplo queremos un búsqueda en la que el título contenga la palabra receta, el contenido la palabra tomate y además que esté dentro la categoría fácil, pero este tipo de búsqueda lo abordaremos en otra entrada.

Para ampliar información sobre las consutas en WordPress puedes visitar la documentación de WP_Query

Comentarios

  1. Muy bien explicado tu post, yo necesito filtrar las búsquedas por campos personalizado, verás, tengo un formulario en el front-end donde el usuario ingresa datos en el input text, en este caso el campo es ciudad. Y lo que quiero hacer es un form de busqueda con un campo de texto donde el visitante escribirá la ciudad, y ese término se busque dentro la lista de ciudades que el usuario ha llenado.

  2. Me arroja un error al momento de filtrar las taxonomías:
    Warning: Missing argument 2 for get_terms_dropdown(), called in ….
    Por favor, bríndame tu ayuda, muchas gracias.

    1. Es un Warning provocado por que el segundo argumento de get_terms_dropdown(), que no es opcional, ya he cambiado el código, ahora la función es:
      function get_terms_dropdown( $taxonomy, $args = array() ){
      Debería funcionarte aun con el Warning.
      Gracias

  3. Hola!
    Estaba viendo tu código (gracias!), estoy tratando de implementar (la parte que va en functions.php) para un formulario de búsqueda (estoy realizando uno para búsqueda cruzada: taxonomía, custom post type y custom field) pero el único problema es que no me muestra números, me explico: tengo custom fields cuyo valor son números (isbn/libros) y en otros los valores son nombres o títulos, si busco por los valores de nombres o títulos se despliega el resultado, ahí bien, pero si busco por los números no aparece nada.
    No sé como incorporar la parte: ‘la tabla de campos personalizados ( wp_postmeta )’ o ‘los valores de los campos personalizados ( wp_postmeta -> meta_value )’ que mencionas en tu código o como hacer para que estos valores numéricos de los custom field aparezcan. (estoy aprendiendo/comenzando con código)

    Gracias!

    Saludos!

    1. Hola Paulina
      Acabo de revisar el código, y me he colado. Me olvidé añadir en la función childorbit_search_join la tabla postmeta y en la función childorbit_search_where añadir el campo de búsqueda para los valores de los custom field. Ahora ya está actualizado el código. Espero que te sirva.
      Mil gracias!
      Un saludo

  4. Hola Pedro, Gracias! ahora muestra los números (el contenido asociado a números) pero dejó de funcionar el formulario (una parte del formulario tiene un select con opciones – son alrededor de 60 opciones – pero que se llaman dinamicamente, esas son taxonomías, que es lo que dejó de funcionar ahora con el arreglo, lo que es campo de texto funciona bien – muestra los números ahora -) voy a intentar ver en que parte lo puedo modificar para ver que resulte.

    Muchas gracias!

    Saludos!

    🙂

    1. Hola de nuevo. No me he explicado bien en el tutorial… La primera solución busca en los valores de taxonomías y en valores de los campos personalizados, pero cuando lo combinas con la solución de taxonomías personalizadas te va ha generar conflictos.

      En tu caso si no he entendido mal quieres que el campo texto introducido en el formulario se busque en los campos habituales (título y contenido, lo que hace WP por defecto) y extender la búsqueda a los valores de los campos personalizados (ISBN…), y además que estén dentro de un término de una taxonomía dado por un campo de selección. Antes de ayudarte confirmare que es así…

      1. Gracias nuevamente por tu respuesta Pedro! 🙂
        Tampoco me expliqué bien… No estoy ocupando el formulario de tu tutorial, estoy ocupando otro, que utiliza otra manera de llamar las taxonomías y generar el select, pero si estoy ocupando tu código (functions.php) para las llamadas de post-type, custom field, taxonomía.
        Acá la parte del select/taxonomía que ocupo en mi formulario:

        Seleccionar Colecciones
        <?php
        $theterms = get_terms('coleccion', 'orderby=name');
        foreach ($theterms as $term) :
        echo "slug.”‘”.($_GET[‘coleccion’] == $term->slug ? ‘ selected=”selected”‘ : ”).”>”.$term->name.”\n”;
        endforeach;
        ?>

        Entonces, en un campo de texto va la búsqueda título – nombre autor – ISBN – Año de edición (lo normal), el filtro se pone interesante si por ejemplo buscas un título combinando con una de las opciones que ofrece la taxonomía, así obtienes un resultado más refinado. (extender la búsqueda como dices)
        Tïtulo y Autor son custom post type – ISBN/ Año edición Custom Field – Colecciones / Secciones son taxonomías.
        Lo que estaba pensando probar, era tu solución completa (los select incluidos) para ver si funciona de esa manera. Lo que pasa es que son 60 campos u opciones en una taxonomía, demasiado para poner manualmente una a una, como en tu ejemplo:
        Tutoriales
        Proyectos
        Trucos

        A todo esto, son dos formularios, uno ‘simple’ que sólo es un campo de texto y ahí tu código funciona muy bien (buscando título, nombre autor, ISBN, Año), lo predeterminado.
        Y el que está dando el problema es de la ‘búsqueda avanzada’ que es el que tiene el select con las taxonomías, ya que la idea es tener esa opción para un rango de búsqueda más amplio.
        Además armé páginas diferentes para ellos. Tendríamos: search.php (para desplegar los resultados) / searchform.php donde va el formulario ‘normal’ / searchpage.php (pagina por defecto) con su respectivo / buscador.php que tiene el formulario ‘avanzado’ y también es página que abriría eventualmente desde algún botón que diga ‘buscar avanzado’ o similar.

        Espero que me expliqué bien. :S

        Gracias nuevamente!! 🙂

        Saludos.

  5. Me di cuenta que se ‘comió los códigos’, anoto nuevamente, disculpa.

    Parte del select de mi formulario:

    Seleccionar Colecciones
    <?php
    $theterms = get_terms('coleccion', 'orderby=name');
    foreach ($theterms as $term) :
    echo "slug."'".($_GET['coleccion'] == $term->slug ? ' selected="selected"' : '').">".$term->name."\n";
    endforeach;
    ?>

    Y en searchpage.php:

    Espero que se vean. Gracias! Saludos! 🙂

    1. No sale bien el código… Tienes que transformar el código a entidades para que se vea todo bien (es un poco engorroso…).
      Creo que te he entendido. Ten en cuenta que los dos formularios de búsqueda van a pasar por las funciones childorbit_search_join childorbit_search_where . Estas funciones sólo te interesan que se ejecuten con el formulario “simple”. Pon un campo oculto en el formulario simple:
      <input type="hidden" name="simple" value="1">
      y luego en las funciones anteriores cambia la linea:
      if ( is_search() ) {
      por algo así:
      if ( is_search() && isset( $_GET[ 'simple' ] ) ) {
      De esta forma sólo se ejecutan estas funciones en el formulario simple

      1. Gracias Pedro!
        ” los dos formularios de búsqueda van a pasar por las funciones childorbit_search_join childorbit_search_where ” – si, lo tengo en cuenta.
        Voy a probar con la sugerencia del campo oculto (ver de ingresar campo oculto con el nombre de la taxonomía, eso?) con el formulario ‘avanzado’, más esa modificación en functions y te cuento.
        Tanto el formulario simple como el avanzado tienen campos ocultos pero para el post_type ‘edicion’.

        Gracias nuevamente!

  6. Hola Pedro, muchas gracias, me funciono muy bien el primer código, pero hay un pequeño inconveniente, los resultados también estan mostrando los posts guardados en borradores (posts que aun no han sido publicados), hay alguna solución para que no lo muestre?
    Gracias

    1. Hola Sebastian. Me parece raro… Las consultas de WordPress por defecto están buscando post publicados. Mira en la parte de los where:

      $where .=

      Comprueba en tu código que tienes el punto (.) antes del igual (=).

      Si está todo bien y te sigue pasando prueba a poner esta otra linea después

      $where .= " AND {$wpdb->posts}.post_status='publish' ";
      1. Hola gracias por responder. Si están en orden el punto (.) antes del (=), probé también agregando el código que pusiste pero siguen apareciendo los posts de borradores, es algo raro que los muestre, antes no los mostraba, solo después de poner el código de la búsqueda avanzada que pusiste.
        Revisado mi blog veo que esto ocurre solo cuando escribo una palabra en el buscador que coincida con los Tags, categoria o Taxonomia del post de borradores, en cambio si escribo su titulo o palabras del contenido no los muestra.

        1. Sigue mostrando los post de borradores, pero bueno creo que la solución final sera simplemente que no les agregue ninguna etiqueta, categoría o taxonomía mientras estén en borradores, y cuando llegue el momento de publicarlos se los agrego.

          Un saludo.

  7. Hola, tengo la siguiente duda:

    Mi custom filed se llama localizacion y esta compuesto por un estado_id y una ciudad_id. Necesito filtrar tanto por la ciudad como por la provincia de forma separada. Entonces como quedaría la consulta?
    $where .= ” OR (t.name LIKE ‘%”.get_search_query().”%’) “;
    $where .= ” OR (pm.meta_value LIKE

    Digamos que en la bd tengo ‘{estado_id=”1″:ciudad_id=”2″}’

    Saludos y gracias de antemano.

    1. Hola Odilen

      El ejemplo que estás tomando sirve para buscar la palabra que pone el usuario en el formulario en los custom field. Si tus custom field ‘estado_id’ y ‘ciudad_id’ tienen valores numéricos, va a ser difícil que el usuario ponga un número en el campo de búsqueda.

      ¿Que quieres hacer exactamente?

      Un saludo

  8. hola pedro buenas tardes, estoy haciendo algo similar pero para la búsqueda que se hace en la biblioteca de archivos este es mi código:
    (los GETS que aparecen en la consulta los tengo insertados en dropdows para que es usuario pueda insertar los valores y no tenga problemas con la base de datos ya que hay aparecen con numero o caracteres que no quiero que vea el usuario)

    function cf_search_join( $join ) {
    global $wpdb;

    if ( is_search() )
    {
    $join .=” LEFT JOIN “.$wpdb->postmeta. ” ON “. $wpdb->posts . “.ID = ” . $wpdb->postmeta . “.post_id “;
    }
    return $join;
    }
    add_filter(‘posts_join’, ‘cf_search_join’ );

    function cf_search_where( $where ) {
    global $pagenow, $wpdb;

    if ( is_search() )
    {
    $where = preg_replace(
    “/\(\s*”.$wpdb->posts.”.post_title\s+LIKE\s*(\'[^\’]+\’)\s*\)/”,
    “(“.$wpdb->posts.”.post_title LIKE $1) OR (“.$wpdb->postmeta.”.meta_value = ‘”.$_GET[‘campop1’].”‘) AND “.$wpdb->postmeta.”.meta_key = ‘”.$_GET[‘campop’].”‘”, $where );
    }
    return $where;
    }
    add_filter( ‘posts_where’, ‘cf_search_where’ );

    function cf_search_distinct( $where ) {
    global $wpdb;

    if ( is_search() ) {
    return “DISTINCT”;
    }

    return $where;
    }
    add_filter( ‘posts_distinct’, ‘cf_search_distinct’ );

    mi pregunta es ya que estoy haciendo un filtro para que el usuario pueda buscar un archivo que tenga varios campos personalizados y coincidan, en la base de datos los campos personalizados los ingresa en filas separadas, una por campo personalizado como le hago para que me muestre
    solo los que cumplen con estos campos que el usuario declara en la base de datos si me funciona pero en el wordpress no, esta es la consulta que hago en la base de datos y ahí si funciona:

    SELECT * FROM `wpcg_postmeta` where meta_key in (‘key1′,’key2’) and meta_value in (‘value1′,’value2’) group by post_id HAVING count(*) >1

    el problema con el que me encuentro es que no me esta tomando el having count(*) > 1 y creo que el group by tampoco espero me puedas ayudar ya que me he partido la cabeza por todos lados.

  9. Hola, o pude implementar, te agradezco mucho, pero no se como hacer que busque solo en un custom post, por que ahora esta buscando en todo el sitio.

    Ojala puedas ayudarme, saludos!

    1. Si te refieres a filtrar por varios términos de una taxonomía, es más complicado… Primero tienes que crear un campo multiselect, luego en el hook where debes capturar ese campo con $_POST y añadir una linea más en where para filtrar. Cuando saque algo de tiempo saco el código…

Responder a Fliberty Cancelar respuesta

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