get_the_content()でmoreがあっても全文を取得をしたい場合があったので解析と代替解決を行った
表題通り、get_the_content()でmoreがある際でも、特定のテンプレートのときにだけ全文取得を行いたかったのができずに少しハマった。
具体的に問題となったのは、検索ページ(search.phpで吐き出されるやつ)。
カテゴリーとかアーカイブ等ではmoreは必要だが、検索ページだけはこれを例外として全文取得したい。
で、全文から文字列を切り出した省略文を自前で作ろうとして、get_the_content()の第二引数である$stripteaserを設定して出そうとした。
しかし、それが効かず、WYSIWYGで突っ込んだmore(<!–more—>のこと)までの文字列しか出てこない。
$stripteaserがあれば全文の取得が強制的にできるんじゃないのかよ!とツッコミながら、同じような問題を探してみたが、俺の検索力では同じような問題に悩まされた人は見つけられなかった。
…そもそも検索ページでmoreを付けたget_the_content()を全文で取得するのが稀有なのだろうか?
まぁ確かに大抵の制作なら、面倒なのでたとえ検索ページだろうが単にthe_content()とかthe_excerpt()とかで済まそうとするけど・・・。
ともあれ、このまま分からない状態にしとくのも何か拙いし納得できないので、やむなくpost-template.phpを開いた。
コメントアウトでメモをしながら当該の部分を抜粋して解析。
解析内容としては以下になる。
関係ないところは省略してるものの、ちょっとだけ長い。
function get_the_content( $more_link_text = null, $strip_teaser = false ) { // // ここでちょっと大事なのはグローバル変数の$more // global $page, $more, $preview, $pages, $multipage; ~~あまり関係ないので略~~ $output = ''; $has_teaser = false; ~~あまり関係ないので略~~ // // ※1 $matchesは<!--more(.*?)?-->そのものと、<!--more(.*?)?-->が現れた文字位置を表す配列 // <!--more(.*?)?-->が存在する限りは必ずこの条件を通る // if ( preg_match( '/<!--more(.*?)?-->/', $content, $matches ) ) { // // ※2 $matches[0](つまり<!--more(.*?)?-->のこと)で最大数2の配列へ$contentを分割。 // $content[0]には<!--more(.*?)?-->までの文字列、$content[1]にはそれ以降の文字列が入る // $content = explode( $matches[0], $content, 2 ); if ( ! empty( $matches[1] ) && ! empty( $more_link_text ) ) $more_link_text = strip_tags( wp_kses_no_null( trim( $matches[1] ) ) ); $has_teaser = true; } else { $content = array( $content ); } if ( false !== strpos( $post->post_content, '<!--noteaser-->' ) && ( ! $multipage || $page == 1 ) ) $strip_teaser = true; // // ※3 <!--more(.*?)?-->が現れるまでの文字列 // $teaser = $content[0]; // // ※4 グローバル変数$moreは通常は投稿タイプと固定ページ用でこの際にTrueを返し、検索ページやアーカイブ等の場合はFalseとなる。 // $strip_teaserをfalseにしても、検索ページやアーカイブ等では$moreは常にFalseなのでこの条件は絶対に通らない。 // つまり本件にはこの条件はあまり意味なし。 // if ( $more && $strip_teaser && $has_teaser ) $teaser = ''; // // ※5 ※3で$teaserに入っていたのは<!--more(.*?)?-->が現れるまでの文字列。 // 以下の$outputに$content[1]は入っていない。 // $output .= $teaser; if ( count( $content ) > 1 ) { // // ※6 検索ページやアーカイブ等の場合はFalse。強制的に※7へ。 // if ( $more ) { $output .= '<span id="more-' . $post->ID . '"></span>' . $content[1]; // // ※7 ※6で$moreにFalseが入っているので、すなわち検索ページやアーカイブ等は必ずこちらの条件を通る // } else { if ( ! empty( $more_link_text ) ) /** ~~WPコメント表記省略~~ */ $output .= apply_filters( 'the_content_more_link', ' <a href="' . get_permalink() . "#more-{$post->ID}\" class=\"more-link\">$more_link_text</a>", $more_link_text ); $output = force_balance_tags( $output ); } } if ( $preview ) // Preview fix for JavaScript bug with foreign languages. $output = preg_replace_callback( '/\%u([0-9A-F]{4})/', '_convert_urlencoded_to_entities', $output ); // // 結局最後まで検索ページやアーカイブ等で$content[1]は追加されなかった… // return $output; }
コメントアウトで書き出している通り、検索ページやアーカイブ等では$strip_teaserを付けてもその設定に何ら意味がないことがわかる。
まぁよく考えてみるとそれもそのはずだよね。
そももそも、検索ページもアーカイブページと言えばアーカイブページだ。
そのアーカイブページでmoreを無視して全文取得して仮に表示するとしたら、本来、記事の概要に相当するmoreまでの文章が何ら意味を持たなくなるもんね。
とはいえ、本来の意味でのアーカイブページというわけでもないので、記事の冒頭に記載した要件を満たすために、検索ページだけはせめて例外としておきたい。
…解析結果から結論をいうと、検索ページで全文から文字列を切り出した省略文を自前で作ろうとする場合、get_the_content()ではその要件を実現することはほぼ不可能である。
WordPressのアプデをガン無視することを前提でget_the_content()そのもののコードを書き換える度胸があればまた別だけど。
この問題を解決するためには、何が何でもget_the_content()にこだわって取得するのではなく、以下のようなコードで代替解決を計るべきだ。
代替コード
function theme_get_content() { $string = get_post(); $string = $string->post_content; // 以下は文字列の置換 // 字数を付けるならtheme_get_contentに引数を付けて指定できるようにするといいかも // $string = strip_tags( preg_replace( '/\r\n|\n|\r|\t/', '', $string ) ); // $string = mb_substr( $string, 0, 255 ); return $string; } echo theme_get_content();
get_post()で投稿情報を呼び出して、post_contentで本文全体を取得。
後は取得した本文全体をstr_replace()やpreg_replace()などで整形してやればいい。
これで、get_the_content()にあるmoreを気にせずに要件通りのコードが書ける。
本日は遅いのでこんなところで。
※2018/04/03 追記
よくよく見たら記事のお題や冒頭の言い回しがとても語弊のある書き方だったので変更しました(´・ω・`)