두 개의 쿼리를 병합하는 방법


10

이미지가있는 게시물을 먼저 표시 한 다음 이미지가없는 게시물을 마지막으로 표시하여 카테고리의 게시물을 주문하려고합니다. 두 개의 쿼리를 실행 하여이 작업을 수행했으며 이제 두 쿼리를 병합하려고합니다.

나는 다음을 가지고있다 :

<?php
$loop = new WP_Query( array('meta_key' => '_thumbnail_id', 'cat' => 1 ) );
$loop2 = new WP_Query( array('meta_key' => '', 'cat' => 1 ) );
$mergedloops = array_merge($loop, $loop2);

while($mergedloops->have_posts()): $mergedloops->the_post(); ?>

그러나 페이지를 보려고하면 다음 오류가 발생합니다.

 Fatal error: Call to a member function have_posts() on a non-object in...

그런 다음 array_merge를 객체에 캐스팅하려고 시도했지만 다음 오류가 발생했습니다.

Fatal error: Call to undefined method stdClass::have_posts() in...

이 오류를 어떻게 해결할 수 있습니까?

답변:


8

단일 쿼리

이것에 대해 조금 더 생각하면 단일 / 기본 쿼리로 갈 수 있습니다. 즉, 기본 쿼리로 작업 할 때 두 개의 추가 쿼리가 필요하지 않습니다. 또한 기본 쿼리로 작업 할 수없는 경우 쿼리를 분할하려는 루프 수에 관계없이 단일 쿼리 이상이 필요하지 않습니다.

전제 조건

먼저 pre_get_posts필터 내에서 필요한 값을 설정해야합니다 (다른 답변과 같이) . 당신은 가능성이 설정합니다 posts_per_page및 cat. pre_get_posts-Filter가 없는 예 :

$catID = 1;
$catQuery = new WP_Query( array(
    'posts_per_page' => -1,
    'cat'            => $catID,
) );
// Add a headline:
printf( '<h1>%s</h1>', number_format_i18n( $catQuery->found_posts )
    .__( " Posts filed under ", 'YourTextdomain' )
    .get_cat_name( $catID ) );

기지 건설

다음으로 필요한 것은 작은 사용자 정의 플러그인입니다 (또는 functions.php업데이트 또는 테마 변경 중에 이동하지 않는 경우 파일에 넣으십시오 ).

<?php
/**
 * Plugin Name: (#130009) Merge Two Queries
 * Description: "Merges" two queries by using a <code>RecursiveFilterIterator</code> to divide one main query into two queries
 * Plugin URl:  http://wordpress.stackexchange.com/questions/130009/how-to-merge-two-queries-together
 */

class ThumbnailFilter extends FilterIterator implements Countable
{
    private $wp_query;

    private $allowed;

    private $counter = 0;

    public function __construct( Iterator $iterator, WP_Query $wp_query )
    {
        NULL === $this->wp_query AND $this->wp_query = $wp_query;

        // Save some processing time by saving it once
        NULL === $this->allowed
            AND $this->allowed = $this->wp_query->have_posts();

        parent::__construct( $iterator );
    }

    public function accept()
    {
        if (
            ! $this->allowed
            OR ! $this->current() instanceof WP_Post
        )
            return FALSE;

        // Switch index, Setup post data, etc.
        $this->wp_query->the_post();

        // Last WP_Post reached: Setup WP_Query for next loop
        $this->wp_query->current_post === $this->wp_query->query_vars['posts_per_page'] -1
            AND $this->wp_query->rewind_posts();

        // Doesn't meet criteria? Abort.
        if ( $this->deny() )
            return FALSE;

        $this->counter++;
        return TRUE;
    }

    public function deny()
    {
        return ! has_post_thumbnail( $this->current()->ID );
    }

    public function count()
    {
        return $this->counter;
    }
}

이 플러그인은 PHP SPL (Standard PHP Library) 과 인터페이스 및 반복자를 사용합니다. 이제 우리가 얻는 것은 FilterIterator루프에서 항목을 편리하게 제거 할 수있게하는 것입니다. PHP SPL Filter Iterator를 확장하므로 모든 것을 설정할 필요는 없습니다. 코드는 잘 주석 처리되어 있지만 다음과 같은 참고 사항이 있습니다.

  1. 이 accept()방법을 사용하면 항목 반복 여부를 기준으로 정의 할 수 있습니다.
  2. 이 방법에서는을 사용 WP_Query::the_post()하므로 템플릿 파일 루프의 모든 템플릿 태그를 간단하게 사용할 수 있습니다.
  3. 또한 마지막 항목에 도달하면 루프를 모니터링하고 포스트를 되감습니다. 이렇게하면 쿼리를 재설정하지 않고도 무한한 수의 루프를 반복 할 수 있습니다.
  4. FilterIterator사양의 일부가 아닌 하나의 사용자 정의 방법이 deny()있습니다. 이 방법은 "프로세스 여부"문만 포함하므로 특히 편리하며 WordPress 템플릿 태그 외에는 아무 것도 알 필요없이 이후 클래스에서 쉽게 덮어 쓸 수 있습니다.

반복하는 방법?

이 새로운 반복자, 우리는 필요하지 않습니다 if ( $customQuery->have_posts() )와 while ( $customQuery->have_posts() )더 이상. foreach필요한 모든 점검이 이미 완료되었으므로 간단한 진술로 진행할 수 있습니다 . 예:

global $wp_query;
// First we need an ArrayObject made out of the actual posts
$arrayObj = new ArrayObject( $wp_query->get_posts() );
// Then we need to throw it into our new custom Filter Iterator
// We pass the $wp_query object in as second argument to keep track with it
$primaryQuery = new ThumbnailFilter( $arrayObj->getIterator(), $wp_query );

마지막으로 기본 foreach루프 만 있으면 됩니다. the_post()모든 템플릿 태그를 삭제 하고 계속 사용할 수도 있습니다 . 전역 $post객체는 항상 동기화 상태를 유지합니다.

foreach ( $primaryQuery as $post )
{
    var_dump( get_the_ID() );
}

자회사 루프

이제 좋은 점은 이후의 모든 쿼리 필터를 처리하기가 매우 쉽다는 것입니다. deny()메서드를 정의하기 만하면 다음 루프로 넘어갈 수 있습니다. $this->current()항상 현재 반복되는 게시물을 가리 킵니다.

class NoThumbnailFilter extends ThumbnailFilter
{
    public function deny()
    {
        return has_post_thumbnail( $this->current()->ID );
    }
}

deny()썸네일이있는 모든 게시물을 반복 하도록 정의한 후 썸네일없이 모든 게시물을 즉시 반복 할 수 있습니다.

foreach ( $secondaryQuery as $post )
{
    var_dump( get_the_title( get_the_ID() ) );
}

그것을 테스트하십시오.

다음 테스트 플러그인 은 GitHub에서 Gist로 사용할 수 있습니다. 간단히 업로드하고 활성화하십시오. 모든 루프 된 게시물의 ID를 loop_start조치 에 대한 콜백으로 출력 / 덤프합니다 . 이는 설정, 게시물 수 및 구성에 따라 약간의 출력을 얻을 수 있음을 의미합니다. 중단 문구를 추가 var_dump()하고 마지막에 s를보고 싶은 곳과보고 싶은 곳으로 변경하십시오. 그것은 단지 개념의 증거 일뿐입니다.


6

이것이이 문제를 해결하는 가장 좋은 방법은 아니지만 (@kaiser의 답변입니다), 질문에 직접 대답하기 위해 실제 쿼리 결과는 다음 $loop->posts과 같습니다 $loop2->posts.

$mergedloops = array_merge($loop->posts, $loop2->posts);

... 작동해야하지만 쿼리를 병합 하면 루프에 대한 객체 "메타"데이터 가 손상되므로 기본 표준 루프 구조가 foreach아닌 루프 를 사용해야합니다 .WP_QueryWP_Query

당신은 또한 이것을 할 수 있습니다 :

$loop = new WP_Query( array('fields' => 'ids','meta_key' => '_thumbnail_id', 'cat' => 1 ) );
$loop2 = new WP_Query( array('fields' => 'ids','meta_key' => '', 'cat' => 1 ) );
$ids = array_merge($loop->posts, $loop2->posts);
$merged = new WP_Query(array('post__in' => $ids,'orderby' => 'post__in'));

물론 이러한 솔루션은 여러 쿼리를 나타내 WP_Query므로 @Kaiser가 필요한 논리를 처리 할 수 있는 이와 같은 경우에 더 적합한 방법입니다 .


3

실제로 meta_query(또는 WP_Meta_Query)-배열 배열을 사용하여 _thumbnail_id행을 검색 할 수 있습니다 . 그런 다음를 확인 EXISTS하면이 필드가있는 항목 만 얻을 수 있습니다. 이것을 cat인수 와 결합 하면 ID가있는 카테고리에 할당되고 1미리보기 이미지가 첨부 된 게시물 만 얻을 수 있습니다. 그런 다음을 기준으로 주문하면 meta_value_num실제로 썸네일 ID를 기준으로 가장 작은 순서대로 주문합니다 ( order및로 표시 ASC). 당신은을 지정할 필요가 없습니다 value사용할 때 EXISTS같은 compare값.

$thumbsUp = new WP_Query( array( 
    'cat'        => 1,
    'meta_query' => array( 
        array(
            'key'     => '_thumbnail_id',
            'compare' => 'EXISTS',
        ),
    ),
    'orderby'    => 'meta_value_num',
    'order'      => 'ASC',
) );

이제 그것들을 루핑 할 때 모든 ID를 수집하고 보조 쿼리에 대한 독점 문에서 사용할 수 있습니다.

$postsWithThumbnails = array();
if ( $thumbsUp->have_posts() )
{
    while ( $thumbsUp->have_posts() )
    {
        $thumbsUp->the_post();

        // collect them
        $postsWithThumbnails[] = get_the_ID();

        // do display/rendering stuff here
    }
}

이제 두 번째 쿼리를 추가 할 수 있습니다. wp_reset_postdata()여기에 대한 필요는 없습니다 -모든 것은 변수에 있고 주 쿼리는 아닙니다.

$noThumbnails = new WP_Query( array(
    'cat'          => 1,
    'post__not_in' => $postsWithThumbnails
) );
// Loop through this posts

물론 훨씬 더 똑똑하고 SQL pre_get_posts쿼리를 변경 하여 기본 쿼리를 낭비하지 않을 수 있습니다. 필터 콜백 $thumbsUp내 에서 첫 번째 쿼리 ( 위)를 간단하게 수행 할 수도 pre_get_posts있습니다.

add_filter( 'pre_get_posts', 'wpse130009excludeThumbsPosts' );
function wpse130009excludeThumbsPosts( $query )
{
    if ( $query->is_admin() )
        return $query;

    if ( ! $query->is_main_query() )
        return $query;

    if ( 'post' !== $query->get( 'post_type' ) )
        return $query;

    // Only needed if this query is for the category archive for cat 1
    if (
        $query->is_archive() 
        AND ! $query->is_category( 1 )
    )
        return $query;

    $query->set( 'meta_query', array( 
        array(
            'key'     => '_thumbnail_id',
            'compare' => 'EXISTS',
        ),
    ) );
    $query->set( 'orderby', 'meta_value_num' );

    // In case we're not on the cat = 1 category archive page, we need the following:
    $query->set( 'category__in', 1 );

    return $query;
}

이로 인해 기본 검색어가 변경되었으므로 미리보기 이미지가 첨부 된 게시물 만 가져옵니다. 이제 위의 첫 번째 쿼리에 표시된대로 주 루프 동안 ID를 수집 한 다음 나머지 게시물을 표시하는 두 번째 쿼리를 추가 할 수 있습니다 (축소판 제외).

그 외에도 posts_clauses메타 값을 기준으로 쿼리를 더 똑똑하고 수정 하고 직접 수정할 수 있습니다. 현재 답변 은 시작점 이므로이 답변 을 살펴보십시오 .


3

실제로 필요한 것은 모든 게시물을 한 번에 가져 오는 세 번째 쿼리입니다. 그런 다음 게시물을 반환하지 않고 작업 할 수있는 형식의 게시물 ID 만 반환하도록 처음 두 쿼리를 변경합니다.

이 'fields'=>'ids'매개 변수는 쿼리가 실제로 일치하는 게시물 ID 번호의 배열을 반환하도록합니다. 그러나 전체 쿼리 객체를 원하지 않기 때문에 대신 get_posts를 사용합니다.

먼저 필요한 게시물 ID를 얻습니다.

$imageposts = get_posts( array('fields'=>'ids', 'meta_key' => '_thumbnail_id', 'cat' => 1 ) );
$nonimageposts = get_posts( array('fields'=>'ids', 'meta_key' => '', 'cat' => 1 ) );

$ imageposts와 $ nonimageposts는 모두 게시물 ID 번호의 배열이되므로 병합합니다.

$mypostids = array_merge( $imageposts, $nonimageposts );

중복 된 ID 번호를 제거하십시오 ...

$mypostids = array_unique( $mypostids );

이제 지정된 순서대로 실제 게시물을 가져 오도록 쿼리를 작성하십시오.

$loop = new WP_Query( array('post__in' => $mypostids, 'ignore_sticky_posts' => true, 'orderby' => 'post__in' ) );

$ loop 변수는 이제 게시물이 포함 된 WP_Query 객체입니다.


고마워 이것이 하나의 루프 구조와 복잡하지 않은 페이지 매김 계산을 유지하기위한 가장 복잡한 솔루션이라는 것을 알았습니다.
— Jay Neely
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.