Quando usar WP_query (), query_posts () e pre_get_posts


159

Eu li o @ nacin's Você não conhece a Consulta ontem e foi enviado um pouco como uma toca de coelho. Antes de ontem, eu estava (erroneamente) usando query_posts()para todas as minhas necessidades de consulta. Agora estou um pouco mais sábio sobre o uso WP_Query(), mas ainda tenho algumas áreas cinzentas.

O que acho que tenho certeza:

Se eu estiver criando loops adicionais em qualquer lugar da página - na barra lateral, no rodapé, em qualquer tipo de "postagens relacionadas" etc. - eu quero usar WP_Query(). Eu posso usar isso repetidamente em uma única página sem nenhum dano. (direito?).

O que não sei ao certo

  1. Quando eu uso @ de nacin pre_get_posts vs. WP_Query()? Devo usar pre_get_postspara tudo agora?
  2. Quando desejo modificar o loop em uma página de modelo - digamos que desejo modificar uma página de arquivo de taxonomia - removo a if have_posts : while have_posts : the_postpeça e escrevo a minha WP_Query()? Ou modifico a saída usando pre_get_postsno meu arquivo functions.php?

tl; dr

As regras tl; dr que eu gostaria de extrair disso são:

  1. Nunca use query_postsmais
  2. Ao executar várias consultas em uma única página, use WP_Query()
  3. Ao modificar um loop, faça isso __________________.

Obrigado por qualquer sabedoria

Terry

ps: Eu vi e li: Quando você deve usar WP_Query vs query_posts () vs get_posts ()? O que adiciona outra dimensão - get_posts. Mas não lida com pre_get_postsnada.



@ salcod, agora é diferente, o WordPress evoluiu, adicionei alguns comentários em comparação com a resposta aceita aqui .
Prosti

Respostas:


145

Você está certo em dizer:

Nunca use query_postsmais

pre_get_posts

pre_get_postsé um filtro, para alterar qualquer consulta. Geralmente é usado para alterar apenas a 'consulta principal':

add_action('pre_get_posts','wpse50761_alter_query');
function wpse50761_alter_query($query){

      if( $query->is_main_query() ){
        //Do something to main query
      }
}

(Eu também verificaria se is_admin()retornos falsos - embora isso possa ser redundante.). A consulta principal aparece nos seus modelos como:

if( have_posts() ):
    while( have_posts() ): the_post();
       //The loop
    endwhile;
endif;

Se você sentir a necessidade de editar esse loop, use pre_get_posts. Se você estiver tentado a usar, query_posts()use-o pre_get_posts.

WP_Query

A consulta principal é uma instância importante de a WP_Query object. O WordPress o utiliza para decidir qual modelo usar, por exemplo, e quaisquer argumentos passados ​​para o URL (por exemplo, paginação) são todos canalizados para a instância do WP_Queryobjeto.

Para loops secundários (por exemplo, em barras laterais ou listas de "postagens relacionadas"), você deseja criar sua própria instância separada do WP_Queryobjeto. Por exemplo

$my_secondary_loop = new WP_Query(...);
if( $my_secondary_loop->have_posts() ):
    while( $my_secondary_loop->have_posts() ): $my_secondary_loop->the_post();
       //The secondary loop
    endwhile;
endif;
wp_reset_postdata();

Observe wp_reset_postdata();- isso ocorre porque o loop secundário substituirá a $postvariável global que identifica a 'postagem atual'. Isso redefine essencialmente o $postque estamos fazendo.

get_posts ()

Este é essencialmente um invólucro para uma instância separada de um WP_Queryobjeto. Isso retorna uma matriz de objetos de postagem. Os métodos usados ​​no loop acima não estão mais disponíveis para você. Este não é um 'Loop', simplesmente uma matriz de objeto de postagem.

<ul>
<?php
global $post;
$args = array( 'numberposts' => 5, 'offset'=> 1, 'category' => 1 );
$myposts = get_posts( $args );
foreach( $myposts as $post ) :  setup_postdata($post); ?>
    <li><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></li>
<?php endforeach; wp_reset_postdata(); ?>
</ul>

Em resposta às suas perguntas

  1. Use pre_get_postspara alterar sua consulta principal. Use um WP_Queryobjeto separado (método 2) para loops secundários nas páginas do modelo.
  2. Se você deseja alterar a consulta do loop principal, use pre_get_posts.

Então, existe algum cenário em que alguém iria direto para get_posts () em vez de WP_Query?
precisa saber é o seguinte

@drtanz - sim. Digamos, por exemplo, que você não precise de paginação ou postagens fixas na parte superior - nesses casos, get_posts()é mais eficiente.
Stephen Harris

Mas isso não adicionaria uma consulta extra, na qual poderíamos modificar pre_get_posts para modificar a consulta principal?
Urok93 26/08/12

@drtanz - você não usaria get_posts()para a consulta principal - é para consultas secundárias.
Stephen Harris

1
@StephenHarris Right =) Se você usar next_post () no objeto em vez de usar the_post, não pisará na consulta global e não precisará se lembrar de usar wp_reset_postdata posteriormente.
Corsário

55

Existem dois contextos diferentes para loops:

  • loop principal que ocorre com base na solicitação de URL e é processado antes do carregamento dos modelos
  • loops secundários que ocorrem de qualquer outra maneira, chamados de arquivos de modelo ou de outra forma

O problema query_posts()é que é o loop secundário que tenta ser o principal e falha miseravelmente. Assim esqueça que existe.

Para modificar o loop principal

  • não use query_posts()
  • use pre_get_postsfiltro com $query->is_main_query()cheque
  • use o requestfiltro alternadamente (um pouco áspero demais, acima é melhor)

Para executar um loop secundário

Use new WP_Queryou get_posts()que são praticamente intercambiáveis ​​(este é um invólucro fino para o anterior).

Limpar

Use wp_reset_query()se você usou query_posts()ou mexeu $wp_querydiretamente com o global - então quase nunca precisará.

Use wp_reset_postdata()se você usou the_post()ou setup_postdata()mexeu com o global $poste precisa restaurar o estado inicial de itens pós-relacionados.


3
Rarst significavawp_reset_postdata()
Gregory

23

Existem cenários legítimos para usar query_posts($query), por exemplo:

  1. Você deseja exibir uma lista de postagens ou postagens do tipo postagem personalizada em uma página (usando um modelo de página)

  2. Você deseja que a paginação dessas postagens funcione

Agora, por que você deseja exibi-lo em uma página em vez de usar um modelo de arquivo morto?

  1. É mais intuitivo para um administrador (seu cliente?) - eles podem ver a página nas "Páginas"

  2. É melhor adicioná-lo aos menus (sem a página, eles teriam que adicionar o URL diretamente)

  3. Se você deseja exibir conteúdo adicional (texto, miniatura da postagem ou qualquer meta-conteúdo personalizado) no modelo, é possível obtê-lo facilmente da página (e tudo faz mais sentido para o cliente também). Veja se você usou um modelo de arquivo, precisará codificar o conteúdo adicional ou usar, por exemplo, opções de tema / plug-in (o que o torna menos intuitivo para o cliente)

Aqui está um código de exemplo simplificado (que estaria no seu modelo de página - por exemplo, página-página-de-posts.php):

/**
 * Template Name: Page of Posts
 */

while(have_posts()) { // original main loop - page content
  the_post();
  the_title(); // title of the page
  the_content(); // content of the page
  // etc...
}

// now we display list of our custom-post-type posts

// first obtain pagination parametres
$paged = 1;
if(get_query_var('paged')) {
  $paged = get_query_var('paged');
} elseif(get_query_var('page')) {
  $paged = get_query_var('page');
}

// query posts and replace the main query (page) with this one (so the pagination works)
query_posts(array('post_type' => 'my_post_type', 'post_status' => 'publish', 'paged' => $paged));

// pagination
next_posts_link();
previous_posts_link();

// loop
while(have_posts()) {
  the_post();
  the_title(); // your custom-post-type post's title
  the_content(); // // your custom-post-type post's content
}

wp_reset_query(); // sets the main query (global $wp_query) to the original page query (it obtains it from global $wp_the_query variable) and resets the post data

// So, now we can display the page-related content again (if we wish so)
while(have_posts()) { // original main loop - page content
  the_post();
  the_title(); // title of the page
  the_content(); // content of the page
  // etc...
}

Agora, para ser perfeitamente claro, poderíamos evitar usar query_posts()aqui também e usá-lo WP_Query- assim:

// ...

global $wp_query;
$wp_query = new WP_Query(array('your query vars here')); // sets the new custom query as a main query

// your custom-post-type loop here

wp_reset_query();

// ...

Mas, por que faríamos isso quando temos uma função tão agradável disponível para ela?


1
Brian, obrigado por isso. Eu tenho me esforçado para fazer com que o pre_get_posts funcione em uma página EXATAMENTE no cenário que você descreve: o cliente precisa adicionar campos / conteúdo personalizados ao que de outra forma seria uma página de arquivo, para que uma "página" precise ser criada; o cliente precisa ver algo para adicionar ao menu de navegação, pois a adição de um link personalizado os escapa; etc +1 de mim!
que você precisa

2
Isso também pode ser feito usando "pre_get_posts". Fiz isso para ter uma "página estática" listando meus tipos de postagem personalizados em um pedido personalizado e com um filtro personalizado. Esta página também é paginada. Confira esta questão para ver como ele funciona: wordpress.stackexchange.com/questions/30851/... Assim, em breve, ainda não há um cenário mais legítima para usar query_posts;)
2ndkauboy

1
Como "Deve-se observar que usar isso para substituir a consulta principal em uma página pode aumentar o tempo de carregamento da página, nos piores casos, mais do que duplicar a quantidade de trabalho necessária ou mais. Embora fácil de usar, a função também é suscetível a confusão. e problemas mais tarde ". Source codex.wordpress.org/Function_Reference/query_posts
Claudiu Creanga

Esta resposta está errada. Você pode criar uma "Página" no WP com o mesmo URL que o tipo de postagem personalizada. Por exemplo, se o seu CPT for Bananas, você poderá obter uma página chamada Bananas com a mesma URL. Então você terminaria com siteurl.com/bananas. Contanto que você tenha archive-bananas.php na sua pasta de temas, ele usará o modelo e "substituirá" essa página. Conforme declarado em um dos outros comentários, o uso desse "método" cria duas vezes a carga de trabalho para o WP, portanto, nunca deve ser usado.
híbrido Web Dev

8

Modifico a consulta do WordPress a partir de functions.php:

//unfortunately, "IS_PAGE" condition doesn't work in pre_get_posts (it's WORDPRESS behaviour)
//so you can use `add_filter('posts_where', ....);`    OR   modify  "PAGE" query directly into template file

add_action( 'pre_get_posts', 'myFunction' );
function myFunction($query) {
    if ( ! is_admin() && $query->is_main_query() )  {
        if (  $query->is_category ) {
            $query->set( 'post_type', array( 'post', 'page', 'my_postType' ) );
            add_filter( 'posts_where' , 'MyFilterFunction_1' ) && $GLOBALS['call_ok']=1; 
        }
    }
}
function MyFilterFunction_1($where) {
   return (empty($GLOBALS['call_ok']) || !($GLOBALS['call_ok']=false)  ? $where :  $where . " AND ({$GLOBALS['wpdb']->posts}.post_name NOT LIKE 'Journal%')"; 
}

estaria interessado em ver este exemplo, mas a cláusula where está na meta personalizada.
Andrew Welch

6

Apenas para descrever algumas melhorias na resposta aceita, uma vez que o WordPress evoluiu ao longo do tempo e algumas coisas são diferentes agora (cinco anos depois):

pre_get_postsé um filtro, para alterar qualquer consulta. Geralmente é usado para alterar apenas a 'consulta principal':

Na verdade é um gancho de ação. Não é um filtro e afetará qualquer consulta.

A consulta principal aparece nos seus modelos como:

if( have_posts() ):
    while( have_posts() ): the_post();
       //The loop
    endwhile;
endif;

Na verdade, isso também não é verdade. A função have_postsitera o global $wp_queryobjeto que não está relacionado apenas à consulta principal. global $wp_query;também pode ser alterado com as consultas secundárias.

function have_posts() {
    global $wp_query;
    return $wp_query->have_posts();
}

get_posts ()

Este é essencialmente um wrapper para uma instância separada de um objeto WP_Query.

Hoje em dia WP_Queryé uma classe, então temos uma instância de uma classe.


Para concluir: Na época, o @StephenHarris escreveu provavelmente tudo isso era verdade, mas com o tempo as coisas no WordPress foram alteradas.


Tecnicamente, é tudo filtros sob o capô, as ações são apenas um filtro simples. Mas você está correto aqui, é uma ação que passa um argumento por referência, e é assim que difere de ações mais simples.
Milo

get_postsretorna uma matriz de objetos de postagem, não um WP_Queryobjeto, de modo que ainda está correto. e WP_Querysempre foi uma classe, instância de um class = object.
Milo

Obrigado, @Milo, correto por algum motivo, eu tinha um modelo simplificado demais na minha cabeça.
Prosti
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.