【WordPress】先頭固定表示に対応してページネーション（ページャ）も表示する

# やりたいこと

- カテゴリアーカイブページで記事一覧を表示したい
- 「ブログのトップに固定」にチェックした記事を先頭に固定表示したい
- ページネーションも表示したい
- 1ページ目の記事一覧では、先頭固定記事の数も含めた一覧を表示したい（要するにページ送りしても全て同じ件数表示）

# コード

- ページネーションは `functions.php` に `wp_pagination()` 関数を定義していますが、まとめて記述するかはご自由にどうぞ。
- 1ページ目は、最初に先頭固定の記事一覧を表示して、残りの記事一覧を表示しています。要するにバラバラに表示しています。
- 1ページ目だけ先頭に固定した記事の数を引いた一覧を表示しています。
- サンプルコードでは 1 ページに MAX 9 件（コード上では `AFTER_PAGE_DISPLAY_COUNT` の数）の記事一覧を表示します。
- 先頭固定する記事の数が 1 ページあたりの MAX 件数を超える想定はしていません。

```php:category.php（記事のタイトルをリスト表示する例）
    <ol>
    <?php

    /**
     * 先頭固定を表示
     */
    // 現在のページ番号を取得
    $paged = get_query_var( 'paged', 1 );

    // 先頭固定したブログIDを格納する変数
    $post__not_in = [];

    // WP_Query() の引数には 'category_name'  => 'カテゴリのスラッグ名' でカテゴリを指定することが可能
    // $query_string はカテゴリアーカイブの場合、"category_name = カテゴリのスラッグ名" の文字列を返す
    // 文字列を処理して "category_name" => "カテゴリのスラッグ名" のように配列にして $org_args に代入
    parse_str ( $query_string, $org_args );

    // WP_Query() の引数に渡す条件を配列で格納
    $add_args = [
        'post_type'      => 'post',
        'posts_per_page' => -1,     // すべての投稿を含める
        'post__in'       => get_option( 'sticky_posts' )
    ];

    // array_merge で配列を結合
    $args = array_merge( $org_args, $add_args );

    // 記事データを $first_query に代入
    $first_query = new WP_Query( $args );

    if ( $first_query->have_posts() ) {
        while ($first_query->have_posts()) {
            $first_query->the_post();

            // 先頭固定表示したブログIDを $post__not_in に格納
            $post__not_in[] = $post->ID;

            if ($paged === 0) {
                // 表示する内容
                the_title( '<li>', '</li>');
            }
        }
    }

    /**
     * 先頭固定以外を表示
     */

    // 2ページ目以降の表示件数（この数は環境にあわせて適宜変更）
    define('AFTER_PAGE_DISPLAY_COUNT', 9);

    // 1ページ目の表示件数
    // $post__not_in には固定表示したブログIDが配列で格納されている
    // 2ページ目以降の表示件数から固定表示の数を引いた数を値として指定
    define('FIRST_PAGE_DISPLAY_COUNT', AFTER_PAGE_DISPLAY_COUNT - count($post__not_in));

    // 2ページ目なら AFTER_PAGE_DISPLAY_COUNT を数を、1ページ目なら FIRST_PAGE_DISPLAY_COUNT の数を $posts_per_page に代入
    $posts_per_page = $paged > 1 ? AFTER_PAGE_DISPLAY_COUNT : FIRST_PAGE_DISPLAY_COUNT;

    // WP_Query() の引数に渡す条件を配列で格納
    // post__not_in で先頭固定表示したブログIDを除外
    $add_args = [
        'post_type'      => 'post',
        'paged'          => $paged,
        'post__not_in'   => $post__not_in,
        'posts_per_page' => $posts_per_page
    ];

    // 2ページ目以降のオフセットを計算
    // オフセットは読み飛ばす投稿の数
    // 2ページ目の場合なら FIRST_PAGE_DISPLAY_COUNT の数を飛ばして、その次から一覧を表示する
    if ( $paged > 1 ) {
        $add_args['offset'] = FIRST_PAGE_DISPLAY_COUNT + ( $paged - 2 ) * AFTER_PAGE_DISPLAY_COUNT;
    }

    // array_merge で配列を結合
    $args = array_merge( $org_args, $add_args );

    // 記事データを $wp_query に代入
    $wp_query = new WP_Query( $args );

    // 合計ページ数を計算
    $wp_query->max_num_pages = ceil ( ( $wp_query->found_posts + (AFTER_PAGE_DISPLAY_COUNT - FIRST_PAGE_DISPLAY_COUNT) ) / AFTER_PAGE_DISPLAY_COUNT );

    if ( $wp_query->have_posts() ) {
        while ( $wp_query->have_posts() ) {
            $wp_query->the_post();

            // 表示する内容
            the_title( '<li>', '</li>');
        }
        wp_reset_postdata();
    }

    // ページネーション
    wp_pagination();
    ?>
    </ol>
```

```php:functions.php

/**
 * ページネーション
 */
function wp_pagination() {
	global $wp_query;
	$big = 99999999;
	$page_format = paginate_links( array(
		'base' => str_replace( $big, '%#%', esc_url( get_pagenum_link( $big ) ) ),
		'format' => '?paged=%#%',
		'current' => max( 1, get_query_var('paged') ),
        'total' => $wp_query->max_num_pages,
        'prev_next'    => True,
		'prev_text'    => __('&lt;'),
		'next_text'    => __('&gt;'),
		'type'  => 'array'
	) );
	if( is_array($page_format) ) {
		echo '<div class="pager"><ul class="pager_list">';
		foreach ( $page_format as $page ) {
    		echo "<li class='pager_item'>$page</li>";
		}
	} else {
        echo '<div class="pager"><ul class="pager_list"><li class="pager_item current"><span class="current">1</span></li></ul></div>';
    }
    echo '</ul></div>';
	wp_reset_query();
}
```

# 感想

「ブログのトップに固定」は標準機能なので、もっと簡単に一覧を作れると思ってたけど、そんなことなかった...（1日消えた）

# 補足

先頭固定表示の記事だけに CSS クラスを追加する `post_class()`や、先頭固定表示か判定する `is_sticky()` などがあります。

# 参考サイト

- [[WordPress] 1ページ目だけ表示件数が異なるページネーションの実装 | 制作メモ | 560DESIGNS](http://www.560designs.com/memo/1296.html)