İki sorgu birlikte nasıl birleştirilir


10

Önce resim içeren yazıları, sonra resimsiz yayınları göstererek bir kategorideki yayınları sipariş etmeye çalışıyorum. Bunu iki sorgu çalıştırarak yapmayı başardım ve şimdi iki sorguyu birlikte birleştirmek istiyorum.

Şunlara sahibim:

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

Ancak sayfayı görüntülemeye çalıştığımda şu hatayı alıyorum:

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

Sonra bir nesneye array_merge döküm denedim, ancak aşağıdaki hatayı aldım:

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

Bu hatayı nasıl düzeltebilirim?

Yanıtlar:


8

Tek bir sorgu

Bunu biraz daha düşündüm ve tek bir / ana sorgu ile gidebilirsiniz. Başka bir deyişle: Varsayılan soruyla çalışabileceğinizde iki ek sorguya gerek yoktur. Varsayılan bir sorguyla çalışamamanız durumunda, sorguyu kaç tane bölmek istediğinize bakılmaksızın tek bir sorgudan daha fazlasına ihtiyacınız olmayacaktır.

Ön şartlar

Öncelikle, bir pre_get_postsfiltrenin içindeki gerekli değerleri ayarlamanız gerekir (diğer cevabımda gösterildiği gibi) . Orada muhtemelen ayarlayacak posts_per_pageve cat. pre_get_posts-Filter içermeyen örnek :

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

Bir üs inşa etmek

İhtiyacımız olan bir sonraki şey küçük bir özel eklentidir (veya functions.phpgüncellemeler veya tema değişiklikleri sırasında hareket ettirmeyi düşünmüyorsanız sadece dosyanıza koyun ):

<?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;
    }
}

Bu eklenti bir şey yapar: PHP SPL (Standart PHP Kütüphanesi) ve onun arayüzleri ve yineleyicileri kullanır. Şimdi sahip olduğumuz FilterIteratorşey, öğeleri döngüden rahatça çıkarmamıza izin veren bir şey. PHP SPL Filter Iterator'ı genişletir, böylece her şeyi ayarlamamız gerekmez. Kod iyi yorumlanmış, ancak bazı notlar:

  1. accept()Ya da - yöntem madde döngü izin kriterleri için imkan sağlar.
  2. Kullandığımız yöntemin WP_Query::the_post()içinde, şablon dosyaları döngünüzdeki her şablon etiketini kolayca kullanabilirsiniz.
  3. Ayrıca, son öğeye ulaştığımızda döngüyü izliyoruz ve mesajları geri alıyoruz. Bu, sorgumuzu sıfırlamadan sonsuz miktarda döngüye girmeyi sağlar.
  4. Parçası olmayan bir özel yöntem var FilterIteratorözellikleri: deny(). Bu yöntem özellikle sadece "sürecimiz ya da değil" durumumuzu içerdiği için uygundur ve WordPress şablon etiketlerinden başka bir şey bilmenize gerek kalmadan sonraki sınıflarda kolayca yazabiliriz.

Döngü nasıl yapılır?

Bu yeni Yineleyici if ( $customQuery->have_posts() )ile while ( $customQuery->have_posts() )artık ihtiyacımız yok. Bizim için foreachgerekli tüm kontroller zaten yapıldığından basit bir açıklama yapabiliriz . Misal:

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

Son olarak, varsayılan bir foreachdöngüden başka bir şeye ihtiyacımız yok . Hatta the_post()tüm şablon etiketlerini bırakıp kullanabiliriz. Global $postnesne her zaman senkronize kalır.

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

Yardımcı döngüler

Şimdi güzel olan şey, daha sonraki her sorgu filtresinin işlenmesinin oldukça kolay olmasıdır: Sadece deny()yöntemi tanımlayın ve bir sonraki döngünüz için hazırsınız. $this->current()her zaman şu anda yayınlanan yazıya işaret eder.

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

Artık deny()küçük resmi olan her gönderiyi döngüye aldığımızı tanımladığımız gibi , tüm yayınları küçük resim olmadan anında döngüleyebiliriz:

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

Dene.

Aşağıdaki test eklentisi GitHub'da Gist olarak kullanılabilir. Sadece yükleyin ve etkinleştirin. Her döngüdeki gönderinin kimliğini loop_starteylemde geri arama olarak çıktılar / döker . Bu, kurulumunuza, yayın sayısına ve yapılandırmanıza bağlı olarak biraz çıkış alabileceği anlamına gelir. Lütfen bazı iptal ifadeleri ekleyin var_dump()ve sonunda görmek istediğiniz yere ve nerede görmek istediğinize göre değiştirin. Bu sadece bir kavram kanıtı.


6

Bu, bu sorunu çözmenin en iyi yolu olmasa da (@ kaiser cevabı), soruyu doğrudan cevaplamak için, gerçek sorgu sonuçları olacak $loop->postsve $loop2->postsbu yüzden ...

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

... çalışmalı, ancak foreachdöngü ile WP_Queryilgili WP_Querynesne "meta" verilerini kıracak şekilde sorguları birleştirmek için temel standart döngü yapısını değil, bir döngü kullanmanız gerekir .

Bunu da yapabilirsiniz:

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

Tabii ki, bu çözümler birden fazla sorguyu temsil eder, bu yüzden @ Kaiser bu gibi durumlarda WP_Querygerekli mantığı işleyebilen daha iyi bir yaklaşımdır .


3

Aslında satırları arayabileceğiniz bir dizi dizi alan meta_query(veya WP_Meta_Query) vardır _thumbnail_id. Daha sonra kontrol ederseniz EXISTS, yalnızca bu alana sahip olanları elde edebilirsiniz. Bunu catargümanla birleştirdiğinizde , yalnızca kimliğine sahip kategoriye atanan 1ve küçük resmi iliştirilmiş yayınlar alırsınız . O zamana bunları sipariş olursa meta_value_num(belirtilen şekilde, o zaman aslında en yüksek küçük resim kimliği düşük bunları sipariş edeceğiz orderve ASC). Değer olarak valuene zaman kullanacağınızı belirtmeniz gerekmez .EXISTScompare

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

Şimdi bunlarla dolaşırken, tüm kimlikleri toplayabilir ve ikincil sorgu için özel bir ifadede kullanabilirsiniz:

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

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

        // do display/rendering stuff here
    }
}

Şimdi ikinci sorgunuzu ekleyebilirsiniz. wp_reset_postdata()Burada gerek yok - her şey değişkendir ve ana sorguda değil.

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

Tabii ki çok daha akıllı olabilir ve sadece pre_get_postsana sorguyu boşa harcamamak için içindeki SQL deyimini değiştirebilirsiniz . $thumbsUpBir pre_get_postsfiltre geri çağrısındaki ilk sorguyu ( yukarıda) da yapabilirsiniz .

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

Bu, ana sorguyu değiştirdi, bu nedenle yalnızca küçük resim eklenmiş yayınları alacağız. Şimdi (yukarıdaki 1. sorguda gösterildiği gibi) ana döngü sırasında kimlikleri toplayabilir ve daha sonra yazıların geri kalanını (küçük resim olmadan) gösteren ikinci bir sorgu ekleyebiliriz.

Bunun yanı sıra, daha akıllı olabilir ve posts_clausesdoğrudan meta değerine göre sorguyu değiştirebilir ve değiştirebilirsiniz. Şimdiki cevap sadece bir başlangıç ​​noktası olduğu için bu cevaba bir göz atın .


3

İhtiyacınız olan şey aslında tüm mesajları bir kerede almak için üçüncü bir sorgu. Ardından, ilk iki sorgunuzu yayınları döndürmek için değil, yalnızca çalışabileceğiniz bir biçimde posta kimliklerini döndürmek için değiştirirsiniz.

'fields'=>'ids'Parametresi bir sorgu aslında sonrası kimlik numaralarını eşleşen bir dizi döndürür yapacaktır. Ancak tüm sorgu nesnesini istemiyoruz, bunun yerine bunlar için get_posts kullanıyoruz.

İlk olarak, ihtiyacımız olan posta kimliklerini alın:

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

$ imageposts ve $ nonimageposts artık bir dizi gönderi kimliği numarası olacak, bu yüzden onları birleştiriyoruz

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

Yinelenen kimlik numaralarını ortadan kaldırın ...

$mypostids = array_unique( $mypostids );

Şimdi, gerçek yayınları belirtilen sırayla almak için bir sorgu yapın:

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

$ Loop değişkeni artık yayınlarınızda bulunan bir WP_Query nesnesidir.


Bunun için teşekkürler. Bunun, bir döngü yapısını ve karmaşık olmayan sayfalandırma hesaplamalarını tutmak için en az karmaşık çözüm olduğu bulunmuştur.
Jay Neely
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.