WP_Query com "post_title LIKE 'something%'"?

44

Eu preciso fazer um WP_Querycom um LIKEno post_title.

Comecei com 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
    )
); 

Mas o que eu realmente quero fazer se parece com isso no 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);

A saída imprime os resultados que estou esperando, mas uso o regular <?php while ( $wp_query->have_posts() ) : $wp_query->the_post(); ?>para exibir os resultados.
E isso não está funcionando $wpdb->get_results().

Como posso conseguir o que descrevi aqui?

Ludo
fonte

Respostas:

45

Eu resolveria isso com um filtro ativado WP_Query. Um que detecta uma variável de consulta extra e a usa como prefixo do 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;
}

Dessa forma, você ainda pode ligar WP_Query, basta passar o título como wpse18703_titleargumento (ou alterar o nome para algo menor).

Jan Fabry
fonte
Este de alguma forma está faltando o $wpdb->prepare().
Kaiser #
@ Kaiser: Faz muito tempo, mas acho que isso não era possível prepare(). $wpdb->prepare('LIKE "%s%%"', 'banana')retornaria "LIKE ''banana'%'", então temos que construir a consulta nós mesmos e fazer a fuga também.
Jan Fabry
1
@JanFabry Feliz em ver você agaaaaaaaain! :) Entre no chat há algum tempo, não é? O StopPress ficaria feliz em vê-lo. Sobre isso prepare(). Sim, isso é complicado e eu tive que tentar isso várias vezes, antes de contornar isso. De algo que eu só fiz: $wpdb->prepare( ' AND {$wpdb->posts}.post_title LIKE %s ', esc_sql( '%'.like_escape( trim( $term ) ).'%' ) ). E tenho certeza de que isso esc_sql()é desnecessário e apenas paranóico.
Kaiser #
Parece que você não pode pesquisar uma string com '(apóstrofo) dentro. Eu acho que é por causa de escapar? Eu não encontrar a solução ainda
Vincent Decaux
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
fonte
13
Inclua uma explicação junto com o seu código.
s_ha_dum
2
Grande simplificação
Timo Huovinen
1
Acho que o código é auto-explicado, pelo menos para mim. Obrigado por compartilhar o script completo.
Hassan Dad Khan
Use '$ wpdb-> esc_like (' em vez de 'esc_sql (like_escape ('
fdrv
@fdrv Você está certo, mas de acordo com o wp docs, $ wpdb-> esc_like ainda precisa de esc_sql (). Então eu acho que o código correto seria esc_sql ($ wpdb-> esc_like ($ search_term))
Waqas Bukhary
16

Queria atualizar esse código em que vocês trabalharam para o wordpress 4.0 e superior, pois esc_sql () está obsoleto no 4.0 mais alto.

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;
}

O resto das coisas é o mesmo.

Também quero salientar que você pode usar a variável s nos argumentos WP_Query para passar os termos de pesquisa, que também procurarão pelo título da postagem em que acredito.

Como isso:

$args = array(
    'post_type' => 'post',
    's' => $search_term,
    'post_status' => 'publish',
    'orderby'     => 'title', 
    'order'       => 'ASC'        
);
$wp_query = new WP_Query($args);
Ashan Jay
fonte
O que exatamente search_prod_titleé? Devo mudar isso para outra coisa?
Antonios Tsimourtos 24/03
Desde quando é esc_sqlprivado? Não é. $wpdb->escapeé embora ... developer.wordpress.org/reference/functions/esc_sql
Jeremy
Observe que o parâmetro s também pesquisa no conteúdo da postagem, que pode não ser o objetivo desejado. =)
Christine Cooper
10

Com algumas soluções vulneráveis ​​postadas aqui, eu venho com uma versão um pouco simplificada e higienizada.

Primeiro, criamos uma função para o posts_wherefiltro, que permite mostrar apenas postagens que correspondem a condições 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;
}

Agora adicionamos cc_search_post_titleem nossos argumentos de consulta:

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

E, finalmente, envolva o filtro em torno da 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 ()

Certas funções que recuperam postagens não executam filtros, portanto, as funções de filtro posts_where que você anexa não modificam a consulta. Se você planeja usar get_posts()para consultar suas postagens, defina suppress_filterscomo false na sua matriz de argumentos:

$args = array(
    'cc_search_post_title' => $search_term,
    'suppress_filters' => FALSE,
    'post_status' => 'publish',
);

Agora você pode 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 );

E o sparâmetro?

O sparâmetro está disponível:

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

Ao adicionar seu termo de pesquisa ao sparâmetro work e ele pesquisará o título da postagem, ele também pesquisará o conteúdo da publicação.

E o titleparâmetro que foi adicionado com o WP 4.4?

Passando um termo de pesquisa para o titleparâmetro:

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

É sensível a maiúsculas e LIKE, não %LIKE%. Isso significa que a pesquisa por hellonão retornará post com título Hello Worldou Hello.

Christine Cooper
fonte
Excelente. Eu estava procurando por 'post_title' como parâmetro e, obviamente, não encontrei nada.
MastaBaba 13/09
7

Com base em outras respostas diante de mim, para fornecer flexibilidade na situação em que você deseja pesquisar uma postagem que contenha uma palavra em um meta-campo OU no título da publicação, ofereço essa opção através do argumento "title_filter_relation". Nesta implementação, permito apenas entradas "OR" ou "AND" com o padrão "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;
}

Aqui está um exemplo do código em ação para um tipo de postagem muito simples "faq", em que a pergunta é o próprio título da postagem:

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
fonte
1
Boa visão, adicionando "consultas de consulta" personalizadas às args de consulta transmitidas para WP_Querypoder acessá-las no posts_wherefiltro.
Tom Auger