2016/10/18 21:31:54

wordpressで投稿内容(記事本文)からhタグを取得し目次として使用する

目次(クリックするとジャンプします)
  • 1:投稿内容(記事本文)の取得
  • 2:マッチング
  • 2.1:preg_match_allを使う
  • 3:正規表現
  • 3.1:<h>タグを検索するための正規表現
  • 3.2:idやクラスを用いた場合
  • 4:を取得する
  • 4.1:関数の定義
  • 4.2:テンプレートファイルで使用する
  • 5:取得したタグを加工する
  • 5.1:ナンバリング
  • 5.2:置き換えでナンバリングを実現
  • 5.3:ナンバリングで階層を表現
  • 5.4:ナンバリング機能を付けた関数の定義
  • 6:まとめ

投稿内容(記事本文)の取得

目次を提示したいときなどに記事内のタグを取得したいときがあります。プラグインを使う手もありますが、自力でもそれほど難しくはないです。今回は記事内のhタグを取得する方法を紹介したいです。

wordpressでの投稿内容は the_content()で取得できるが、今回は投稿内容を加工する必要がありますので、投稿情報が格納されるグローバル変数$postから投稿内容(記事本文)を取得することになります。

投稿内容は$postpost_contentというメンバ変数に格納されています。ループ内で以下の記述で取得できます。

$content = $post->post_content;

この記事本文には<p>タグ<img>タグなど、挿入したタグもそのままの形で入っています。もちろんタグも入っています。

マッチング

preg_match_allを使う

$post->post_content中から、タグを選別するためにはPHPの関数であるpreg_match_allを使う。これは文字列の中から任意の検索条件にヒットする文字列をすべて取得する関数です。具体的には以下の様に記述することになります。

preg_match_all('/<h[1-6]>.+<\/h[1-6]>/u', $post->post_content, $matches);

/<h[1-6]>.+<\/h[1-6]>/uは後述するがタグを取得する為の検索条件。これを用いて$post->post_contentからタグを取得していきます。取得したタグは$matchesに格納されていきます。

正規表現

<h>タグを検索するための正規表現

任意の検索条件とは「正規表現」と呼ばれる方法で条件指定を行う。先ほどの/<h[1-6]>.+<\/h[1-6]>/uが正規表現で記述された検索条件です。

正規表現はなかなかに難しく、すべてを語り倒せるほどの知識は@MINOにはないので、とりあえず今回の正規表現の意味を説明したいと思います。

<h[1-6]><h1><h2><h3><h4><h5><h6>にヒットします。 [1-6]123456のどれか一文字という意味となります。.(ドット)は改行を除く任意の一文字、+(プラス)は一回以上の繰り返しを意味します。

なので.+は任意の一文字以上の文字列を意味することになります。つまりタグで囲まれた見出し本文に当たります。

<\/h[1-6]>は終了タグ</h1></h2></h3></h4></h5></h6>にヒットします。 \(バックスラッシュ)があるがこれは/(スラッシュ)をエスケープする為の記号となります。

前後に/(スラッシュ)がありますが、これはデリミタと呼ばれるもので、ここから正規表現が始まり、ここで終わりですよという意味になります。末尾のuはマルチバイト(UTF-8)対応させるために必要になります。

この正規表現で投稿内容(記事本文)の<h>タグすべてを取得できます。

idやクラスを用いた場合

@MINOは記事内で<h>タグidクラスを使用はせず、CSSで親子関係でスタイルを指定していますが、idクラスを必要としている方もいるでしょう。

タグにidクラスを用いる際はこの正規表現ではヒットさせる事はできないです。

/<h[1-6]\s.+>.+<\/h[1-6]>/u

のように開始タグに.+を付け加えると

<h1 id="hoge">これはh1です</h1> <h1 class="hoge">これはh1です</h1>

などにヒットさせることができます。

タグになんらかの属性を用いる場合に参考にしてほしいです。

実際に<h>タグを取得する

関数の定義

さて、基本的な内容を説明したうえで具体例に入りたいと思います。テンプレートファイルに直接処理を書き込んでも構わないですが、やはりfunctions.phpに関数として定義するのが良いかと思います。<h>タグを取得する関数は以下の様になります。

//functions.phpに定義する関数
function get_index() {
    //グローバル変数を使う為の宣言
    global $post;
    //マッチングで<h>タグを取得する
    preg_match_all('/<h[1-6]>.+<\/h[1-6]>/u', $post->post_content, $matches);
    //取得した<h>タグの個数をカウント
    $matches_count = count($matches[0]);
    if(empty($matches)){
        //<h>タグがない場合の出力
        echo '<span>Sorry no index</span>';
    }else{
        //<h>タグが存在する場合に<h>タグを出力
        for ($i = 0; $i < $matches_count; $i++){
            echo  $matches[0][$i];
        }
    }     
}

preg_match_allを使って記事本文から<h>タグをすべて取得し、取得した個数分出力するという処理です。

テンプレートファイルで使用する

ここで定義した関数をテンプレートファイルで使用することで<h>タグが出力されます。 以下はsingle.phpでの使用例です。

<?php get_header(); ?>
<div id="container">
        <?php if (have_posts()) : the_post();?>
            <h1 class="title_h1">
                <?php echo the_title(); ?>
            </h1>
            <div id="index">
                <?php get_index(); //ここで<h>を出力する?> 
            </div>
            <div id="content">
                 <?php echo the_content(); ?>
            </div>
        <?php endif; ?>
    <?php get_sidebar(); ?>
</div>
<?php get_footer(); ?>

例えば<h2>タグだけ取得したい場合は正規表現を/ <h2\s.+>.+<\/h2>/uとして<h2>タグのみヒットするようにすることも可能です。

取得したタグを加工する

ナンバリング

抽出した<h>タグを目次として使いたいとき、数字を付けたい場合があります。

元 → <h1>これはh1です</h1> ナンバリング → <h1>1.これはh1です</h1>

もとの<h>タグ内でナンバリングする方法もあるだろうが、目次用途の時だけナンバリングしたい場合は具合が悪いです。関数に少し処理を付け加えてナンバリングを実現してみます。

置き換えでナンバリングを実現

加工にはpreg_replaceを使いたいです。正規表現でヒットした文字列を指定した文字列に置き換える関数です。いくつかナンバリングの方法は考えられると思いますが、今回は開始タグを置き換える方法で実現してみます。 以下の様なイメージです。

元 → <h1> 置き換え後 → <h1>1.

ナンバリングで階層を表現

ただやみくもにナンバリングすると、<h1>にも<h2>にも連番で数字が付いてしまいます。各タグごとのナンバリングはもちろんのこと、上位タグに切り替わった際、数字をリセットする必要があります。その為、関数では<h1>から<h6>までの場合に分けて処理をする形にしています。

各if文内でカウント用の変数を1にリセットすることで階層を表現しています。たとえば<h2>の下に<h3>がある場合、それは<h2>に対しての小見出しとなると思います。

その為、新しい<h2>の下の<h3>とナンバリングが続いているのはおかしいです。他のタグも同様です。その為上位のタグが出現する度に下位のタグのナンバリングをリセットする必要があります。

<h1>なら2~6、<h2>なら3~6をリセットすれば階層を考慮したナンバリングをすることが可能になります。

ナンバリング機能を付けた関数の定義

コードはすこし長くなりますが、ナンバリング機能を付けた関数を定義してみましましました。入れ子が多重なのであまりいい例ではないかもしれないですが、一応用は足す関数になっています。

//functions.phpに定義する関数ナンバリングあり
function get_index_with_number() {
    //グローバル変数を使う為の宣言
    global $post;
    //ナンバリング用のカウント
    $h1_count = 1;
    $h2_count = 1;
    $h3_count = 1;
    $h4_count = 1;
    $h5_count = 1;
    $h6_count = 1;
    //マッチングで<h>タグを取得する
    preg_match_all('/<h[1-6]>.+<\/h[1-6]>/u', $post->post_content, $matches);
    //取得した<h>タグの個数をカウント
    $matches_count = count($matches[0]);
    if(empty($matches)){
        //<h>タグがない場合の出力
        echo '<span>Sorry no index</span>';
    }else{
        //<h>タグが存在する場合に<h>タグを出力
        for ($i = 0; $i < $matches_count; $i++){
            //取得したタグがどれであるかを判断するため開始タグだけ取り出す
            preg_match('/<h[1-6]>/u', $matches[0][$i] , $matches2);
            //<h1>タグの場合
            if($matches2[0] == "<h1>"){
                //開始タグを<h1>から<h1>1.のように書き換える
                $number_h = preg_replace('/<h[1-6]>/u', $matches2[0] . $h1_count .".", $matches[0][$i]);
                //<h1>用のカウント送り
                $h1_count++;
                //他のカウントをリセットする
                $h2_count = 1;
                $h3_count = 1;
                $h4_count = 1;
                $h5_count = 1;
                $h6_count = 1;
                echo  $number_h;
            //<h2>タグの場合
            }else if($matches2[0] == "<h2>"){
                //開始タグを<h2>から<h2>1.のように書き換える
                $number_h = preg_replace('/<h[1-6]>/u', $matches2[0] . $h2_count .".", $matches[0][$i]);
                //<h2>用のカウント送り
                $h2_count++;
                //下位のタグのナンバリングカウントをリセットする
                $h3_count = 1;
                $h4_count = 1;
                $h5_count = 1;
                $h6_count = 1;
                echo  $number_h;
            //<h3>タグの場合
            }else if($matches2[0] == "<h3>"){
                //開始タグを<h3>から<h3>1.のように書き換える
                $number_h = preg_replace('/<h[1-6]>/u', $matches2[0] . $h3_count .".", $matches[0][$i]);
                //<h3>用のカウント送り
                $h3_count++;
                //下位のタグのナンバリングカウントをリセットする
                $h4_count = 1;
                $h5_count = 1;
                $h6_count = 1;
                echo  $number_h;
            //<h4>タグの場合
            }else if($matches2[0] == "<h4>"){
                //開始タグを<h4>から<h4>1.のように書き換える
                $number_h = preg_replace('/<h[1-6]>/u', $matches2[0] . $h4_count .".", $matches[0][$i]);
                //<h4>用のカウント送り
                $h4_count++;
                //下位のタグのナンバリングカウントをリセットする
                $h5_count = 1;
                $h6_count = 1;
                echo  $number_h;
            //<h5>タグの場合
            }else if($matches2[0] == "<h5>"){
                //開始タグを<h5>から<h5>1.のように書き換える
                $number_h = preg_replace('/<h[1-6]>/u', $matches2[0] . $h5_count .".", $matches[0][$i]);
                //<h5>用のカウント送り
                $h5_count++;
                //下位のタグのナンバリングカウントをリセットする
                $h6_count = 1;
                echo  $number_h;
            //<h6>タグの場合
            }else if($matches2[0] == "<h6>"){
                //開始タグを<h6>から<h6>1.のように書き換える
                $number_h = preg_replace('/<h[1-6]>/u', $matches2[0] . $h6_count .".", $matches[0][$i]);
                //<h6>用のカウント送り
                $h6_count++;
                echo  $number_h;
            } 
        }
    }     
}

ナンバリングに限らず、なんらかの加工を試みる場合に参考にしていただけるのではないかと思います。

まとめ

本文から任意にタグを取得したりする処理はwordpressを使っていると意外と欲しくなるときが多いです。記事の内容などを抜粋する場合、抜粋文だけでなく、見出しや画像などを抽出して用いるとコンテンツの構築に選択肢が増えるのではないかと思います。

もちろんプラグインを使う手もありますが、自分で作ってみる事でwordpressPHPの理解が深まるのでお勧めです。