Quando usar WP_query (), query_posts () e pre_get_posts

151

Eu li @ nacin's query_posts() para todas as minhas necessidades de consulta. Agora eu sou um pouco mais sábio sobre o uso de WP_Query() , mas ainda tenho algumas áreas cinzas.

O que eu acho que tenho certeza:

Se eu estiver fazendo adicionais loops em qualquer lugar de uma página - na barra lateral, em um rodapé, em qualquer tipo de "posts relacionados", etc - eu quero estar usando WP_Query() . Eu posso usar isso repetidamente em uma única página sem qualquer dano. (certo?).

O que eu não sei ao certo

  1. Quando uso @ nacin's pre_get_posts vs. WP_Query() ? Devo usar pre_get_posts para tudo agora?
  2. Quando eu quero modificar o loop em uma página de modelo - digamos que eu queira modificar uma página de arquivo de taxonomia - eu removo a parte if have_posts : while have_posts : the_post e escrevo minha própria WP_Query() ? Ou eu modifico a saída usando pre_get_posts no meu arquivo functions.php?

tl; dr

As regras que eu gostaria de tirar disso são:

  1. Nunca use query_posts mais
  2. Ao executar várias consultas em uma única página, use WP_Query()
  3. Ao modificar um loop, faça isso
por saltcod 01.05.2012 / 15:04
fonte

5 respostas

140

Você tem razão em dizer:

  

Nunca use mais query_posts

pre_get_posts

pre_get_posts é um filtro para alterar qualquer consulta. É mais frequentemente 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 que is_admin() retorna falso - 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ê já sentiu a necessidade de editar este loop - use pre_get_posts . Ou seja, se você estiver tentado a usar query_posts() - use pre_get_posts .

WP_Query

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

Para loops secundários (por exemplo, em barras laterais ou listas de "posts relacionados"), você desejará criar sua própria instância separada do objeto WP_Query . 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 variável global $post , que identifica a 'postagem atual'. Isso basicamente redefine isso para o $post em que estamos.

get_posts ()

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

<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_posts para alterar sua consulta principal. Use um objeto WP_Query (método 2) separado para loops secundários nas páginas de modelo.
  2. Se você quiser alterar a consulta do loop principal, use pre_get_posts .
por Stephen Harris 01.05.2012 / 15:27
fonte
53

Existem dois contextos diferentes para loops:

  • O loop main que acontece com base na solicitação de URL e é processado antes que os modelos sejam carregados
  • Loops secundários que acontecem de qualquer outra forma, chamados de arquivos de modelo ou de outra forma

O problema com 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 o filtro pre_get_posts com $query->is_main_query() check
  • alternadamente use request filter (um pouco áspero, então acima é melhor)

Para executar o loop secundário

Use new WP_Query ou get_posts() , que são praticamente intercambiáveis (o último é um wrapper fino para o anterior).

Para limpar

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

Use wp_reset_postdata() se você usou the_post() ou setup_postdata() ou mexeu com% global$post e precisa restaurar o estado inicial das coisas relacionadas à postagem.

    
por Rarst 01.05.2012 / 15:27
fonte
22

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

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

  2. Você quer 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?

  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ê quiser exibir conteúdo adicional (texto, pós-miniatura ou qualquer meta-conteúdo personalizado) no modelo, poderá obtê-lo facilmente a partir da página (e isso também faz mais sentido para o cliente). Veja se você usou um modelo de arquivo, você 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)

Veja um código de exemplo simplificado (que estaria no seu modelo de página, por exemplo, page-page-of-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 ficar bem claro, poderíamos evitar usar query_posts() aqui também e usar 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 pequena função disponível para isso?

    
por Lukas Pecinka 16.09.2012 / 09:34
fonte
8

Modifico a consulta do WordPress em 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%')"; 
}
    
por T.Todua 23.01.2015 / 11:11
fonte
6

Apenas para delinear algumas melhorias para a resposta aceita desde 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. É mais frequentemente 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_posts itera o objeto global $wp_query que não está relacionado somente à consulta principal. global $wp_query; pode ser alterado com as consultas secundárias também.

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.

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

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

    
por prosti 28.12.2016 / 02:26
fonte