WordPressの標準機能では、投稿本文に <!--nextpage--> を入れることで記事を複数ページに分割できます。ところが、ACFの柔軟コンテンツ(Flexible Content)で本文を組んでいる場合、この仕組みは使えません。
今回は、ACFの柔軟コンテンツに「改ページ」というレイアウトを用意し、改ページごとにHTMLを配列へ保存してから出力することで、長い記事を複数ページに分割する方法を紹介します。
なぜ標準の改ページが使えないのか
標準の <!--nextpage--> は、post_content(投稿本文)の文字列を分割して動いています。柔軟コンテンツの中身は本文ではなくカスタムフィールドに保存されているため、the_content() や wp_link_pages() のページ分割には一切引っかかりません。
そこで、「分割は自分で行い、ページ送りも自分で作る」方針にします。
全体の仕組み
やることは大きく3つです。
- 「改ページ」レイアウトを作る:柔軟コンテンツに、中身のない目印用のレイアウトを追加します。
- 改ページごとに配列へ保存する:柔軟コンテンツをそのまま出力せず、行ごとのHTMLを
$html .=で文字列として連結していきます。改ページに当たったら、そこまでのHTMLを配列に入れて、次のページに切り替えます。 - 表示するページだけ出力する:URLのページ番号に対応する配列の要素を
echoして、ページ送りを付けます。
改ページのレイアウト自体は出力せず、「区切り」としてだけ使います
① 「改ページ」レイアウトを作る
柔軟コンテンツはACF PRO の機能です。すでに本文用のフィールド(ここでは contents とします)がある前提で、レイアウトを1つ追加します。
- フィールドグループを開く:柔軟コンテンツのフィールドがあるフィールドグループを編集します。
- 「レイアウトを追加」を押す:ラベルを「改ページ」、名前を
pagebreakにします。 - サブフィールドは作らない:目印として使うだけなので、中身は不要です。編集画面で何のブロックか分かるように、「メッセージ」フィールドに「ここでページが分かれます」と書いておくと親切です。
メッセージフィールドは値を保存しないため、データには影響しません。
② 改ページごとに配列へ保存する
柔軟コンテンツを have_rows() でループしながら、レイアウトごとのHTMLを $html に連結していきます。pagebreak に当たったら、それまでの $html を $pages に入れて、$html を空に戻します。
functions.php に次の関数を追加します。ここではテキストと画像の2つのレイアウトを例にしています。
/**
* 柔軟コンテンツを改ページ単位のHTML配列にする
*
* @param string $field_name 柔軟コンテンツのフィールド名
* @param int|false $post_id 投稿ID(省略時は現在の投稿)
* @return array ページごとのHTML文字列の配列
*/
function get_flexible_pages( $field_name = 'contents', $post_id = false ) {
$pages = array();
$html = '';
if ( have_rows( $field_name, $post_id ) ) {
while ( have_rows( $field_name, $post_id ) ) {
the_row();
switch ( get_row_layout() ) {
case 'pagebreak':
$pages[] = $html; // ここまでを1ページとして確定
$html = '';
break;
case 'text':
$html .= '<div class="flex-text">';
$html .= wp_kses_post( get_sub_field( 'text' ) );
$html .= '</div>';
break;
case 'image':
$image_id = get_sub_field( 'image' ); // 戻り値は「画像ID」
if ( $image_id ) {
$html .= '<figure class="flex-image">';
$html .= wp_get_attachment_image( $image_id, 'large' );
$html .= '</figure>';
}
break;
}
}
}
$pages[] = $html; // 最後のページ
// 先頭や連続した改ページで空になったページを除外して詰め直す
return array_values( array_filter( $pages, 'strlen' ) );
}
ポイントは次の3つです。
pagebreakのcaseで「配列に入れて、$htmlを空にする」だけで分割できます。他のレイアウトは普通に出力処理を書くだけで、改ページを意識する必要はありません。- エスケープは
$htmlに連結する時点で済ませています(wp_kses_post()など)。そのため、出力時はそのままechoできます。 - 最後の
array_filter()で空のページを取り除いているため、改ページを置き間違えて連続させても空白ページは生まれません。
レイアウトが増えたら、case を足していくだけです。
③ 表示するページだけ出力する
現在のページ番号を取得する
投稿の2ページ目以降のURL(/記事スラッグ/2/ など)は、WordPressが page というクエリ変数として解釈してくれます。paged ではなく page である点に注意してください(paged はアーカイブ用です)。
<?php
$pages = get_flexible_pages( 'contents' );
$total = count( $pages );
$paged = max( 1, (int) get_query_var( 'page' ) );
// 存在しないページ番号は404にする
if ( $total > 0 && $paged > $total ) {
global $wp_query;
$wp_query->set_404();
status_header( 404 );
nocache_headers();
get_template_part( '404' );
exit;
}
get_header();
?>
該当ページのHTMLを出力する
配列は0始まりなので、$paged - 1 番目を出力します。
<?php if ( $total ) : ?>
<div class="post-body">
<?php echo $pages[ $paged - 1 ]; ?>
</div>
<?php endif; ?>
ページ送りを作る
wp_link_pages() は本文の分割数を元に動くので、柔軟コンテンツでは使えません。ページ送りは自前で作ります。
<?php if ( $total > 1 ) : ?>
<nav class="pager" aria-label="ページ送り">
<?php for ( $i = 1; $i <= $total; $i++ ) :
$url = ( 1 === $i )
? get_permalink()
: trailingslashit( get_permalink() ) . $i . '/';
?>
<?php if ( $i === $paged ) : ?>
<span class="pager__item is-current" aria-current="page"><?php echo $i; ?></span>
<?php else : ?>
<a class="pager__item" href="<?php echo esc_url( $url ); ?>"><?php echo $i; ?></a>
<?php endif; ?>
<?php endfor; ?>
</nav>
<?php endif; ?>
「前へ」「次へ」のリンクが欲しい場合は、$paged - 1 と $paged + 1 のURLを同じ要領で組み立てれば作れます。
運用で気をつけたいこと
表示しないページ分のHTMLも生成される
この方式は、2ページ目を開いたときも1ページ目・3ページ目のHTMLを組み立てています(使わずに捨てることになります)。通常の記事の量であれば処理時間はごくわずかで、体感できる差は出ません。ただ、レイアウトの中に重い処理(外部データの取得や、関連記事のクエリなど)を入れる場合は、頭の片隅に置いておいてください。
パーマリンク設定
上のページ送りは /記事スラッグ/2/ という形のURLを前提にしています。パーマリンクが「基本」(?p=123)の場合は、add_query_arg( 'page', $i, get_permalink() ) のようにクエリ形式で組み立てる必要があります。
2ページ目以降が正しく表示されるか確認する
投稿の2ページ目以降のURLは、本文に <!--nextpage--> がなくても表示されるはずですが、テーマやプラグイン(キャノニカル制御やリダイレクト系)によっては1ページ目へ飛ばされることがあります。実装後は、実際に /記事スラッグ/2/ を開いて、次の3点を確認してください。
- 2ページ目が正しく表示される(1ページ目にリダイレクトされない)
- 存在しないページ番号(
/記事スラッグ/99/など)が404になる - 1ページ目に
/記事スラッグ/1/でアクセスしたときの挙動が意図どおりか
SEO面の注意
分割したページはそれぞれ別URLになります。2ページ目以降も title やキャノニカルが1ページ目と同じだと、重複と見なされる可能性があります。2ページ目以降のタイトルに「(2ページ目)」を付ける、キャノニカルを自ページのURLにする、といった調整を検討してください。そもそも分割する必要があるほど長い記事かどうか、というのも最初に考えておきたい点です。
まとめ
| 手順 | やること | 場所 |
|---|---|---|
| ① レイアウト作成 | 中身のない「改ページ」レイアウトを追加 | ACFのフィールドグループ |
| ② 配列に保存 | 行ごとのHTMLを $html .= で連結し、pagebreak で配列に入れる | functions.php |
| ③ 出力 | page クエリ変数のページだけ echo し、ページ送りを付ける | テンプレート(single.php など) |
柔軟コンテンツを「そのまま出力せず、いったん配列に溜めてから出力する」という考え方さえ押さえておけば、改ページ以外にも応用できます。たとえば、見出しレイアウトだけを集めて目次を作る、といったこともできるので、柔軟コンテンツで記事を組んでいる方はぜひ試してみてください。
