WordPressではトップページやアーカイブページ等、記事リストの一覧を表示しますが、その表示の仕方をもっと柔軟に出力できるよう、色々試してみました。
よくある2つのパターン
配布されているテーマでもよく見かける一般的なやり方は次の2種類です。
どちらもメインループ内に記述するWordPressのテンプレート関数です。上のthe_excerpt()は抜粋を設定していればそれを表示、なければ本文を指定された文字数分だけカットして表示します。ただこれは本文中の文字情報だけを取得するので、画像とか他のタグも一覧内で表示させておきたいと言うときは使えません。
そんなときは下のthe_content()を使用します。こちらは単純に本文を表示させる関数ですが、本文中にmoreタグ(続きを読む)があればそこで切ってくれます。これなら文字情報だけに限らず任意の場所で切り出すことが可能です。ただしmoreタグを設定してない記事では全ての内容を表示してしまうことになります。今から新しいホームページを始めるならいいかもしれませんが、過去にさかのぼって全ての記事にmoreタグを入れ直すのは正直面倒くさいです。
PHPで条件分岐
ということで、どちらでも対応できない場合は自分で条件分けをするしかありません。改めて今回の更新でやりたかった動作をまとめてみます。
①moreタグがある場合はmoreタグでカットして表示
②短い記事は全文表
③長い記事は適当なところでカットして表示
④リストではなく単一ページの場合は全文表示
|
<?php $content=$post->post_content; $main_content = get_extended($content)['main']; $more_content = get_extended($content)['extended']; ?> |
まずはメインループの中で本文全体を取得します。通常ならget_the_content()を使用するところですが、今回は$post->post_contentで呼び出したものをget_extended()に入れています。get_extended()はmoreタグの前後で本文をカットしてくれる関数で、moreタグ前を[‘main’]に後を[‘extended’]に入れられます。moreタグがなければ[‘extended’]は空になるので、$more_contentがあればmoreタグがあるよっていう条件分岐ができます。
次に本文が短いかどうかの判断はタグを除いた本文の文字数を数えます。
|
<?php $cut=200; if ( mb_strlen(strip_tags($content), 'UTF-8')>$cut) { } ?> |
strip_tags()でタグを外し、mb_strlen()でマルチバイト換算で文字数をカウントします。短ければ切らずに出力します。しかしここで問題になるのが、この指定した文字数を超えた場合にどこで切るのかと言うことです。文字だけじゃないので単純に文字数で切ってしまうとレイアウトが崩壊します。色々悩んでこんな感じにしました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
<?php $countp = substr_count ($main_content,'<p'); if ($countp>1) { $p = 0; $offset = 0; while($p < 2){ $pos = strpos($main_content, '<p', $offset); if($pos === false){ break; } else { $p = $p + 1; $offset = $pos + 1; } } $more_content = substr($main_content,$pos); $main_content = substr($main_content,0,$pos); } ?> |
多少長くなっていますが、ざっくりいうと2回めのpタグ、“<p”の位置を検索してそこで分けるという処理を行っています。
これでHTML的な構文が途切れずにきれいに本文を分けることが可能になりました。最終的に単一ページかどうかの判定も加えて出来たのが以下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
|
<?php $reedmore=false; $short=false; $cut=200; $content=$post->post_content; $main_content = get_extended($content)['main']; $more_content = get_extended($content)['extended']; $countp = substr_count ($main_content,'<p'); if ( empty($more_content) && mb_strlen(strip_tags($main_content), 'UTF-8')>$cut && $countp>1 && is_singular() === false ) { $reedmore = true; $p = 0; $offset = 0; while($p < 2){ $pos = strpos($main_content, '<p', $offset); if($pos === false){ break; } else { $p = $p + 1; $offset = $pos + 1; } } $more_content = substr($main_content,$pos); $main_content = substr($main_content,0,$pos); }elseif(empty($more_content)===false){ $reedmore = true; }elseif(mb_strlen(strip_tags($main_content), 'UTF-8')<=$cut ){ $short=true; } ?> <div class="entry-body"> <?php $main_content = apply_filters( 'the_content', $main_content ); $main_content = str_replace( ']]>', ']]>', $main_content ); echo $main_content; ?> </div> |
続きを読む~後半部分をアコーディオンにして、その場で展開
|
<?php if ($reedmore===true): ?> <input id="<?php the_ID(); ?>" type="checkbox" class="reedmore"> <label id="reedmore<?php the_ID(); ?>" class="reedmore" for="<?php the_ID(); ?>">続きを読む<span class="arrow">▼</span></label> <div class="entry-body more-content"> <?php $more_content = apply_filters( 'the_content', $more_content ); $more_content = str_replace( ']]>', ']]>', $more_content ); echo $more_content; ?> </div> <div class="reedmore">最後までご覧頂きありがとうございました!</div> <?php endif;?> |
最後に、続きがある場合のみページリンクを出力、してもいいのですが、スマホで続けて読みやすいように、アコーディオンのようにその場で続きを展開できるようにしました。inputタグ+labelをCSSだけで非表示から表示に切り替えられます。CSSも合わせたこの動作については別記事で解説します。
まとめ
条件さえはっきり定義できれば、投稿リストもPHPで自由に出力することが出来ます。ぜひお試しを。