WP_Query con "post_title LIKE 'something%'"?

44

Necesito hacer un WP_Querycon un LIKEen el post_title.

Empecé con este regular WP_Query:

$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 imprime los resultados que estoy esperando, pero utilizo el habitual <?php while ( $wp_query->have_posts() ) : $wp_query->the_post(); ?>para mostrar los resultados.
Y eso no está funcionando $wpdb->get_results().

¿Cómo puedo lograr lo que describí aquí?

Ludo
fuente

Respuestas:

45

Solucionaría esto con un filtro activado WP_Query. Uno que detecta una variable de consulta adicional y la usa 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 todavía puede llamar WP_Query, simplemente pasa el título como wpse18703_titleargumento (o cambia el nombre a algo más corto).

Jan Fabry
fuente
A este de alguna manera le falta el $wpdb->prepare().
Kaiser
@kaiser: Ha pasado mucho tiempo, pero creo que esto no fue posible con prepare(). $wpdb->prepare('LIKE "%s%%"', 'banana')volvería "LIKE ''banana'%'", por lo que tenemos que construir la consulta nosotros mismos y también hacer el escape.
Jan Fabry
1
@ JanFabry ¡Feliz de verte aaaaaaaaain! :) Pase por el chat en algún momento, ¿hm? StopPress estaría encantado de verte. Sobre eso prepare(). Sí, eso 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 esc_sql()es innecesario y solo paranoico.
kaiser
Parece que no puede buscar una cadena con '(apóstrofe) dentro. Supongo que es por escapar? Todavía no encontré la solución
Vincent Decaux el
19

Simplificado:

function title_filter( $where, &$wp_query )
{
    global $wpdb;
    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,
    '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, 2 );
return $wp_query;
Rao
fuente
13
Incluya una explicación junto con su código.
s_ha_dum
2
Gran simplificación
Timo Huovinen
1
El código es, creo, auto explicado, al menos para mí. Gracias por compartir el guión completo.
Hassan Dad Khan
Use '$ wpdb-> esc_like (' en lugar de 'esc_sql (like_escape ('
fdrv
@fdrv Tiene razón, pero según wp docs $ 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))
Waqas Bukhary
16

Quería actualizar este código en el que trabajaron para WordPress 4.0 y superior, ya que esc_sql () está en desuso en 4.0 superior.

function title_filter($where, &$wp_query){
    global $wpdb;

    if($search_term = $wp_query->get( 'search_prod_title' )){
        /*using the esc_like() in here instead of other 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 es igual.

También quiero señalar que puede usar la variable s dentro de los argumentos WP_Query para pasar los términos de búsqueda, que también buscará el título de la publicación, creo.

Me gusta esto:

$args = array(
    'post_type' => 'post',
    's' => $search_term,
    'post_status' => 'publish',
    'orderby'     => 'title', 
    'order'       => 'ASC'        
);
$wp_query = new WP_Query($args);
Ashan Jay
fuente
¿Qué search_prod_titlees exactamente ? ¿Debo cambiar esto a otra cosa?
Antonios Tsimourtos
¿Desde cuándo es esc_sqldepricated? No es. $wpdb->escapeaunque ... developer.wordpress.org/reference/functions/esc_sql
Jeremy
Tenga en cuenta que el parámetro s también busca el contenido de la publicación, que puede no ser el objetivo deseado. =)
Christine Cooper
10

Con alguna solución vulnerable publicada aquí, vengo con una versión un poco simplificada y desinfectada.

Primero, creamos una función para el posts_wherefiltro que le permite mostrar solo publicaciones que coinciden 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_titlea nuestros argumentos de consulta:

$args = array(
    'cc_search_post_title' => $search_term, // search post title only
    'post_status' => 'publish',
);

Y finalmente envuelva 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 de filtro posts_where que adjunte no modificarán la consulta. Si planea usar get_posts()para consultar sus publicaciones, debe establecerlo suppress_filtersen falso en su matriz 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 sparámetro?

El sparámetro está disponible:

$args = array(
    's' => $search_term,
);

Al agregar su término de búsqueda en el strabajo de parámetros y buscará el título de la publicación, también buscará el contenido de la publicación.

¿Qué pasa con el titleparámetro que se agregó con WP 4.4?

Pasar un término de búsqueda al titleparámetro:

$args = array(
    'title' => $search_term,
);

Mayúsculas y minúsculas y LIKE, no %LIKE%. Esta búsqueda media hellono devolverá la publicación con título Hello Worldo Hello.

Christine Cooper
fuente
Excelente. Estaba buscando 'post_title' como parámetro y, obviamente, no encontré nada.
MastaBaba
7

Sobre la base de otras respuestas que tengo ante mí, para proporcionar flexibilidad en la situación en la que desea buscar una publicación que contenga una palabra en un metacampo O en el título de la publicación, doy esa opción a través del argumento "title_filter_relation". En esta implementación, solo permito las 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); //instead of 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 una publicación muy simple tipo "faq" donde la pregunta es el título de la publicación en sí:

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);
David Choy
fuente
1
Buena idea, agregando "variables de consulta" personalizadas a los argumentos de consulta pasados ​​para WP_Querypoder acceder a ellos dentro del posts_wherefiltro.
Tom Auger