Bagaimana cara memperbaiki pagination untuk loop kustom?

122

Saya telah menambahkan kueri kustom / sekunder ke file templat / templat halaman khusus; bagaimana cara membuat WordPress menggunakan kueri khusus untuk pagination, alih-alih menggunakan pagination loop kueri utama?

Tambahan

Saya telah memodifikasi kueri loop utama via query_posts(). Mengapa pagination tidak berfungsi, dan bagaimana cara memperbaikinya?

Chip Bennett
sumber

Jawaban:

215

Masalah

Secara default, dalam konteks apa pun yang diberikan, WordPress menggunakan kueri utama untuk menentukan pagination. Objek kueri utama disimpan di $wp_queryglobal, yang juga digunakan untuk menampilkan loop kueri utama:

if ( have_posts() ) : while ( have_posts() ) : the_post();

Saat Anda menggunakan kueri khusus , Anda membuat objek kueri yang sepenuhnya terpisah:

$custom_query = new WP_Query( $custom_query_args );

Dan permintaan itu adalah output melalui loop yang sepenuhnya terpisah:

if ( $custom_query->have_posts() ) : 
    while ( $custom_query->have_posts() ) : 
        $custom_query->the_post();

Tapi tag pagination Template, termasuk previous_posts_link(), next_posts_link(), posts_nav_link(), dan paginate_links(), mendasarkan output mereka pada objek query utama , $wp_query. Kueri utama itu mungkin atau mungkin tidak paginasi. Jika konteks saat ini adalah templat halaman kustom, misalnya, $wp_queryobjek utama hanya akan terdiri dari satu posting - ID dari halaman di mana templat halaman kustom ditugaskan.

Jika konteks saat ini adalah semacam indeks arsip, utama $wp_querymungkin terdiri dari cukup banyak posting yang menyebabkan pagination, yang mengarah ke bagian selanjutnya dari masalah: untuk $wp_queryobjek utama , WordPress akan mengirimkan paged parameter ke kueri, berdasarkan pada pagedVariabel kueri URL. Ketika kueri diambil, pagedparameter itu akan digunakan untuk menentukan set posting paginasi mana yang akan dikembalikan. Jika tautan pagination yang ditampilkan diklik, dan halaman berikutnya dimuat, permintaan khusus Anda tidak akan mengetahui bahwa pagination telah berubah .

Solusinya

Melewati Parameter Paged yang Benar ke Permintaan Kustom

Dengan asumsi bahwa kueri khusus menggunakan array args:

$custom_query_args = array(
    // Custom query parameters go here
);

Anda harus meneruskan pagedparameter yang benar ke array. Anda dapat melakukannya dengan mengambil variabel kueri URL yang digunakan untuk menentukan halaman saat ini, melalui get_query_var():

get_query_var( 'paged' );

Anda kemudian dapat menambahkan parameter itu ke array args kueri khusus:

$custom_query_args['paged'] = get_query_var( 'paged' ) 
    ? get_query_var( 'paged' ) 
    : 1;

Catatan: Jika halaman Anda adalah halaman depan statis , pastikan untuk menggunakan pagebukan pagedsebagai halaman depan statis gunakan pagedan tidak paged. Inilah yang harus Anda miliki untuk halaman depan statis

$custom_query_args['paged'] = get_query_var( 'page' ) 
    ? get_query_var( 'page' ) 
    : 1;

Sekarang, ketika kueri khusus diambil, kumpulan kiriman paginasi yang benar akan dikembalikan.

Menggunakan Objek Kueri Kustom untuk Fungsi Pagination

Agar fungsi pagination dapat menghasilkan output yang benar - yaitu tautan sebelumnya / berikutnya / halaman relatif terhadap permintaan khusus - WordPress harus dipaksa untuk mengenali permintaan khusus. Hal ini memerlukan sedikit "hack": mengganti utama $wp_queryobjek dengan objek query kustom, $custom_query:

Meretas objek permintaan utama

  1. Cadangkan objek permintaan utama: $temp_query = $wp_query
  2. Null objek permintaan utama: $wp_query = NULL;
  3. Tukar kueri khusus ke objek kueri utama: $wp_query = $custom_query;

    $temp_query = $wp_query;
    $wp_query   = NULL;
    $wp_query   = $custom_query;
    

"Retasan" ini harus dilakukan sebelum memanggil fungsi pagination

Setel ulang objek kueri utama

Setelah fungsi pagination telah di-output, reset objek query utama:

$wp_query = NULL;
$wp_query = $temp_query;

Perbaikan Fungsi Pagination

The previous_posts_link()fungsi akan bekerja secara normal, terlepas dari pagination. Ini hanya menentukan halaman saat ini, dan kemudian menampilkan tautan untuk page - 1. Namun, perbaikan diperlukan untuk next_posts_link()menghasilkan dengan benar. Ini karena next_posts_link()menggunakan max_num_pagesparameter:

<?php next_posts_link( $label , $max_pages ); ?>

Seperti parameter kueri lainnya, secara default fungsi akan digunakan max_num_pagesuntuk $wp_queryobjek utama . Untuk memaksa next_posts_link()untuk memperhitungkan $custom_queryobjek, Anda harus meneruskan max_num_pageske fungsi. Anda dapat mengambil nilai ini dari $custom_queryobjek $custom_query->max_num_pages::

<?php next_posts_link( 'Older Posts' , $custom_query->max_num_pages ); ?>

Menyatukan semuanya

Berikut ini adalah konstruksi dasar dari loop kueri khusus dengan fungsi pagination yang berfungsi dengan baik:

// Define custom query parameters
$custom_query_args = array( /* Parameters go here */ );

// Get current page and append to custom query parameters array
$custom_query_args['paged'] = get_query_var( 'paged' ) ? get_query_var( 'paged' ) : 1;

// Instantiate custom query
$custom_query = new WP_Query( $custom_query_args );

// Pagination fix
$temp_query = $wp_query;
$wp_query   = NULL;
$wp_query   = $custom_query;

// Output custom query loop
if ( $custom_query->have_posts() ) :
    while ( $custom_query->have_posts() ) :
        $custom_query->the_post();
        // Loop output goes here
    endwhile;
endif;
// Reset postdata
wp_reset_postdata();

// Custom query loop pagination
previous_posts_link( 'Older Posts' );
next_posts_link( 'Newer Posts', $custom_query->max_num_pages );

// Reset main query object
$wp_query = NULL;
$wp_query = $temp_query;

Tambahan: Bagaimana query_posts()?

query_posts() untuk Loop Sekunder

Jika Anda menggunakan query_posts()untuk menghasilkan loop kustom, alih-alih instantiating objek terpisah untuk permintaan kustom melalui WP_Query(), maka Anda _doing_it_wrong(), dan akan mengalami beberapa masalah (tidak sedikit yang akan menjadi masalah pagination). Langkah pertama untuk menyelesaikan masalah-masalah tersebut adalah mengonversi penggunaan yang tidak query_posts()tepat menjadi WP_Query()panggilan yang tepat .

Menggunakan query_posts()untuk Memodifikasi Loop Utama

Jika Anda hanya ingin mengubah parameter untuk kueri loop utama - seperti mengubah posting per halaman, atau mengecualikan kategori - Anda mungkin tergoda untuk menggunakannya query_posts(). Tapi kamu tetap tidak seharusnya. Saat Anda menggunakan query_posts(), Anda memaksa WordPress untuk mengganti objek permintaan utama. (WordPress benar-benar membuat kueri kedua, dan menimpa $wp_query.) Masalahnya, meskipun, ia melakukan penggantian ini terlambat dalam proses untuk memperbarui pagination.

Solusinya adalah dengan memfilter permintaan utama sebelum posting diambil , melalui pre_get_postshook.

Alih-alih menambahkan ini ke file templat kategori ( category.php):

query_posts( array(
    'posts_per_page' => 5
) );

Tambahkan yang berikut ke functions.php:

function wpse120407_pre_get_posts( $query ) {
    // Test for category archive index
    // and ensure that the query is the main query
    // and not a secondary query (such as a nav menu
    // or recent posts widget output, etc.
    if ( is_category() && $query->is_main_query() ) {
        // Modify posts per page
        $query->set( 'posts_per_page', 5 ); 
    }
}
add_action( 'pre_get_posts', 'wpse120407_pre_get_posts' );

Alih-alih menambahkan ini ke file template indeks posting blog ( home.php):

query_posts( array(
    'cat' => '-5'
) );

Tambahkan yang berikut ke functions.php:

function wpse120407_pre_get_posts( $query ) {
    // Test for main blog posts index
    // and ensure that the query is the main query
    // and not a secondary query (such as a nav menu
    // or recent posts widget output, etc.
    if ( is_home() && $query->is_main_query() ) {
        // Exclude category ID 5
        $query->set( 'category__not_in', array( 5 ) ); 
    }
}
add_action( 'pre_get_posts', 'wpse120407_pre_get_posts' );

Dengan begitu, WordPress akan menggunakan objek yang sudah dimodifikasi $wp_querysaat menentukan pagination, tanpa perlu modifikasi templat.

Kapan harus menggunakan fungsi apa

Penelitian pertanyaan ini dan jawaban dan pertanyaan dan jawaban untuk memahami bagaimana dan kapan menggunakan WP_Query, pre_get_postsdan query_posts().

Chip Bennett
sumber
31
Halaman harapan dalam codex bisa sangat lengkap.
Pieter Goosen
Chip, kau membuat hariku!
tepkenvannkorn
1
Chip, Anda selalu menghemat banyak waktu! jika saja google akan memberi peringkat Anda jawaban yang lebih tinggi (info untuk googler) sebelum saya menjadi gila mencari;) terima kasih.
Sagive SEO
Dengan menggunakan contoh Anda, saya tidak dapat mengaktifkan paging sampai saya menggunakan blok if-else yang ditemukan di tengah jalan (alih-alih ?: Bersyarat ) di halaman ini: themeforest.net/forums/thread/… , sangat aneh. Kalau tidak, jawaban ini banyak mengajari saya.
P aul
2
Jawaban yang bagus - 1 hal, saya mengalami masalah menjalankan fungsi link postingan berikutnya / sebelumnya dalam panggilan ajax - itu tidak akan terjadi - setelah penggalian cepat di sekitar saya menemukan global pagedtidak diperbarui (obv ada hubungannya dengan admin lingkungan ajax.php) jadi saya menambahkan ini: global $paged; $paged = $custom_query_args['paged']; dan itu berhasil :)
acSlater
21

Saya menggunakan kode ini untuk loop kustom dengan pagination:

<?php
if ( get_query_var('paged') ) {
    $paged = get_query_var('paged');
} elseif ( get_query_var('page') ) { // 'page' is used instead of 'paged' on Static Front Page
    $paged = get_query_var('page');
} else {
    $paged = 1;
}

$custom_query_args = array(
    'post_type' => 'post', 
    'posts_per_page' => get_option('posts_per_page'),
    'paged' => $paged,
    'post_status' => 'publish',
    'ignore_sticky_posts' => true,
    //'category_name' => 'custom-cat',
    'order' => 'DESC', // 'ASC'
    'orderby' => 'date' // modified | title | name | ID | rand
);
$custom_query = new WP_Query( $custom_query_args );

if ( $custom_query->have_posts() ) :
    while( $custom_query->have_posts() ) : $custom_query->the_post(); ?>

        <article <?php post_class(); ?>>
            <h3><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h3>
            <small><?php the_time('F jS, Y') ?> by <?php the_author_posts_link() ?></small>
            <div><?php the_excerpt(); ?></div>
        </article>

    <?php
    endwhile;
    ?>

    <?php if ($custom_query->max_num_pages > 1) : // custom pagination  ?>
        <?php
        $orig_query = $wp_query; // fix for pagination to work
        $wp_query = $custom_query;
        ?>
        <nav class="prev-next-posts">
            <div class="prev-posts-link">
                <?php echo get_next_posts_link( 'Older Entries', $custom_query->max_num_pages ); ?>
            </div>
            <div class="next-posts-link">
                <?php echo get_previous_posts_link( 'Newer Entries' ); ?>
            </div>
        </nav>
        <?php
        $wp_query = $orig_query; // fix for pagination to work
        ?>
    <?php endif; ?>

<?php
    wp_reset_postdata(); // reset the query 
else:
    echo '<p>'.__('Sorry, no posts matched your criteria.').'</p>';
endif;
?>

Sumber:

webvitaly
sumber
1
Tutorial bagus lainnya dengan varian jawaban ini: callmenick.com/post/custom-wordpress-loop-with-pagination
mrwweb
5

Luar biasa seperti biasa. Sebagai tambahan untuk ini, pertimbangkan situasi di mana Anda menggunakan templat halaman global yang dilampirkan ke Halaman untuk beberapa "teks intro" dan diikuti oleh subquery yang ingin Anda paging.

Menggunakan paginate_links () seperti yang Anda sebutkan di atas, dengan sebagian besar default, (dan dengan asumsi Anda telah mengaktifkan permalink cukup) tautan pagination Anda akan default ke mysite.ca/page-slug/page/#yang indah tetapi akan menimbulkan 404kesalahan karena WordPress tidak tahu tentang struktur URL tertentu dan akan benar-benar akan cari halaman anak dari "halaman" itu adalah anak dari "page-slug".

Triknya di sini adalah menyisipkan aturan penulisan ulang yang bagus yang hanya berlaku untuk siput halaman "pseudo archive page" tertentu yang menerima /page/#/struktur dan menulis ulangnya ke string kueri yang dapat dipahami oleh WordPress, yaitu mysite.ca/?pagename=page-slug&paged=#. Perhatikan pagenamedan pagedtidak namedan page(yang menyebabkan saya benar-benar JAM kesedihan, memotivasi jawaban ini di sini!).

Berikut aturan pengalihan:

add_rewrite_rule( "page-slug/page/([0-9]{1,})/?$", 'index.php?pagename=page-slug&paged=$matches[1]', "top" );

Seperti biasa, ketika mengubah aturan penulisan ulang, jangan lupa untuk membilas permalink Anda dengan mengunjungi Pengaturan> Permalinks di Admin.

Jika Anda memiliki beberapa halaman yang akan berperilaku seperti ini (misalnya, ketika berhadapan dengan beberapa jenis posting kustom), Anda mungkin ingin menghindari membuat aturan penulisan ulang baru untuk setiap halaman slug. Kami dapat menulis ekspresi reguler yang lebih umum yang berfungsi untuk setiap halaman siput yang Anda identifikasi.

Satu pendekatan di bawah ini:

function wpse_120407_pseudo_archive_rewrite(){
    // Add the slugs of the pages that are using a Global Template to simulate being an "archive" page
    $pseudo_archive_pages = array(
        "all-movies",
        "all-actors"
    );

    $slug_clause = implode( "|", $pseudo_archive_pages );
    add_rewrite_rule( "($slug_clause)/page/([0-9]{1,})/?$", 'index.php?pagename=$matches[1]&paged=$matches[2]', "top" );
}
add_action( 'init', 'wpse_120407_pseudo_archive_rewrite' );

Kekurangan / Peringatan

Salah satu kelemahan dari pendekatan ini yang membuat saya muntah sedikit di mulut saya adalah hard-coding dari Page slug. Jika admin pernah mengubah halaman slug dari halaman pseudo-arsip itu, Anda bersulang - aturan penulisan ulang tidak akan lagi cocok dan Anda akan mendapatkan 404 yang ditakuti.

Saya tidak yakin bisa memikirkan solusi untuk metode ini, tetapi alangkah baiknya jika itu adalah templat halaman global yang entah bagaimana memicu aturan penulisan ulang. Suatu hari saya dapat meninjau kembali jawaban ini jika tidak ada orang lain yang memecahkan kacang itu.

Tom Auger
sumber
1
Anda dapat mengaitkan penyimpanan pos, periksa apakah laman memiliki templat arsip di bawah tombol meta _wp_page_template, lalu tambahkan aturan penulisan ulang dan flush lainnya.
Milo
2

Saya telah memodifikasi kueri loop utama via query_posts(). Mengapa pagination tidak berfungsi, dan bagaimana cara memperbaikinya?

Jawaban hebat Chip yang dibuat perlu dimodifikasi hari ini.
Untuk beberapa waktu kami memiliki $wp_the_queryvariabel yang harus sama dengan $wp_queryglobal setelah permintaan utama dijalankan.

Inilah sebabnya mengapa ini bagian dari jawaban Chip:

Meretas objek permintaan utama

sudah tidak dibutuhkan lagi. Kita bisa melupakan bagian ini dengan membuat variabel sementara.

// Pagination fix
$temp_query = $wp_query;
$wp_query   = NULL;
$wp_query   = $custom_query;

Jadi sekarang kita bisa menelepon:

$wp_query   = $wp_the_query;

atau bahkan lebih baik kita dapat memanggil:

wp_reset_query();

Segala yang lainnya yang diuraikan Chip tetap. Setelah permintaan-reset-bagian Anda dapat memanggil fungsi pagination yang f($wp_query)- tergantung pada $wp_queryglobal.


Untuk lebih meningkatkan mekanika pagination dan memberikan lebih banyak kebebasan untuk query_postsfungsi, saya membuat peningkatan yang mungkin:

https://core.trac.wordpress.org/ticket/39483

prosti
sumber
1
global $wp_query;
        $paged = get_query_var('paged', 1);

    $args = array( 
        'post_type' => '{your_post_type_name}',
        'meta_query' => array('{add your meta query argument if need}'),  
        'orderby' => 'modified',
        'order' => 'DESC',
        'posts_per_page' => 20,
        'paged' => $paged 
    );
    $query = new WP_Query($args);

    if($query->have_posts()):
        while ($query->have_posts()) : $query->the_post();
            //add your code here
        endwhile;
        wp_reset_query();

        //manage pagination based on custom Query.
        $GLOBALS['wp_query']->max_num_pages = $query->max_num_pages;
        the_posts_pagination(array(
            'mid_size' => 1,
            'prev_text' => __('Previous page', 'patelextensions'),
            'next_text' => __('Next page', 'patelextensions'),
            'before_page_number' => '<span class="meta-nav screen-reader-text">' . __('Page', 'patelextensions') . ' </span>',
        ));
    else:
    ?>
        <div class="container text-center"><?php echo _d('Result not found','30'); ?></div>
    <?php
        endif;
    ?>
ravi patel
sumber