จะแก้ไขการแบ่งหน้าสำหรับลูปแบบกำหนดเองได้อย่างไร


122

ฉันได้เพิ่มคิวรีที่กำหนดเอง / รองลงในไฟล์เทมเพลต / เทมเพลตหน้าเว็บที่กำหนดเอง; ฉันจะทำให้ WordPress ใช้เคียวรีที่กำหนดเองของฉันสำหรับการแบ่งหน้าแทนการใช้การแบ่งหน้าลูปแบบสอบถามหลักได้อย่างไร

ภาคผนวก

query_posts()ฉันได้แก้ไขแบบสอบถามวงหลักผ่าน ทำไมการแบ่งหน้าไม่ทำงานและฉันจะแก้ไขได้อย่างไร

คำตอบ:


215

ปัญหา

ตามค่าเริ่มต้นในบริบทใดก็ตาม WordPress ใช้เคียวรีหลักเพื่อกำหนดเลขหน้า วัตถุแบบสอบถามหลักจะถูกเก็บไว้ใน$wp_queryส่วนกลางซึ่งยังใช้ในการส่งออกห่วงแบบสอบถามหลัก:

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

เมื่อคุณใช้แบบสอบถามที่กำหนดเองคุณสร้างวัตถุแบบสอบถามแยกต่างหากทั้งหมด:

$custom_query = new WP_Query( $custom_query_args );

และแบบสอบถามนั้นถูกส่งออกผ่านการวนซ้ำที่แยกจากกันโดยสิ้นเชิง:

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

แต่แท็กเลขแม่แบบรวมทั้งprevious_posts_link(), next_posts_link(), posts_nav_link()และpaginate_links()ฐานการส่งออกของพวกเขาในวัตถุแบบสอบถามหลัก$wp_query , แบบสอบถามหลักนั้นอาจหรือไม่อาจแบ่งหน้า หากบริบทปัจจุบันเป็นเทมเพลตของหน้าเว็บที่กำหนดเองตัวอย่างเช่น$wp_queryวัตถุหลักจะมีเพียงโพสต์เดียวซึ่งเป็นรหัสของหน้าซึ่งมีการกำหนดเทมเพลตหน้าที่กำหนดเอง

หากบริบทปัจจุบันเป็นดัชนีที่เก็บถาวรของการเรียงลำดับบางอย่างหลัก$wp_queryอาจประกอบด้วยโพสต์มากพอที่จะทำให้เกิดการแบ่งหน้าซึ่งนำไปสู่ส่วนต่อไปของปัญหา: สำหรับ$wp_queryวัตถุหลักWordPress จะส่งpaged พารามิเตอร์ไปยังแบบสอบถามตามpagedตัวแปรแบบสอบถาม URL เมื่อดึงแบบสอบถามแล้วpagedพารามิเตอร์นั้นจะถูกใช้เพื่อกำหนดชุดของโพสต์ที่ให้เลขหน้าเพื่อส่งคืน หากมีการเชื่อมโยงการแบ่งหน้าแสดงการคลิกและหน้าถัดไปโหลดแบบสอบถามแบบกำหนดเองของคุณจะไม่มีทางใดที่จะรู้ว่าเลขที่มีการเปลี่ยนแปลง

การแก้ไขปัญหา

ผ่านพารามิเตอร์ Paged ที่ถูกต้องไปยัง Custom Query

สมมติว่าแบบสอบถามแบบกำหนดเองใช้อาร์เรย์ args:

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

คุณจะต้องส่งpagedพารามิเตอร์ที่ถูกต้องไปยังอาร์เรย์ คุณสามารถทำได้โดยดึงตัวแปรคิวรี่ URL ที่ใช้เพื่อกำหนดหน้าปัจจุบันผ่านget_query_var():

get_query_var( 'paged' );

จากนั้นคุณสามารถผนวกพารามิเตอร์นั้นไปยังคิวรีแบบกำหนดเองของคุณทำอาร์เรย์อาร์เรย์

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

หมายเหตุ:หากหน้าของคุณเป็นแบบหน้าคงที่ให้ใช้pageแทนpagedหน้าแบบคงที่ใช้pageและไม่pagedใช้ นี่คือสิ่งที่คุณควรมีสำหรับหน้าคงที่

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

ตอนนี้เมื่อดึงเคียวรีที่กำหนดเองแล้วจะส่งคืนชุด paginated ที่ถูกต้องของโพสต์

การใช้ Custom Query Object สำหรับฟังก์ชันการแบ่งหน้า

เพื่อให้ฟังก์ชั่นการให้เลขหน้าให้ผลผลิตที่ถูกต้อง - เช่นการเชื่อมโยงก่อนหน้า / ถัดไป / หน้าเทียบกับแบบสอบถามที่กำหนดเอง - WordPress จะต้องถูกบังคับให้รับรู้แบบสอบถามที่กำหนดเอง สิ่งนี้ต้องใช้ "แฮ็ค": แทนที่$wp_queryวัตถุหลักด้วยวัตถุแบบสอบถามที่กำหนดเอง$custom_query:

แฮ็ควัตถุเคียวรีหลัก

  1. สำรองข้อมูลวัตถุแบบสอบถามหลัก: $temp_query = $wp_query
  2. ไม่มีวัตถุแบบสอบถามหลัก: $wp_query = NULL;
  3. สลับเคียวรีที่กำหนดเองเป็นวัตถุคิวรีหลัก: $wp_query = $custom_query;

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

"แฮ็ค" นี้จะต้องทำก่อนที่จะเรียกฟังก์ชั่นเลขหน้า

รีเซ็ตวัตถุแบบสอบถามหลัก

เมื่อฟังก์ชั่นการแบ่งหน้าได้รับการส่งออกตั้งค่าวัตถุแบบสอบถามหลัก:

$wp_query = NULL;
$wp_query = $temp_query;

แก้ไขฟังก์ชันการแบ่งหน้า

previous_posts_link()ฟังก์ชั่นจะทำงานได้ตามปกติโดยไม่คำนึงถึงการแบ่งหน้า page - 1มันเป็นเพียงการกำหนดหน้าปัจจุบันแล้วเอาท์พุทสำหรับการเชื่อมโยง อย่างไรก็ตามจำเป็นต้องมีการแก้ไขสำหรับnext_posts_link()การส่งออกอย่างถูกต้อง นี่เป็นเพราะnext_posts_link()ใช้max_num_pagesพารามิเตอร์:

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

เช่นเดียวกับพารามิเตอร์การสืบค้นอื่น ๆ โดยค่าเริ่มต้นฟังก์ชั่นจะใช้max_num_pagesสำหรับ$wp_queryวัตถุหลัก เพื่อบังคับnext_posts_link()ให้บัญชีสำหรับ$custom_queryวัตถุคุณจะต้องผ่านmax_num_pagesไปยังฟังก์ชั่น คุณสามารถดึงค่านี้จาก$custom_queryวัตถุ$custom_query->max_num_pages:

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

วางมันทั้งหมดเข้าด้วยกัน

ต่อไปนี้เป็นโครงสร้างพื้นฐานของลูปแบบสอบถามที่กำหนดเองที่มีฟังก์ชั่นการแบ่งหน้าการทำงานอย่างถูกต้อง:

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

ภาคผนวก: เกี่ยวกับquery_posts()อะไร

query_posts() สำหรับลูปรอง

หากคุณกำลังใช้query_posts()การส่งออกห่วงที่กำหนดเองแทนแล้ว instantiating วัตถุที่แยกต่างหากสำหรับแบบสอบถามแบบกำหนดเองผ่านWP_Query()แล้วคุณ_doing_it_wrong()และจะทำงานเป็นปัญหาหลาย (ไม่น้อยที่จะเป็นประเด็นที่เลขหน้า) ขั้นตอนแรกในการแก้ไขปัญหาเหล่านั้นคือการแปลงการใช้ที่ไม่เหมาะสมquery_posts()ไปเป็นการWP_Query()โทรที่เหมาะสม

การใช้query_posts()เพื่อปรับเปลี่ยนลูปหลัก

ถ้าคุณเพียงต้องการที่จะปรับเปลี่ยนพารามิเตอร์สำหรับแบบสอบถามวงหลัก - เช่นการเปลี่ยนโพสต์ต่อหนึ่งหน้าหรือไม่รวมหมวดหมู่ - query_posts()คุณอาจถูกล่อลวงไปใช้ แต่คุณก็ยังไม่ควร เมื่อคุณใช้query_posts()คุณบังคับให้ WordPress เปลี่ยนวัตถุแบบสอบถามหลัก (WordPress สร้างคำถามที่สองจริง ๆ แล้วเขียนทับ$wp_query) ปัญหาคือว่ามันจะแทนที่สิ่งนี้ช้าเกินไปในกระบวนการอัปเดตการแบ่งหน้า

วิธีการแก้ปัญหาคือการกรองแบบสอบถามหลักก่อนโพสต์จะถูกดึงผ่านpre_get_postsเบ็ด

แทนที่จะเพิ่มสิ่งนี้ลงในไฟล์เทมเพลตหมวดหมู่ ( category.php):

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

เพิ่มรายการต่อไปนี้ใน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' );

แทนที่จะเพิ่มสิ่งนี้ลงในไฟล์เทมเพลตดัชนีบล็อก ( home.php):

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

เพิ่มรายการต่อไปนี้ใน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' );

ด้วยวิธีนี้ WordPress จะใช้$wp_queryวัตถุที่ถูกแก้ไขแล้วเมื่อกำหนดเลขหน้าโดยไม่จำเป็นต้องแก้ไขเทมเพลต

เมื่อใดจึงจะใช้ฟังก์ชั่น

งานวิจัยนี้คำถามและคำตอบและคำถามนี้และคำตอบจะเข้าใจวิธีการและเมื่อใช้WP_Query, และpre_get_postsquery_posts()


31
หน้าเว็บที่ต้องการใน codex อาจจะเสร็จสมบูรณ์
Pieter Goosen

ชิปคุณสร้างวันของฉัน!
tepkenvannkorn

1
ชิปคุณประหยัดเวลามากเสมอ! ถ้าเพียง google จะจัดอันดับคุณคำตอบที่สูงขึ้น (คำบรรยายภาพสำหรับ googler) ก่อนที่ฉันจะค้นหาบ้า;) ขอบคุณ
Sagive SEO

การใช้ตัวอย่างของคุณฉันไม่สามารถทำให้เพจจิ้งทำงานจนกว่าฉันจะใช้บล็อก if-else ที่พบในช่วงกลาง (แทนที่จะเป็น?: เงื่อนไข) ลงหน้านี้: themeforest.net/forums/thread/ …แปลกมาก มิฉะนั้นคำตอบนี้สอนฉันมาก
P aul

2
คำตอบที่ดี - มีสิ่งหนึ่งที่ฉันมีปัญหาในการเรียกใช้ฟังก์ชั่นลิงค์โพสต์ถัดไป / ก่อนหน้าในการโทร ajax - มันก็ไม่ได้เกิดขึ้น - หลังจากการขุดรอบ ๆ อย่างรวดเร็วฉันพบว่าทั่วโลกpagedไม่ได้รับการปรับปรุง สภาพแวดล้อม ajax.php) ดังนั้นฉันจึงเพิ่มนี้: global $paged; $paged = $custom_query_args['paged']; และมันทำงาน :)
acSlater

21

ฉันใช้รหัสนี้สำหรับวงที่กำหนดเองกับการแบ่งหน้า:

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

ที่มา:


1
กวดวิชาที่ดีอีกข้อหนึ่งที่แตกต่างจากคำตอบนี้: callmenick.com/post/custom-wordpress-loop-with-pagination
mrwweb

5

ยอดเยี่ยมเช่นเคยชิป ในฐานะที่เป็นภาคผนวกของเรื่องนี้ให้พิจารณาสถานการณ์ที่คุณใช้เทมเพลตหน้าส่วนกลางที่แนบมากับหน้าสำหรับ "ข้อความแนะนำ" และตามด้วยแบบสอบถามย่อยที่คุณต้องการได้รับการเพจ

การใช้paginate_links ()ตามที่คุณกล่าวถึงข้างต้นส่วนใหญ่เป็นค่าเริ่มต้น (และสมมติว่าคุณเปิดใช้งานลิงก์ถาวร) ลิงก์การแบ่งหน้าของคุณจะเริ่มต้นmysite.ca/page-slug/page/#ที่น่ารัก แต่จะโยน404ข้อผิดพลาดเนื่องจาก WordPress ไม่ทราบเกี่ยวกับโครงสร้าง URL นั้น ๆ ค้นหาหน้าย่อยของ "หน้า" ซึ่งเป็นส่วนย่อยของ "page-slug"

เคล็ดลับที่นี่คือการแทรกเขียนกฎดีที่จะใช้กับโดยเฉพาะอย่างยิ่ง "หลอกหน้าเก็บ" กระสุนหน้าว่ายอมรับ/page/#/โครงสร้างและเขียนใหม่เพื่อสตริงแบบสอบถามที่ WordPress mysite.ca/?pagename=page-slug&paged=#สามารถเข้าใจคือ หมายเหตุpagenameและpagedไม่ใช่nameและpage(ซึ่งทำให้ฉันเสียใจอย่างแท้จริงเป็นเวลาหลายชั่วโมงกระตุ้นให้เกิดคำตอบที่นี่!)

นี่คือกฎการเปลี่ยนเส้นทาง:

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

เช่นเคยเมื่อเปลี่ยนกฎการเขียนซ้ำอย่าลืมล้างลิงก์ของคุณโดยไปที่การตั้งค่า> ลิงก์ถาวรในส่วนหลังของผู้ดูแลระบบ

หากคุณมีหลายหน้าที่จะทำเช่นนี้ (ตัวอย่างเช่นเมื่อจัดการกับโพสต์ที่กำหนดเองหลายประเภท) คุณอาจต้องการหลีกเลี่ยงการสร้างกฎการเขียนใหม่ใหม่สำหรับแต่ละหน้าบุ้ง เราสามารถเขียนนิพจน์ทั่วไปทั่วไปที่ใช้งานได้กับหน้ากระสุนที่คุณระบุ

วิธีการหนึ่งอยู่ด้านล่าง:

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

ข้อเสีย / ข้อ จำกัด

ข้อเสียอย่างหนึ่งของวิธีการนี้ที่ทำให้ฉันอ้วกในปากของฉันเล็กน้อยคือการเขียนโค้ดของกระสุนหน้าอย่างหนัก หากผู้ดูแลระบบเปลี่ยนกระสุนหน้าของหน้าหลอกเก็บถาวรนั้นคุณจะต้องปิ้ง - กฎการเขียนซ้ำจะไม่ตรงกันอีกต่อไปและคุณจะได้รับ 404 ที่หวาดกลัว

ฉันไม่แน่ใจว่าฉันจะคิดวิธีแก้ปัญหาสำหรับวิธีนี้ แต่มันจะดีถ้ามันเป็นแม่แบบหน้าทั่วโลกที่อย่างใดเรียกใช้กฎการเขียนใหม่ บางวันฉันอาจทบทวนคำตอบนี้ถ้าไม่มีใครแตกถั่วนั้น


1
คุณสามารถขอให้โพสต์บันทึกตรวจสอบว่าหน้ามีเทมเพลตเก็บถาวรของคุณภายใต้คีย์เมตา_wp_page_templateจากนั้นเพิ่มกฎการเขียนใหม่และล้างข้อมูลอีกครั้ง
Milo

2

query_posts()ฉันได้แก้ไขแบบสอบถามวงหลักผ่าน ทำไมการแบ่งหน้าไม่ทำงานและฉันจะแก้ไขได้อย่างไร

คำตอบที่ยอดเยี่ยมต้องสร้างชิปที่จะต้องแก้ไข
บางครั้งเรามี$wp_the_queryตัวแปรที่ควรจะเท่ากับ$wp_queryโลกหลังจากแบบสอบถามหลักดำเนินการ

นี่คือสาเหตุที่ส่วนนี้มาจากคำตอบของ Chip:

แฮ็ควัตถุเคียวรีหลัก

ไม่จำเป็นอีกต่อไป เราสามารถลืมส่วนนี้ด้วยการสร้างตัวแปรชั่วคราว

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

ดังนั้นตอนนี้เราสามารถโทร:

$wp_query   = $wp_the_query;

หรือดีกว่าเราสามารถโทร:

wp_reset_query();

ชิปทุกอย่างที่เหลือเค้าร่างไว้ หลังจากส่วนการสืบค้นข้อมูลรีเซ็ตนั้นคุณสามารถเรียกใช้ฟังก์ชันการให้เลขหน้าf($wp_query)- ซึ่งขึ้นอยู่กับ$wp_queryระดับโลก


เพื่อปรับปรุงกลไกการให้เลขหน้าและเพื่อให้อิสระกับquery_postsฟังก์ชันมากขึ้นฉันได้สร้างการปรับปรุงที่เป็นไปได้นี้:

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


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;
    ?>
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.