WP_Query con "post_title LIKE 'algo%'"
Necesito hacer una consulta WP_Query
con un LIKE
en el post_title
.
Comencé con esta WP_Query
regular:
$wp_query = new WP_Query(
array (
'post_type' => 'wp_exposants',
'posts_per_page' => '1',
'post_status' => 'publish',
'orderby' => 'title',
'order' => 'ASC',
'paged' => $paged
)
);
Pero lo que realmente quiero hacer se ve así en SQL:
$query = "
SELECT *
FROM $wpdb->posts
WHERE $wpdb->posts.post_title LIKE '$param2%'
AND $wpdb->posts.post_type = 'wp_exposants'
ORDER BY $wpdb->posts.post_title
";
$wpdb->get_results($query);
La salida muestra los resultados que espero, pero uso el regular <?php while ( $wp_query->have_posts() ) : $wp_query->the_post(); ?>
para mostrar los resultados.
Y eso no funciona con $wpdb->get_results()
.
¿Cómo puedo lograr lo que describí aquí?
Yo resolvería esto con un filtro en WP_Query
. Uno que detecte una variable de consulta adicional y la use como prefijo del título.
add_filter( 'posts_where', 'wpse18703_posts_where', 10, 2 );
function wpse18703_posts_where( $where, &$wp_query )
{
global $wpdb;
if ( $wpse18703_title = $wp_query->get( 'wpse18703_title' ) ) {
$where .= ' AND ' . $wpdb->posts . '.post_title LIKE \'' . esc_sql( $wpdb->esc_like( $wpse18703_title ) ) . '%\'';
}
return $where;
}
De esta manera, aún puedes llamar a WP_Query
, solo pasas el título como argumento wpse18703_title
(o cambias el nombre a algo más corto).

@kaiser: Ha pasado mucho tiempo, pero creo que esto no era posible con prepare()
. $wpdb->prepare('LIKE "%s%%"', 'banana')
devolvería "LIKE ''banana'%'"
, así que tenemos que construir la consulta nosotros mismos y hacer el escape también.

@JanFabry ¡Feliz de verte de nuevo! :) Pásate por el chat algún momento, ¿eh? StopPress estaría encantado de verte. Sobre ese prepare()
. Sí, es complicado y tuve que intentarlo varias veces antes de solucionarlo. De algo que acabo de hacer: $wpdb->prepare( ' AND {$wpdb->posts}.post_title LIKE %s ', esc_sql( '%'.like_escape( trim( $term ) ).'%' ) )
. Y estoy bastante seguro de que el esc_sql()
es innecesario y solo paranoico.

Parece que no puedes buscar una cadena con '
(apóstrofe) dentro. Supongo que es por el escape ? Todavía no encontré la solución

Simplificado:
function title_filter( $where, &$wp_query )
{
global $wpdb;
// 2. extraer la consulta personalizada aquí:
if ( $search_term = $wp_query->get( 'search_prod_title' ) ) {
$where .= ' AND ' . $wpdb->posts . '.post_title LIKE \'%' . esc_sql( like_escape( $search_term ) ) . '%\'';
}
return $where;
}
$args = array(
'post_type' => 'product',
'posts_per_page' => $page_size,
'paged' => $page,
// 1. definir una variable de consulta personalizada aquí para pasar el término:
'search_prod_title' => $search_term,
'post_status' => 'publish',
'orderby' => 'title',
'order' => 'ASC'
);
add_filter( 'posts_where', 'title_filter', 10, 2 );
$wp_query = new WP_Query($args);
remove_filter( 'posts_where', 'title_filter', 10 );
return $wp_query;

El código creo que se explica por sí mismo, al menos para mí. Gracias por compartir el script completo.

@fdrv Tienes razón pero según la documentación de wp, $wpdb->esc_like todavía necesita esc_sql(). Así que creo que el código correcto sería esc_sql( $wpdb->esc_like( $search_term ) )

Logré que lo anterior funcionara pero por alguna razón cuando ejecuté mi búsqueda estaba devolviendo datos de otros post_types aunque había especificado el tipo de publicación en el array $args. Terminé añadiéndolo a la consulta del título lo cual funcionó para mí $where .= ' OR ' . $wpdb->posts . '.post_title LIKE \'%' . $wpdb->esc_like( $search_term ) . '%\' AND '. $wpdb->posts . '.post_type = \'huguenot-family\'';

Quería actualizar este código en el que trabajaron para WordPress 4.0 y versiones superiores, ya que esc_sql() está obsoleto en 4.0 y versiones posteriores.
function title_filter($where, &$wp_query){
global $wpdb;
if($search_term = $wp_query->get( 'search_prod_title' )){
/*usando esc_like() aquí en lugar de esc_sql()*/
$search_term = $wpdb->esc_like($search_term);
$search_term = ' \'%' . $search_term . '%\'';
$where .= ' AND ' . $wpdb->posts . '.post_title LIKE '.$search_term;
}
return $where;
}
El resto de las cosas sigue igual.
También quiero señalar que puedes usar la variable s dentro de los argumentos de WP_Query para pasar términos de búsqueda, lo que también buscará en el título de la publicación, creo.
Así:
$args = array(
'post_type' => 'post',
's' => $search_term,
'post_status' => 'publish',
'orderby' => 'title',
'order' => 'ASC'
);
$wp_query = new WP_Query($args);

¿Qué es exactamente search_prod_title
? ¿Debería cambiarlo por algo más?

¿Desde cuándo está obsoleto esc_sql
? No lo está. Sin embargo, $wpdb->escape
sí lo está... https://developer.wordpress.org/reference/functions/esc_sql/

Ten en cuenta que el parámetro s también busca en el contenido de la publicación, lo cual puede no ser el objetivo deseado. =)

Con algunas soluciones vulnerables publicadas aquí, presento una versión un poco simplificada y saneada.
Primero, creamos una función para el filtro posts_where
que permite mostrar solo publicaciones que coincidan con condiciones específicas:
function cc_post_title_filter($where, &$wp_query) {
global $wpdb;
if ( $search_term = $wp_query->get( 'cc_search_post_title' ) ) {
$where .= ' AND ' . $wpdb->posts . '.post_title LIKE \'%' . $wpdb->esc_like( $search_term ) . '%\'';
}
return $where;
}
Ahora agregamos cc_search_post_title
a nuestros argumentos de consulta:
$args = array(
'cc_search_post_title' => $search_term, // buscar solo en el título de la publicación
'post_status' => 'publish',
);
Y finalmente envolvemos el filtro alrededor de la consulta:
add_filter( 'posts_where', 'cc_post_title_filter', 10, 2 );
$query = new WP_Query($args);
remove_filter( 'posts_where', 'cc_post_title_filter', 10 );
Usando get_posts()
Ciertas funciones que recuperan publicaciones no ejecutan filtros, por lo que las funciones del filtro posts_where que agregues no modificaran la consulta. Si planeas usar get_posts()
para consultar tus publicaciones, debes establecer suppress_filters
en false en tu arreglo de argumentos:
$args = array(
'cc_search_post_title' => $search_term,
'suppress_filters' => FALSE,
'post_status' => 'publish',
);
Ahora puedes usar get_posts()
:
add_filter( 'posts_where', 'cc_post_title_filter', 10, 2 );
$posts = get_posts($args);
remove_filter( 'posts_where', 'cc_post_title_filter', 10 );
¿Qué pasa con el parámetro s
?
El parámetro s
está disponible:
$args = array(
's' => $search_term,
);
Si bien agregar tu término de búsqueda en el parámetro s
funciona y buscará en el título de la publicación, también buscará en el contenido de la publicación.
¿Qué pasa con el parámetro title
que se agregó en WP 4.4?
Pasar un término de búsqueda al parámetro title
:
$args = array(
'title' => $search_term,
);
Es sensible a mayúsculas y usa LIKE
, no %LIKE%
. Esto significa que buscar hola
no devolverá publicaciones con título Hola Mundo
o Hola
.

Excelente. Estaba buscando 'post_title' como parámetro y, obviamente, no encontré nada.

Estoy recibiendo un error con wp query o get posts: "E_WARNING Error en el archivo »class-wp-hook.php« en la línea 288: Se esperaba que el parámetro 2 para cc_post_title_filter() fuera una referencia, pero se proporcionó un valor"

Basándome en respuestas anteriores, para brindar flexibilidad en la situación donde deseas buscar una publicación que contenga una palabra en un campo meta O en el título de la publicación, ofrezco esa opción mediante el argumento "title_filter_relation". En esta implementación, solo permito entradas "OR" o "AND" con un valor predeterminado de "AND".
function title_filter($where, &$wp_query){
global $wpdb;
if($search_term = $wp_query->get( 'title_filter' )){
$search_term = $wpdb->esc_like($search_term); //en lugar de esc_sql()
$search_term = ' \'%' . $search_term . '%\'';
$title_filter_relation = (strtoupper($wp_query->get( 'title_filter_relation'))=='OR' ? 'OR' : 'AND');
$where .= ' '.$title_filter_relation.' ' . $wpdb->posts . '.post_title LIKE '.$search_term;
}
return $where;
}
Aquí hay un ejemplo del código en acción para un tipo de publicación muy simple "faq" donde la pregunta es el título mismo de la publicación:
add_filter('posts_where','title_filter',10,2);
$s1 = new WP_Query( array(
'post_type' => 'faq',
'posts_per_page' => -1,
'title_filter' => $q,
'title_filter_relation' => 'OR',
'post_status' => 'publish',
'orderby' => 'title',
'order' => 'ASC',
'meta_query' => array(
'relation' => 'OR',
array(
'key' => 'faq_answer',
'value' => $q,
'compare' => 'LIKE'
)
)
));
remove_filter('posts_where','title_filter',10,2);

El enfoque óptimo para realizar una consulta WP_Query con una búsqueda 'LIKE' en el post_title es incluir el argumento de consulta 'search_columns'. Si estás construyendo el WP_Query manualmente, debes especificarlo de la siguiente manera:
'search_columns' => ['post_title']
Puedes encontrar más detalles en el código fuente de WordPress en Trac: https://core.trac.wordpress.org/browser/tags/6.3/src/wp-includes/class-wp-query.php#L1426
Sin embargo, si tu intención es modificar consultas generadas por plugins de terceros o temas que crearon la consulta, puedes lograrlo utilizando el hook de filtro 'post_search_columns'.
La documentación oficial proporciona información detallada sobre cómo usar este hook: https://developer.wordpress.org/reference/hooks/post_search_columns/
