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


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