Imagine que em um site eu tenho uma taxonomia chamada “Tipo”, com os termos “Praia”, “mirante”, “restaurante” e “pousada”.
Quero fazer com que a busca do wordpress procure também nos nomes desses termos, ou seja, se eu digitar na busca a palavra “Praia” ele deve me retornar também os posts que estão associados a este termo (praia).
A busca padrão do WordPress tenta encontrar ocorrências dos termos pesquisados dentro dos campos título, conteúdo e resumo do post.
Podemos utilizar o mecanismo da busca padrão para encontrar ocorrências em outros campos também, neste caso, o nome dos termos de uma taxonomia associadas aos posts.
Minha primeira tentativa foi utilizar o hook pre_get_posts para modificar os argumentos da query, incluindo um argumento tax_query.
function tax_na_busca($query) { if ( !$query->is_search ){ //caso não seja busca retorna $query sem alteração return $query; } //caso é uma query de busca: //pega todos os termos da taxonomia "nome_taxonomia" que o tenham o nome igual ao valor digitado na busca $term = get_term_by('name', $query->query['s'], 'nome_taxonomia'); //verifica se existe algum termo if ($term) { //pega a id do termo $id_termo = $term->term_id; //cria uma variavel com a tax_query que iremos inserir $tax_query = array( array( 'taxonomy' => 'nome_taxonomia', 'field' => 'id', 'terms' => $id_termo, ) ); // modifica a query, adicionando a tax_query $query->set( 'tax_query', $tax_query ); } } add_action( 'pre_get_posts', 'tax_na_busca' );
Não fiquei satisfeito com o resultado pois se existe o termo buscado na taxonomia a query irá trazer só os posts que tem esse termo de taxonomia ligado a ele.
Por exemplo, ao buscar pela palavra “praia” no site com os seguintes posts publicados:
título: Praia de Boiçucanga
conteudo: Lorem ipsum dolor sit amet
termos: praia, mirante
título: Camburi
conteudo: Praia Lorem ipsum dolor sit
termos: mirante
título: Toque toque pequeno
conteudo: Lorem ipsum dolor sit amet
termos: praia
em uma busca padrão ( sem a modificação ) teríamos como resultado os posts que se enquadram na seguinte regra lógica:
( (Tem praia no nome) OU (tem praia no conteúdo) OU (tem praia no resumo) )
ou seja, só os posts 1 e 2.
Na nossa busca modificada, como o termo “praia” existe na taxonomia “tipo”, temos como resultado os posts que se enquadram na seguinte regra lógica:
( (Tem praia no nome) OU (tem praia no conteúdo) OU (tem praia no resumo) ) E (é associado ao termo “praia” da taxonomia “tipo”)
ou seja, o post 1.
Para resolver o meu problema eu queria uma função que fizesse com que a busca retornasse esses três posts, seguindo a seguinte regra lógica:
( (Tem praia no nome) OU (tem praia no conteúdo) OU (tem praia no resumo) ) OU (é associado ao termo “praia” da taxonomia “tipo”)
Decidi então (após conselho do Matheus) utilizar o hook do wordpress posts_where para adicionar um filtro que altera a clausula WHERE do sql da query.
A minha nova função ficou assim:
function busca_tax( $where,&$wp_query ){ global $wpdb; global $wp_query; //verifica se a query é uma busca e se é main query if ($wp_query->is_main_query() AND $wp_query->is_search) { $busca = $_GET['s']; //Adiciona na clausula WHERE a busca por nome de termos mantendo o conteúdo anterior ( utilizando .= em vez de = ). $where .= " OR $wpdb->posts.ID IN (SELECT tr.object_id FROM $wpdb->term_relationships AS tr INNER JOIN $wpdb->term_taxonomy AS tt ON tr.term_taxonomy_id = tt.term_taxonomy_id WHERE tt.taxonomy = 'tipo' AND tt.term_id IN (SELECT t.term_id FROM $wpdb->terms AS t WHERE name LIKE '%$busca%' ) )"; return $where; } return $where; } add_filter( 'posts_where', 'busca_tax', 10, 2 );
Em uma busca padrão pela palavra “praia” teriamos a seguinte clausula WHERE:
AND ( ( (wp_posts.post_title LIKE '%praia%') OR (wp_posts.post_excerpt LIKE '%praia%') OR (wp_posts.post_content LIKE '%praia%') ) ) AND wp_posts.post_type IN ('post', 'page', 'attachment') AND ( wp_posts.post_status = 'publish' OR wp_posts.post_status = 'private' )
Na nossa nova busca com o filtro ativo temos a seguinte clausula WHERE:
AND ( ( (wp_posts.post_title LIKE '%praia%') OR (wp_posts.post_excerpt LIKE '%praia%') OR (wp_posts.post_content LIKE '%praia%') ) ) AND wp_posts.post_type IN ('post', 'page', 'attachment') AND ( wp_posts.post_status = 'publish' OR wp_posts.post_status = 'private' ) OR wp_posts.ID IN ( SELECT tr.object_id FROM wp_term_relationships AS tr INNER JOIN wp_term_taxonomy AS tt ON tr.term_taxonomy_id = tt.term_taxonomy_id WHERE tt.taxonomy = 'tipo' AND tt.term_id IN ( SELECT t.term_id FROM wp_terms AS t WHERE name LIKE '%praia%' ) )
Você pode observar que até a linha 18 o código é o mesmo, a parte que adicionamos começa com um “OR”, para adicionar os resultados à busca padrão.
Dessa forma se o post contem a palavra “praia” no título mas não está relacionada com o termo “praia” da taxonomia ele também será retornado.
O novo código cria uma relação entre as tabelas wp_term_relashionships e wp_term_taxonomy da base de dados para limitar a busca dos termos que estejam incluídos na taxonomia “tipo”.
Durante esse processo utilizei a ferramenta Query Monitor, muito echo e print_r() para ver os parâmetros das querys e o conteúdo da variável $where.