web-dev-qa-db-ja.com

カテゴリのランディングページとそれに続く投稿のページを作成する方法を教えてください。

ユーザーがアーカイブの最初のページにいる場合は静的コンテンツを表示し、その後のページにはカテゴリの投稿を表示するように、テーマにカテゴリテンプレートを作成しようとしています。私は基本的にWired.comでカテゴリーの振る舞いを再現しようとしています。ここで、 http://www.wired.com/category/design はランディングページで、/ category/design/page/1は時系列の逆順のリストです。あなたがカテゴリアーカイブに期待するような投稿の数。

ここで重要なのは、最初のページにカテゴリアーカイブの投稿が表示されていないことです。そのため、最初の投稿から始めるには次のページが必要です。私は最初に offsetとmanual paginationを使ってこれをやろうとしました 'paged'クエリ変数が2の場合、クエリのオフセットを0に設定することによって試みました。オフセットを1に設定し、カテゴリ内の2番目の投稿から始めます。

これがfunctions.phpに追加したものです。

add_action('pre_get_posts', 'category_query_offset', 1 );
function category_query_offset(&$query) {
  // Before anything else, make sure this is the right query
  if (!$query->is_category('news')) {
    return;
  }

  // Next, determine how many posts per page
  $ppp = get_option('posts_per_page');

  //Next, detect and handle pagination
  if ($query->is_paged) {
    // Manually determine page query offset
    $page_offset = (($query->query_vars['paged']-1) * $ppp) - $ppp;
    // Apply adjust page offset
    $query->set('offset', $page_offset );
  }
}

何が良いのでしょう、そしてそれが現れるのは Wired のように、デフォルトのページ区切りを使うので投稿は1ページから始まりますが静的コンテンツのために0ページを挿入することです。仮に、これは 'paged'が0の場合は静的コンテンツを表示し、 'paged'が1の場合は投稿の最初のページを表示することで可能になるようです。問題はWordpressが 'paged'を設定するため 'paged'が1にならないことです。ユーザーが最初のページを要求したときに0になります。つまり、/ category/news/page/1と/ category/newsの両方で 'paged'が0になります。

/ category/newsではなく、ユーザーが/ category/news/page/1を要求したかどうかを確認する方法はありますか?それに失敗して、2ページから始まるカテゴリ内のすべての投稿を表示する方法はありますか?

8
willcwelch

これは非常に興味深い質問です(私はこれを支持しました、特にあなたのアプローチと研究のために)。ここでの大きな曲線は、クエリの最初のページです。

  • 最初のページに0投稿を返すようにクエリを設定することはできません。

  • すべてのページのページコンテンツを1ページ上に移動すると、クエリにはまだ同じ投稿数しかないため、最後のページが失われるため、$max_num_pagesプロパティは同じままになります。

WP_Queryクラスをどうにかして1ページのオフセットで投稿を正確に返すようにし、またクエリの最後のページが失われないように正しいページ数を取得する必要があります。

次のアイデアを見て、すべてをコードに入れてみましょう。私たちがする前に、私はここでいくつかのメモを上げたいと思います

重要な注意:

  • 何もテストされていないので、バグがあるかもしれません。デバッグをオンにして、これをローカルでテストしてください。

  • コードには少なくともPHP 5.3が必要です。5.3より下のバージョンでは致命的なエラーが発生します。 PHP 5.5より下のバージョンをまだ使用している場合は、もうしばらく前にアップグレードしておく必要があります。

  • 自分のニーズに合わせてコードを修正して悪用する

電球のアイデア:

何WEが必要です

すべてがうまくいくようにするには、以下が必要になります。

  • 現在表示されているページ番号

  • 読み取り設定で設定されたposts_per_pageオプション

  • カスタムoffset

  • クエリの$found_postsプロパティを修正して$max_num_pagesプロパティを修正します

ページネーションはWP_Queryが非常に単純な数行のコードになっている

if ( empty($q['nopaging']) && !$this->is_singular ) {
    $page = absint($q['paged']);
    if ( !$page )
        $page = 1;
    // If 'offset' is provided, it takes precedence over 'paged'.
    if ( isset( $q['offset'] ) && is_numeric( $q['offset'] ) ) {
        $q['offset'] = absint( $q['offset'] );
        $pgstrt = $q['offset'] . ', ';
    } else {
        $pgstrt = absint( ( $page - 1 ) * $q['posts_per_page'] ) . ', ';
    }
    $limits = 'LIMIT ' . $pgstrt . $q['posts_per_page'];
}

基本的には、オフセットが明示的に設定されると、pagedパラメータは無視されます。 SQL LIMIT句の最初のパラメータはオフセットから再計算され、生成されたSQLクエリでスキップされるポストの数になります。

あなたの質問から、どうやらoffset0に設定するとき、オフセットクエリは失敗します、これは奇妙です、それは以下のチェックがtrueを返すべきであるからです

if ( isset( $q['offset'] ) && is_numeric( $q['offset'] ) )

0は有効な数値であり、trueを返すはずです。インストールに問題がない場合は、問題をデバッグする必要があります。

当面の問題に戻るために、同じ種類のロジックを使用してオフセットを計算および設定し、2ページのpost 1を取得し、そこからクエリをページ分割します。最初のページでは何も変更しないので、1ページ目にあるはずの投稿は通常どおりページ上に表示されたままになります。ページに表示しないように後で非表示にするだけです1

add_action( 'pre_get_posts', function ( $q )
{
    if (    !is_admin() // Only target the front end, VERY VERY IMPORTANT
         && $q->is_main_query() // Only target the main query, VERY VERY IMPORTANT
         && $q->is_cateory( 'news' ) // Only target the news category
    ) {
        $current_page = $q->get( 'paged' ); // Get the current page number
        // We will only need to run this from page 2 onwards
        if ( $current_page != 0 ) { // You can also use if ( is_paged() ) {
            // Get the amount of posts per page
            $posts_per_page = get_option( 'posts_per_page' );
            // Recalculate our offset
            $offset = ( ( $current_page - 1) * $posts_per_page ) - $posts_per_page; // This should work on page 2 where it returns 0

            // Set our offset
            $q->set( 'offset', $offset );
        }
    }
});

1ページの2ページから同じ投稿が表示されるはずです。前述したように、これが起こらない場合は、is_numeric( 0 )がfalseを返す(そうではない)か、別のpre_get_postsアクションがあります。また、オフセットを設定しようとしているか、posts_*句のフィルタ(より具体的にはpost_limitsフィルタ)を使用して使用しています。これは自分でデバッグする必要があるでしょう。

次の問題は、ページ付けを修正することです。前に述べたように、ページが短くなるからです。そのためには、クエリをその量だけオフセットしているので、クエリで見つかった投稿数にget_option( 'posts_per_page' )の値を追加する必要があります。こうすることで、1プロパティに$max_num_pagesを効果的に追加しています。

add_action( 'found_posts', function ( $found_posts, $q )
{
    if (    !is_admin() // Only target the front end, VERY VERY IMPORTANT
         && $q->is_main_query() // Only target the main query, VERY VERY IMPORTANT
         && $q->is_cateory( 'news' ) // Only target the news category
    ) {
        $found_posts = $found_posts + get_option( 'posts_per_page');
    }
}, 10, 2 );

これは最初のページを除いてすべてをソートするはずです。

今すぐ一緒に(および@ialocin - Yellow Submarine

これはすべてfunctions.phpに入ります。

add_action( 'pre_get_posts', function ( $q )
{
    if (    !is_admin() // Only target the front end, VERY VERY IMPORTANT
         && $q->is_main_query() // Only target the main query, VERY VERY IMPORTANT
         && $q->is_cateory( 'news' ) // Only target the news category
    ) {
        $current_page = $q->get( 'paged' ); // Get the current page number
        // We will only need to run this from page 2 onwards
        if ( $current_page != 0 ) { // You can also use if ( is_paged() ) {
            // Get the amount of posts per page
            $posts_per_page = get_option( 'posts_per_page' );
            // Recalculate our offset
            $offset = ( ( $current_page - 1) * $posts_per_page ) - $posts_per_page; // This should work on page 2 where it returns 0

            // Set our offset
            $q->set( 'offset', $offset );
        }
    }
});

add_filter( 'found_posts', function ( $found_posts, $q )
{
    if (    !is_admin() // Only target the front end, VERY VERY IMPORTANT
         && $q->is_main_query() // Only target the main query, VERY VERY IMPORTANT
         && $q->is_cateory( 'news' ) // Only target the news category
    ) {
        $found_posts = $found_posts + get_option( 'posts_per_page');
    }
    return $found_posts;
}, 10, 2 );

最初のページオプション

ここにいくつかの選択肢があります。

オプション1

私はたぶんこの選択肢のために行くでしょう。ここでやりたいことはcategory-news.phpまだ行っていない場合)を作成することです。これはnewsカテゴリが表示されるときはいつでも使用されるテンプレートになります。このテンプレートは非常に簡単になります

<?php
get_header()

if ( !is_paged() ) { // This is the first page
    get_template_part( 'news', 'special' );
} else { // This is not the first page
    get_template_part( 'news', 'loop' );
}

get_sidebar();
get_footer();

ご覧のとおり、news-special.phpnews-loop.phpの2つのテンプレート部分があります。今、2つのカスタムテンプレートの基本は以下のとおりです。

  • news-special.php - >このテンプレート部分は最初のページに表示したいものになります。ここにすべてのカスタム静的情報を追加してください。最初のページの投稿が表示されるため、このテンプレートのループを呼び出さないように十分に注意してください。

  • news-loop.php - >これはループを呼び出すテンプレートです。このセクションは次のようになります。

    global $wp_query;
    while ( have_posts() ) {
    the_post();
    
        // Your template tags and markup
    
    }
    

オプション2

静的コンテンツを含む別のテンプレートを作成し、newsカテゴリの最初のページを表示するときにこのテンプレートを使用するには、単にcategory_templateフィルタを使用します。また、このテンプレートではデフォルトのループを呼び出さないでください。また、ここでの命名規則がテンプレート階層内のテンプレート名と衝突しないようにしてください。

これが便利だと思います。気になる点がありましたら、お気軽にコメントをお寄せください。

編集

OPのおかげで、WP_Queryクラスにはっきりしたバグがあります、 tracチケット#34060 をチェックしてください。私が投稿したコードはWordpress v4.4からのもので、このバージョンではバグが修正されています。

私はバグのあるv4.3のソースコードに戻りました。コードは単にoffsetパラメータがoffsetであるかどうかをチェックするので、emptyに値を設定すると0が無視されることを確認できます。 PHPでは0は空と見なされます。私はこの振る舞い(バグ)がv4.3のみで発見されたのか、すべての以前のバージョンで発見されたのかわかりません(チケットによると、このバグはv4.3に存在します)。 Tracのチケットで。私が言ったように、このバグは間違いなくv4.4で修正されています

7
Pieter Goosen

更新:

add_action('pre_get_posts', 'category_query_offset', 1 );
function category_query_offset($query) {
 // Before anything else, make sure this is the right query
 if (!$query->is_category('news')) {
    return;
 }

 // Next, determine paged
 $ppp = get_option('paged');
}
// Next, handle paged
if($ppp > 1){
    $query->set('paged', $ppp-1);
}

のようなものを試してください

$paged = ( get_query_var('paged') ) ? get_query_var('paged')-1 : 1;
$args = array (
    'paged' => $paged,
);
0
Kika