web-dev-qa-db-ja.com

カスタムループのページ区切り替えを修正するにはどうすればいいですか?

テンプレートファイル/カスタムページテンプレートにカスタム/セカンダリクエリを追加しました。メインクエリループのページ区切りを使用するのではなく、WordPressでページ区切りにカスタムクエリを使用する方法

補遺

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

119
Chip Bennett

問題

デフォルトでは、特定のコンテキストで、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はpaged URLクエリ変数に基づく、クエリのpagedパラメーター。クエリが取得されると、そのpagedパラメータを使用して、どのページ分割された投稿のセットを返すかが決定されます。表示されたページネーションリンクをクリックして、次のページがロードされると、カスタムクエリにはページネーションが変更されたことを知る方法がありません

ソリューション

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

カスタムクエリがargs配列を使用すると仮定します。

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

正しいpagedパラメーターを配列に渡す必要があります。 get_query_var() を使用して、現在のページを決定するために使用されるURLクエリ変数を取得することにより、これを行うことができます。

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. メインクエリオブジェクトをnullにする:$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() =、およびいくつかの問題に遭遇します(leastではなく、ページネーションの問題です)。これらの問題を解決する最初のステップは、 query_posts() の不適切な使用を適切な WP_Query() 呼び出しに変換することです。

query_posts()を使用してメインループを変更する

メインループクエリのパラメーターを変更するだけの場合(ページごとの投稿の変更やカテゴリの除外など)、 query_posts()を使用したくなるかもしれません 。しかし、まだすべきではありません。 query_posts() を使用する場合、WordPressを強制してreplaceメインクエリオブジェクトにします。 (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_posts 、および query_posts()

213
Chip Bennett

このコードは、ページ区切り付きのカスタムループに使用します。

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

ソース:

21
webvitaly

いつものように素晴らしいチップ。これに対する補足として、ある「イントロテキスト」のためにPageにアタッチされたグローバルページテンプレートを使用していて、その後にページングしたいサブクエリが続くという状況を考えてみましょう。

上記のように paginate_links() を使用すると、ほとんどデフォルトで、(そしてかなりパーマリンクがオンになっていると仮定して)ページネーションリンクはデフォルトでmysite.ca/page-slug/page/#になりますが、404エラーが発生します。特定のURL構造で、実際には "page"の子ページが "page-slug"の子になります。

ここでのトリックは、/page/#/構造を受け入れ、それをWordPressが理解できるクエリ文字列、すなわちmysite.ca/?pagename=page-slug&paged=#に書き換える、その特定の "疑似アーカイブページ"ページスラッグにのみ適用される気の利いた書き換えルールを挿入することです。 pagenamepagednamepageではないことに注意してください(これが文字通り私の悲しみの原因となっていました。

これがリダイレクトのルールです。

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

デメリット/警告

私が私の口の中で少し戸惑うのは、このアプローチの1つの欠点は、ページスラッグのハードコーディングです。管理者がその疑似アーカイブページのページスラッグを変更したことがある場合は乾杯です - 書き換え規則が一致しなくなり、恐ろしい404が表示されます。

私はこの方法のための回避策を考えることができるかどうかわからないが、それがどういうわけか書き換え規則を引き起こしたのがグローバルページテンプレートであるならば、それはいいだろう。他の誰もその特定のナッツを割っていないならばいつか私はこの答えを再検討するかもしれません。

5
Tom Auger

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

素晴らしい答えChipが作成したものは今日修正する必要があります。
しばらくの間、メインクエリが実行された直後の$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 globalに依存します。


ページネーションのメカニズムをさらに改善し、query_posts関数により多くの自由を与えるために、私はこの可能な改善を作成しました:

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

2
prosti
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;
    ?>
1
ravi patel