Cara menggabungkan dua pertanyaan bersama

10

Saya mencoba memesan posting dalam kategori dengan menunjukkan posting dengan gambar terlebih dahulu dan kemudian posting tanpa gambar terakhir. Saya telah berhasil melakukannya dengan menjalankan dua kueri dan sekarang saya ingin menggabungkan kedua kueri tersebut.

Saya memiliki yang berikut ini:

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

Tetapi ketika saya mencoba dan melihat halaman maka saya mendapatkan kesalahan berikut:

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

Saya kemudian mencoba casting array_merge ke objek, tapi saya mendapat kesalahan berikut:

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

Bagaimana saya bisa memperbaiki kesalahan ini?

Howli
sumber

Jawaban:

8

Satu permintaan

Pikirkan hal ini sedikit lebih dan ada kemungkinan Anda bisa pergi dengan satu / permintaan utama. Atau dengan kata lain: Tidak perlu dua kueri tambahan saat Anda bisa bekerja dengan yang default. Dan jika Anda tidak dapat bekerja dengan yang default, Anda tidak akan membutuhkan lebih dari satu permintaan, tidak peduli berapa banyak loop yang ingin Anda pisahkan.

Prasyarat

Pertama, Anda perlu mengatur (seperti yang ditunjukkan dalam jawaban saya yang lain) nilai yang dibutuhkan di dalam pre_get_postsfilter. Di sana Anda mungkin akan mengatur posts_per_pagedan cat. Contoh tanpa 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 ) );

Membangun basis

Hal berikutnya yang kami butuhkan adalah plugin khusus berukuran kecil (atau masukkan saja ke functions.phpfile Anda jika Anda tidak keberatan memindahkannya selama pembaruan atau perubahan tema):

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

Plugin ini melakukan satu hal: Menggunakan PHP SPL (Perpustakaan PHP Standar) dan Antarmuka dan Iterator-nya. Apa yang sekarang kami dapatkan adalah FilterIteratoryang memungkinkan kami untuk dengan mudah menghapus item dari loop kami. Ini memperluas PHP SPL Filter Iterator sehingga kita tidak perlu mengatur semuanya. Kode ini dikomentari dengan baik, tetapi ada beberapa catatan:

  1. The accept()Metode memungkinkan untuk menentukan kriteria yang memungkinkan perulangan item - atau tidak.
  2. Di dalam metode itu kami gunakan WP_Query::the_post(), jadi Anda bisa menggunakan setiap tag templat di loop file templat Anda.
  3. Dan kami juga memonitor loop dan memundurkan posting ketika kami mencapai item terakhir. Ini memungkinkan untuk mengulang melalui jumlah loop yang tak terbatas tanpa mengatur ulang kueri kami.
  4. Ada satu metode kustom yang bukan bagian dari FilterIteratorspesifikasi: deny(). Metode ini sangat nyaman karena hanya berisi "proses atau tidak" pernyataan kami dan kami dapat dengan mudah menimpanya di kelas berikutnya tanpa perlu tahu apa pun selain dari tag template WordPress.

Bagaimana cara mengulang?

Dengan Iterator baru ini, kita tidak perlu if ( $customQuery->have_posts() )dan while ( $customQuery->have_posts() )lagi. Kita dapat pergi dengan foreachpernyataan sederhana karena semua pemeriksaan yang diperlukan sudah dilakukan untuk kita. Contoh:

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

Akhirnya kita membutuhkan tidak lebih dari foreachloop default . Kami bahkan dapat menjatuhkan the_post()dan masih menggunakan semua tag templat. $postObjek global akan selalu tetap sinkron.

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

Loop anak perusahaan

Sekarang yang menyenangkan adalah bahwa setiap filter kueri selanjutnya cukup mudah ditangani: Cukup tentukan deny()metode dan Anda siap untuk melanjutkan ke loop berikutnya. $this->current()akan selalu menunjuk ke pos kami yang sekarang di-loop.

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

Seperti yang telah kami definisikan bahwa sekarang kami deny()memutarkan setiap posting yang memiliki thumbnail, kami kemudian dapat secara otomatis mengulang semua posting tanpa thumbnail:

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

Menguji.

Plugin tes berikut tersedia sebagai Gist di GitHub. Cukup unggah dan aktifkan. Ini output / kesedihan ID dari setiap posting diulang sebagai callback pada loop_starttindakan. Ini berarti bahwa mungkin mendapatkan sedikit keluaran tergantung pada pengaturan Anda, jumlah posting dan konfigurasi. Harap tambahkan beberapa pernyataan batalkan dan ubah huruf var_dump()s di bagian akhir menjadi apa yang ingin Anda lihat dan di mana Anda ingin melihatnya. Itu hanya bukti konsep.

kaisar
sumber
6

Meskipun ini bukan cara terbaik untuk menyelesaikan masalah ini (jawaban @ kaiser adalah), untuk menjawab pertanyaan secara langsung, hasil kueri yang sebenarnya akan berada di $loop->postsdan $loop2->posts, jadi ...

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

... harus bekerja, tetapi Anda harus menggunakan foreachloop dan bukan WP_Querystruktur loop standar berdasarkan penggabungan pertanyaan seperti itu akan merusak WP_Queryobjek "meta" data tentang loop.

Anda juga dapat melakukan ini:

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

Tentu saja, solusi tersebut mewakili beberapa kueri, itulah sebabnya @ Kaiser adalah pendekatan yang lebih baik untuk kasus seperti ini di mana WP_Querydapat menangani logika yang diperlukan.

s_ha_dum
sumber
3

Sebenarnya ada meta_query(atau WP_Meta_Query) - yang mengambil array array - di mana Anda dapat mencari _thumbnail_idbaris. Jika kemudian Anda periksa EXISTS, Anda hanya dapat memperoleh yang memiliki bidang ini. Menggabungkan ini dengan catargumen, Anda hanya akan mendapatkan posting yang ditugaskan untuk kategori dengan ID 1dan yang memiliki gambar mini terlampir. Jika Anda memesannya dengan meta_value_num, maka Anda akan benar-benar memesannya dengan thumbnail ID terendah ke tertinggi (sebagaimana dinyatakan dengan orderdan ASC). Anda tidak harus menentukan valuekapan Anda menggunakan EXISTSsebagai comparenilai.

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

Sekarang saat mengulanginya, Anda dapat mengumpulkan semua ID dan menggunakannya dalam pernyataan eksklusif untuk kueri tambahan:

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

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

        // do display/rendering stuff here
    }
}

Sekarang Anda dapat menambahkan permintaan kedua Anda. Tidak perlu di wp_reset_postdata()sini - semuanya ada dalam variabel dan bukan kueri utama.

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

Tentu saja Anda bisa lebih pintar dan cukup mengubah pernyataan SQL di dalam pre_get_postsuntuk tidak menyia-nyiakan kueri utama. Anda juga bisa melakukan kueri pertama (di $thumbsUpatas) di dalam pre_get_postscallback filter.

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

Ini mengubah kueri utama, jadi kami hanya akan mendapatkan posting yang memiliki lampiran thumbnail. Sekarang kita dapat (seperti yang ditunjukkan pada kueri 1 di atas) mengumpulkan ID selama loop utama dan kemudian menambahkan kueri kedua yang menampilkan sisa posting (tanpa thumbnail).

Selain itu Anda bisa menjadi lebih pintar dan mengubah posts_clausesdan memodifikasi permintaan langsung memesan dengan nilai meta. Lihatlah jawaban ini karena jawaban saat ini hanyalah titik awal.

kaisar
sumber
3

Yang Anda butuhkan sebenarnya adalah permintaan ketiga untuk mendapatkan semua posting sekaligus. Kemudian, Anda mengubah dua kueri pertama untuk tidak mengembalikan kiriman, tetapi hanya kiriman ID dalam format yang dapat Anda gunakan.

The 'fields'=>'ids'parameter akan membuat query sebenarnya mengembalikan array yang cocok nomor pasca ID. Tapi kami tidak ingin seluruh objek permintaan, jadi kami menggunakan get_posts untuk ini.

Pertama, dapatkan ID pos yang kami butuhkan:

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

$ imageposts dan $ nonimageposts sekarang akan menjadi array nomor ID pos, jadi kami menggabungkannya

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

Hilangkan duplikasi nomor ID ...

$mypostids = array_unique( $mypostids );

Sekarang, buat kueri untuk mendapatkan posting aktual dalam urutan yang ditentukan:

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

Variabel $ loop sekarang menjadi objek WP_Query dengan posting Anda di dalamnya.

Otto
sumber
Terima kasih untuk ini. Menemukan ini menjadi solusi paling rumit untuk menjaga satu struktur loop dan perhitungan pagination yang tidak rumit.
Jay Neely