本文内の見出し(H1~H6)を階層配列として保存する方法【WordPress】

本文内の見出し(H1~H6)を階層配列として保存する方法【WordPress】



WordPressで本文に表示する目次用にH1~H6の見出しを一旦階層を持つ配列にして保存しておく方法を熟考しました。

まずは本文中から見出しを取得して、一時的に配列に保存しておきます。

//--------------------------------------------------------.
$content     = get_the_content();
$heading_ary = array();
$paged_ary   = explode( '<!--nextpage-->', $post->post_content );
$i           = 1;
foreach ( $paged_ary as $page_content ) {
	if ( preg_match_all( '/^<h([1-6])(.*?)>(.*?)<\/h([1-6])>/im', $page_content, $search, PREG_SET_ORDER ) ) {
		if ( $search ) {
			foreach ( $search as $val ) {
				$label               = wp_strip_all_tags( $val[3], true );
				$element             = (int)$val[1];
				$ary                 = array(
					'element' => $element,
					'label'   => $label,
				);
				$heading_ary[ $i ][] = $ary;
				$content             = str_replace( $val[0], '<h' . $element . $val[2] . ' id="' . $label . '">' . $val[3] . '</h' . $element . '>', $content );
			}
		}
		$i++;
	}
}
global $g_heading_list;
$g_heading_list = $heading_ary;
//--------------------------------------------------------.

解説:

  • まずはget_the_content()で本文を取得しておきます。
  • 次にページ分割を考慮して、$post->post_contentの未処理本文を取得して<!--nextpage-->で配列に分割します。
  • 各ページごとにH1~H6までの見出しを取得して、$label(HTMLタグを除いた見出しテキスト)と$element(見出し級数)を配列にして保存します。
  • $contentの方も各見出しにアンカーリンク用のIDプロパティ$labelを追加しておきます。
  • ショートコードで使うことを想定して、一旦グローバル変数に格納しておきます。

この状態で仮に以下のような本文であれば、

<h2>はじめに</h2>
...
<h2>目的</h2>
...
<h2>内容</h2>
...
<h3>環境調査</h3>
...
<h3>屋内実験</h3>
...
<!--nextpage-->
<h2>結果</h2>
...
<!--nextpage-->
<h2>参考文献</h2>
...

見出し配列は、

Array
(
    [1] => Array
        (
            [0] => Array
                (
                    [element] => 2
                    [label] => はじめに
                )
            [1] => Array
                (
                    [element] => 2
                    [label] => 目的
                )
            [2] => Array
                (
                    [element] => 2
                    [label] => 内容
                )
            [3] => Array
                (
                    [element] => 3
                    [label] => 環境調査
                )
            [4] => Array
                (
                    [element] => 3
                    [label] => 屋内実験
                )
        )
    [2] => Array
        (
            [0] => Array
                (
                    [element] => 2
                    [label] => 結果
                )

        )

    [3] => Array
        (
            [0] => Array
                (
                    [element] => 2
                    [label] => 参考文献
                )
        )
)

のように取得出来ます。
この時点ではまだ各見出し要素同士が階層に別れていないので、もう一つ作業を追加します。

global $g_heading_list;
$i           = 1;
$top_element = $g_heading_list[1][0]['element'];
foreach ( $g_heading_list as $index ) {
	$page_list   = array();
	$index       = array_reverse( $index, true );
	$pre_ary     = array();
	$pre_element = '' ;
	foreach ( $index as $val ) {
		$element = $val['element'];
		$label   = $val['label'];
		$key     = array(
			'label' => $label,
			'child' => array(),
		);
		if ( $top_element === $element ) {
			if ( $pre_ary ) {
				$key['child'] = $pre_ary;
				$pre_ary      = array();
			}
			array_unshift( $page_list, $key );
		} else {
			if ( ! $pre_element || $pre_element === $element ) {
				array_unshift( $pre_ary, $key );
			} elseif ( $pre_element > $element ) {
				$key['child'] = $pre_ary;
				$pre_ary      = array();
				array_unshift( $pre_ary, $key );
			}
		}
		$pre_element = $element;
	}
	$list[ $i ] = $page_list;
	$i++;
}

解説:

  • まず目次配列を保存しているグローバル変数を呼び出します。
  • 目次配列の一番上の見出し要素の級数を取得して($g_heading_list[1][0]['element'])、これを基準とします。
  • ページ毎に配列を回します。
  • 一番最後から順に階層に分けるために、array_reverse()で配列を逆にします。
  • 見出し要素から見出しテキストテキストを取り出して読み込む用の配列にします($key)。
  • 最上位級数と同階層であるか、前の処理時の階層かに応じて、子階層に相当するならば$key'child'に格納します。
  • 最終的に$list配列に格納します。

これで以下のような階層付きの配列を取得することが出来ました。

Array
(
    [1] => Array
        (
            [0] => Array
                (
                    [label] => はじめに
                    [child] => Array
                        (
                        )
                )
            [1] => Array
                (
                    [label] => 目的
                    [child] => Array
                        (
                        )
                )
            [2] => Array
                (
                    [label] => 内容
                    [child] => Array
                        (
                            [0] => Array
                                (
                                    [label] => 環境調査
                                    [child] => Array
                                        (
                                        )
                                )
                            [1] => Array
                                (
                                    [label] => 屋内実験
                                    [child] => Array
                                        (
                                        )
                                )
                        )
                )
        )
    [2] => Array
        (
            [0] => Array
                (
                    [label] => 結果
                    [child] => Array
                        (
                        )
                )
        )
    [3] => Array
        (
            [0] => Array
                (
                    [label] => 参考文献
                    [child] => Array
                        (
                        )
                )
        )
)

解説はかなり適当に端折ってます。
あと、コード内で!や[、]が文字化けしたので全角文字にしています。

今日はここまで

関連する記事



こちらはいかが?


コメントを残す