カスタムループのページネーションを修正する方法は?


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オブジェクトは、カスタムページテンプレートが割り当てられているページのIDの投稿のみで構成されます。

現在のコンテキストが何らかのアーカイブインデックスである場合、メイン$wp_queryはページネーションを引き起こすのに十分な投稿で構成されている可能性があり、それが問題の次の部分につながり$wp_queryます。メインオブジェクトの場合、WordPressはpagedpagedURLクエリ変数。クエリがフェッチされると、そのpagedパラメーターを使用して、どのページ分割された投稿のセットを返すかが決定されます。表示されたページネーションリンクをクリックして、次のページがロードされると、カスタムクエリにはページネーションが変更されたことを知る方法がありません

ソリューション

正しいページパラメータをカスタムクエリに渡す

カスタムクエリが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;

これで、カスタムクエリが取得されると、ページ分割された投稿の正しいセットが返されます。

ページネーション関数にカスタムクエリオブジェクトを使用する

ページネーション関数が正しい出力を生成するために-すなわち、カスタムクエリに関連する前/次/ページリンク-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()を経由してカスタムクエリのために別のオブジェクトをインスタンス化するというし、出力にカスタムループをWP_Query()、あなたはしている_doing_it_wrong()、と(いないいくつかの問題に実行されます少なくともページネーションの問題になりますています)。これらの問題を解決するための最初のステップは、不適切な使用をquery_posts()適切なWP_Query()呼び出しに変換することです。

query_posts()メインループの変更に使用

ページごとの投稿の変更やカテゴリの除外など、メインループクエリのパラメーターを変更するだけの場合は、を使用したくなるかもしれませんquery_posts()。しかし、まだすべきではありません。を使用するとquery_posts()、WordPressがメインのクエリオブジェクトを強制的に置き換えます。(WordPressは実際に2番目のクエリを作成し、上書きします$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_Querypre_get_postsquery_posts()


31
コーデックスのウィッシュページは非常に完成している可能性があります。
ピーターグースン14

チップ、あなたは私の一日を作りました!
tepkenvannkorn

1
チップ、あなたはいつも多くの時間を節約します!グーグルだけがランク付けするなら、私がクレイジーな検索を始める前に、あなたはより高い回答(グーグルのためのコールアウト);)ありがとう。
Sagive SEO

あなたの例を使用すると、このページの途中で(?:条件付きの代わりに)見つかったif-elseブロックを使用するまでページングが機能しませんでした:themeforest.net/forums/thread/…、非常に奇妙です。そうでなければ、この答えは私に多くを教えてくれました。
ポール14

2
素晴らしい答え-1つのこと、私はajaxコールで次/前のポストリンク機能を実行するのに問題がありました-それはまったくかかりません-周りをすばやく掘り下げた後、グローバルpagedが更新されていないことがわかりました(管理者と関係あります- ajax.php環境)ので、私はこれを追加しました: global $paged; $paged = $custom_query_args['paged']; そしてそれは働いた:)
acSlater 14

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」の子ページを探します。

ここでの秘trickは、/page/#/構造を受け入れ、WordPress CANが理解できるクエリ文字列、つまりに書き換える特定の「疑似アーカイブページ」ページナメクジにのみ適用される気の利いた書き換えルールを挿入することmysite.ca/?pagename=page-slug&paged=#です。注意pagenameしてpagedいないnamepage(ここでは、この答えをやる気に、私に悲しみの文字通りの時間を引き起こしています!)。

リダイレクトルールは次のとおりです。

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

いつものように、書き換えルールを変更するときは、管理バックエンドで[設定]> [パーマリンク]にアクセスしてパーマリンクフラッシュすることを忘れないでください

このように動作する複数のページがある場合(たとえば、複数のカスタム投稿タイプを処理する場合)、各ページスラッグの新しい書き換えルールの作成を避けることができます。特定したページスラッグに対して機能する、より一般的な正規表現を作成できます。

1つのアプローチは次のとおりです。

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

短所/警告

このアプローチの短所の1つは、ページスラッグのハードコーディングです。管理者がその擬似アーカイブページのページスラッグを変更した場合、あなたは乾杯します-書き換えルールは一致しなくなり、恐ろしい404を受け取ります。

この方法の回避策が考えられるかどうかはわかりませんが、それが何らかの理由で書き換えルールをトリガーしたグローバルページテンプレートであるとよいでしょう。他の誰もその特定のナッツを割ったことがなければ、いつかこの答えを再訪するかもしれません。


1
保存後をフックし、ページのメタキーの下にアーカイブテンプレートがあるかどうかを確認してから、_wp_page_template別の書き換えルールとフラッシュルールを追加します。
ミロ

2

を介してメインループクエリを変更しましたquery_posts()。ページネーションが機能しないのはなぜですか?どうすれば修正できますか?

作成された素晴らしい答えチップは今日修正する必要があります。
しばらくの間、メインクエリの実行直後にグローバルに$wp_the_query等しくなる変数があり$wp_queryます。

これが、これがチップの答えの一部である理由です:

メインクエリオブジェクトをハックする

もう必要ありません。一時変数を作成することでこの部分を忘れることができます。

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

だから今、私たちは呼び出すことができます:

$wp_query   = $wp_the_query;

またはさらに良い呼び出し:

wp_reset_query();

他のチップの概要はすべてそのままです。query-reset-partの後、ページネーション関数を呼び出すことができます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.