WP_query (), query_posts () 및 pre_get_posts를 사용하는시기


159

나는 @ nacin 's를 읽었습니다. 당신은 어제 Query를 몰라 약간의 질의 토끼 구멍으로 보내졌습니다. 어제 전에는 query_posts()모든 쿼리 요구 에 (잘못) 사용 하고있었습니다. 이제는 사용 WP_Query()에 대해 조금 더 현명 하지만 여전히 회색 영역이 있습니다.

내가 확실히 알고있는 것 :

사이드 바, 바닥 글, 모든 종류의 "관련 게시물"등 페이지의 어느 곳에서나 추가 루프를 만드는 경우을 사용하고 싶습니다 WP_Query(). 아무 문제없이 단일 페이지에서 반복해서 사용할 수 있습니다. (권리?).

내가 확실하지 않은 것

  1. @ nacin 's pre_get_posts vs. 는 언제 사용 WP_Query()합니까? pre_get_posts지금 모든 것에 사용해야합니까 ?
  2. 템플릿 페이지에서 루프를 수정하고 싶을 때 — 분류 아카이브 페이지를 수정하고 싶을 때 – if have_posts : while have_posts : the_post부품을 제거하고 직접 작성 WP_Query()합니까? 또는 pre_get_postsfunctions.php 파일을 사용하여 출력을 수정 합니까?

tl; dr

내가 이것에서 얻고 싶은 tl; dr 규칙은 다음과 같습니다.

  1. query_posts더 이상 사용 하지 마십시오
  2. 단일 페이지에서 여러 개의 쿼리를 실행할 때 WP_Query()
  3. 루프를 수정할 때 __________________을 수행하십시오.

모든 지혜에 감사드립니다

테리 직물

추신 : 나는 읽고 읽었습니다 : WP_Query 대 query_posts () 대 get_posts ()를 언제 사용해야합니까? 또 다른 차원을 추가합니다 — get_posts. 그러나 전혀 다루지 않습니다 pre_get_posts.



@saltcod, 이제 다릅니다, WordPress 진화, 나는 여기에 허용되는 답변과 비교하여 몇 가지 의견을 추가했습니다 .
prosti

답변:


145

당신은 말할 권리가 있습니다 :

query_posts더 이상 사용 하지 마십시오

pre_get_posts

pre_get_posts변경에 대한 필터이며, 어떤 쿼리. '주요 쿼리'만 변경하는 데 가장 자주 사용됩니다.

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

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

( 중복 일 수도 있지만 falseis_admin()반환 하는지 확인합니다 .). 기본 쿼리는 템플릿에 다음과 같이 나타납니다.

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

이 루프 사용을 편집 할 필요가 있다고 생각되면 pre_get_posts. 당신이 사용하는 유혹을하는 경우 즉 query_posts()- 사용하는 pre_get_posts대신.

WP_Query

기본 쿼리는의 중요한 인스턴스입니다 WP_Query object. 예를 들어 WordPress는이를 사용하여 사용할 템플릿을 결정하고 url (예 : 페이지 매김)에 전달 된 인수는 모두 해당 WP_Query개체 인스턴스로 전달됩니다 .

보조 루프 (예 : 사이드 바 또는 '관련 게시물'목록)의 경우, 별도의 WP_Query객체 인스턴스를 생성해야 합니다. 예 :

$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();

통지 wp_reset_postdata();-이것은 2 차 루프가 $post'현재 게시물'을 식별하는 전역 변수를 무시하기 때문 입니다. 이것은 본질적으로 $post우리가 그것을 다시 설정합니다 .

get_posts ()

이것은 본질적으로 WP_Query객체 의 개별 인스턴스에 대한 래퍼입니다 . 이것은 포스트 객체의 배열을 반환합니다. 위 루프에서 사용 된 방법은 더 이상 사용할 수 없습니다. 이것은 '루프'가 아니며 단순히 포스트 객체의 배열입니다.

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

귀하의 질문에 대한 답변

  1. pre_get_posts기본 쿼리를 변경하는 데 사용하십시오 . WP_Query템플릿 페이지에서 보조 루프에 별도의 객체 (방법 2)를 사용하십시오 .
  2. 메인 루프의 쿼리를 변경하려면을 사용하십시오 pre_get_posts.

WP_Query가 아닌 get_posts ()로 바로가는 시나리오가 있습니까?
urok93

@ drtanz-그렇습니다. 예를 들어 페이지 매김이 필요하지 않거나 맨 위에 고정 된 포스트가 필요하지 않다고 가정하십시오 get_posts(). 이 경우 더 효율적입니다.
Stephen Harris

그러나 pre_get_posts를 수정하여 기본 쿼리를 수정할 수있는 추가 쿼리를 추가하지 않습니까?
urok93

@drtanz- get_posts()기본 쿼리에는 사용하지 않고 보조 쿼리에는 사용하지 않습니다 .
Stephen Harris

1
@StephenHarris Right =) the_post를 사용하는 대신 객체에서 next_post ()를 사용하면 전역 쿼리를 밟을 필요가 없으며 나중에 wp_reset_postdata를 사용할 필요가 없습니다.
Privateer

55

루프에 대한 두 가지 컨텍스트가 있습니다.

  • URL 요청에 따라 발생하고 템플릿이로드되기 전에 처리되는 기본 루프
  • 템플릿 파일 또는 다른 방법으로 호출되는 다른 방식으로 발생하는 보조 루프

문제 query_posts()는 주요 루프를 시도하고 비참하게 실패하는 것이 보조 루프라는 것입니다. 따라서 존재하는 것을 잊어라.

메인 루프를 수정하려면

  • 사용하지 마십시오 query_posts()
  • 점검 pre_get_posts과 함께 필터 사용$query->is_main_query()
  • 교대로 request필터를 사용하십시오 (약간 너무 거칠기 때문에 위의 것이 좋습니다)

보조 루프를 실행하려면

new WP_Query또는 get_posts()거의 교환 가능한 것을 사용하십시오 (후자는 전자를 위해 얇은 포장지입니다).

정리

전역을 직접 wp_reset_query()사용 query_posts()하거나 엉망으로 사용하는 경우 사용 $wp_query하므로 거의 필요하지 않습니다.

global wp_reset_postdata()을 사용 the_post()했거나 setup_postdata()엉망이어서 $post사후 관련 항목의 초기 상태를 복원해야하는 경우에 사용하십시오 .


3
Rarst의 의미wp_reset_postdata()
Gregory

23

예를 들어을 사용하는 합법적 인 시나리오가 있습니다 query_posts($query).

  1. 페이지 템플리트를 사용하여 페이지에 게시물 또는 사용자 정의 게시물 유형 게시물 목록을 표시하려고합니다.

  2. 해당 게시물을 페이지 매김하고 싶습니다.

이제 왜 아카이브 템플릿을 사용하는 대신 페이지에 표시 하시겠습니까?

  1. 관리자에게는 더 직관적입니다 (고객?). '페이지'에서 페이지를 볼 수 있습니다.

  2. 메뉴에 추가하는 것이 좋습니다 (페이지가 없으면 URL을 직접 추가해야 함)

  3. 템플릿에 추가 컨텐츠 (텍스트, 게시물 썸네일 또는 사용자 정의 메타 컨텐츠)를 표시하려는 경우 페이지에서 쉽게 가져올 수 있습니다 (또한 고객에게도 해당됩니다). 보관 템플릿을 사용했는지 확인한 경우 추가 콘텐츠를 하드 코딩하거나 테마 / 플러그인 옵션 (예 : 고객에게 덜 직관적 임)을 사용해야합니다.

다음은 간단한 예제 코드입니다 (예 : 페이지 템플릿 (예 : 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...
}

이제 완벽하게 명확하게하기 위해 query_posts()여기에서도 사용 WP_Query하지 않고 대신 사용할 수 있습니다.

// ...

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();

// ...

그러나 우리가 사용할 수있는 아주 멋진 기능이있을 때 왜 그렇게할까요?


1
브라이언, 고마워 pre_get_posts가 정확히 당신이 설명하는 시나리오에서 페이지에서 작동하도록 고심하고 있습니다. 클라이언트는 아카이브 페이지가 될 곳에 사용자 정의 필드 / 내용을 추가해야하므로 "페이지"를 만들어야합니다. 클라이언트는 사용자 정의 링크를 추가하면 탐색 메뉴에 추가 할 항목이 표시되어야합니다. 나에게서 +1!
Will Lanni가

2
"pre_get_posts"를 사용하여 수행 할 수도 있습니다. 사용자 정의 게시물 유형을 사용자 정의 순서와 사용자 정의 필터로 나열하는 "정적 프론트 페이지"를 갖습니다. 이 페이지도 페이지가 매겨져 있습니다. 그것이 어떻게 작동하는지 보려면이 질문을 확인하십시오 : wordpress.stackexchange.com/questions/30851/… 간단히 말해서, query_posts를 사용하기위한 합법적 인 시나리오는 아직 없습니다.)
2ndkauboy

1
"이 기능을 사용하여 페이지의 기본 쿼리를 바꾸면 페이지로드 시간이 길어질 수 있습니다. 최악의 경우에는 필요한 작업량을 두 배 이상 늘리는 것이 좋습니다. 사용하기 쉽지만이 기능은 혼동되기 쉽습니다. 나중에 문제가됩니다. " 소스 codex.wordpress.org/Function_Reference/query_posts
Claudiu Creanga

이 답변은 모든 종류의 잘못입니다. WP에서 맞춤 게시물 유형과 동일한 URL을 사용하여 "페이지"를 만들 수 있습니다. EG CPT가 바나나 인 경우 동일한 URL을 가진 바나나라는 페이지를 얻을 수 있습니다. 그런 다음 siteurl.com/bananas로 끝납니다. 테마 폴더에 archive-bananas.php가 있으면 템플릿을 사용하고 대신 해당 페이지를 "재정의"합니다. 다른 의견 중 하나에서 언급했듯이이 "방법"을 사용하면 WP에 대한 작업 부하가 두 배가되므로 절대 사용해서는 안됩니다.
하이브리드 웹 개발자

8

functions.php에서 WordPress 쿼리를 수정합니다.

//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%')"; 
}

이 예제를 보길 원하지만 where 절은 사용자 정의 메타에 있습니다.
앤드류 웰치

6

시간이 지남에 따라 WordPress가 발전하고 현재 (5 년 후) 다른 점이 있기 때문에 수용 된 답변의 개선 사항을 간략하게 설명하면 다음과 같습니다.

pre_get_posts쿼리를 변경하기위한 필터입니다. '주요 쿼리'만 변경하는 데 가장 자주 사용됩니다.

실제로 행동 후크입니다. 필터가 아니며 모든 쿼리에 영향을 미칩니다.

기본 쿼리는 템플릿에 다음과 같이 나타납니다.

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

실제로 이것은 또한 사실이 아닙니다. 이 함수 는 기본 쿼리 에만 관련되지 않은 개체를 have_posts반복합니다 . 보조 쿼리로 변경 될 수도 있습니다.global $wp_queryglobal $wp_query;

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

get_posts ()

이것은 본질적으로 WP_Query 객체의 개별 인스턴스에 대한 래퍼입니다.

실제로 요즘 WP_Query은 클래스이므로 클래스의 인스턴스가 있습니다.


결론 : 당시 @StephenHarris는이 모든 것이 사실 일 가능성이 가장 높았지만 시간이 지남에 따라 WordPress의 사항이 변경되었습니다.


기술적으로, 그것은 후드 아래의 모든 필터이며, 작업은 단순한 필터입니다. 그러나 당신은 여기에 맞습니다. 그것은 참조로 인수를 전달하는 액션입니다. 이것은 더 간단한 액션과 어떻게 다른지입니다.
Milo

get_posts객체가 아닌 포스트 객체의 배열을 반환 WP_Query하므로 실제로는 정확합니다. 그리고 WP_Query항상 클래스 = 객체의 클래스 인스턴스를하고있다.
Milo

고마워, @Milo, 내 머리에 모델을 지나치게 단순화 한 어떤 이유로 든 올바른지.
prosti
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.