BurgerEditorで作った記事内容を簡単に2箇所に分割表示して楽しむ手法

昨年の baserCMS Advent Calendar 2019に続き、今年もbaserCMS Advent Calendar 2020が始まってます。
さてその7日担当です。=[ 'ω' ] トウフー

baserCMS4のご紹介

baserCMSとは、CMS(Contents Management System)です。
世の中には、WordpressとかMovableTypeとかSHIRASAGIなどいろんなCMSがありまして、その中で、PHPのフレームワークであるCakePHPを用いてできてるCMSです。

baserCMS(ベーサーシーエムエス)とは、Webサイト制作プラットフォームとして最適な国産CMSです。

です。
baserCMS - 国産オープンソース!フリー(無料)でコンテンツ管理に強いCMS

プラグインとかテーマの配布・販売やってます。
baserマーケット

導入事例いっぱいあります。着々と増加中です。
baserCMS導入事例 | baser CMS』

それから、記事をブロック単位で作ることができる、ハンバーガみたいに積み上げていく感覚で簡単に記事作成ができるBurgerEditorというプラグインがあります。
BurgerEditor(baserCMS3対応)(baserCMS4対応) ダウンロード | baserマーケット

この記事で紹介すること

この記事では、baserCMS+BurgerEditorで記事を作るときに、記事の1編集領域でBurgerEditorを利用して作成した内容を、2箇所に分割表示する手法を紹介します。
どちらかと言えば開発者さん向けですが大丈夫。
少しがんばる気力があればたぶん運営者でもできちゃいます。

記事内容を2箇所に分割表示するってどういうこと?

1記事の編集領域は通常1つです。
なので、BurgerEditorで作成する記事内容も1つということになります。

■ 管理側の記事サンプル
sc_20201207191443.png
■ 公開側の表示サンプル
sc_20201207193443.png

記事編集領域の内容を2箇所に表示する、ということなので、
手っ取り早く固定ページテンプレートに、編集領域で書いた内容を反映する箇所を2つに増やします。
BcPage->content() を2箇所に書いちゃえば良いですね。

<?php $this->BcPage->content();?>
<section>
<hr>
※この部分はどのページでも表示される共通部分
<br>
 固定ページテンプレート(/theme/bc_sample/Pages/templates/default.php)に記載する
<hr>
</section>
<?php $this->BcPage->content(); ?>

結果、公開側はどうなるか?
もちろん編集領域で作成した同じ内容が2つ表示されます。
sc_20201207194540.png

やりたいことは「この見出しより下のブロック群は別の箇所に表示したい」なので、もちろんこれではNGです。
なので記事の編集内容を分割し、「この見出しより下のブロック群は別の箇所に表示したい」部分は、「※この部分はどのページでも表示される共通部分」より下の段に表示されるようにします。

分割するには規則が必要なので、今回はバーガーブロックの「独自class設定」を用いて、独自classに「bottom-content」が設定されたブロックは、共通部分の下段に表示されるようにしてみます。

パースして分割しちゃうのが手っ取り早い

必要なのは以下。
・A. 独自classに bottom-content が設定されていないブロックの一覧でできたHTML
・B. 独自classに bottom-content が設定されたブロックの一覧でできたHTML

ということでテーマヘルパを利用して必要なHTMLが取得できるようにします。
テーマヘルパについてはこちら。
テーマヘルパー https://basercms.net/functions/theme_helper
テーマヘルパに以下2つのメソッドを追加します。
※今回、テーマヘルパはbaserCMSインストール時に標準テーマとして入る bc_sample テーマに同梱されている ContestSampleHelper を利用している前提とします。
 ・/theme/bc_sample/Helper/ContestSampleHelper.php

	/**
	 * 指定した独自classを設定したブロック以外のブロックでできたHTMLを取得する
	 *
	 * @param string $content 記事編集領域の内容
	 * @return string
	 */
	public function getArticleCustomClassBlock($content, $className = '') {
		$contentList = [];

		$DOM = new DOMDocument;
		// 文字化けを防ぐ目的で mb_convert_encoding() を利用する
		$content = mb_convert_encoding($content, 'HTML-ENTITIES', 'UTF-8');
		// @はHTML記述に誤りがあった場合にエラー表示しないため
		@$DOM->loadHTML($content);
		$XPath = new DOMXPath($DOM);
		$blockList = $XPath->query('//div[@data-bgb]');

		if ($className) {
			foreach ($blockList as $node) {
				$hasTargetClass = false;
				$attributes = $node->attributes;
				foreach ($attributes as $attr) {
					if ($attr->name === 'class') {
						if(strpos($attr->value, $className) !== false){
							// class名に bottom-content が含まれている場合
							$hasTargetClass = true;
						}
					}
				}
				// 特定のclass名のみを持ったブロックを取得する
				if ($hasTargetClass) {
					$contentList[] = $DOM->saveHTML($node);
				} else {
					continue;
				}
			}
		} else {
			foreach ($blockList as $node) {
				$contentList[] = $DOM->saveHTML($node);
			}
		}

		unset($DOM, $blockList);
		return implode('', $contentList);
	}

	/**
	 * 指定した独自classを設定したブロック以外のブロックでできたHTMLを取得する
	 *
	 * @param string $content 記事編集領域の内容
	 * @return string
	 */
	public function getArticleExcludeCustomClassBlock($content, $className = '') {
		$contentList = [];

		$DOM = new DOMDocument;
		// 文字化けを防ぐ目的で mb_convert_encoding() を利用する
		$content = mb_convert_encoding($content, 'HTML-ENTITIES', 'UTF-8');
		// @はHTML記述に誤りがあった場合にエラー表示しないため
		@$DOM->loadHTML($content);
		$XPath = new DOMXPath($DOM);
		$blockList = $XPath->query('//div[@data-bgb]');

		if ($className) {
			foreach ($blockList as $node) {
				$hasTargetClass = false;
				$attributes = $node->attributes;
				foreach ($attributes as $attr) {
					if ($attr->name === 'class') {
						if(strpos($attr->value, $className) !== false){
							// class名に bottom-content が含まれている場合
							$hasTargetClass = true;
						}
					}
				}
				// 特定のclass名のみを持ったブロックを取得する
				if ($hasTargetClass) {
					continue;
				} else {
					$contentList[] = $DOM->saveHTML($node);
				}
			}
		} else {
			foreach ($blockList as $node) {
				$contentList[] = $DOM->saveHTML($node);
			}
		}

		unset($DOM, $blockList);
		return implode('', $contentList);
	}

次に、ページテンプレート側では内容を以下のように変えます。

<?php
ob_start();
$this->BcPage->content();
$content = ob_get_contents();
ob_end_clean();

echo $this->ContestSample->getArticleExcludeCustomClassBlock($content, 'bottom-content');
?>
<section>
<hr>
※この部分はどのページでも表示される共通部分
<br>
 固定ページテンプレート(/theme/bc_sample/Pages/templates/default.php)に記載する
<hr>
</section>

<?php
ob_start();
$this->BcPage->content();
$content = ob_get_contents();
ob_end_clean();

echo $this->ContestSample->getArticleCustomClassBlock($content, 'bottom-content');

BcPage->content() では、内部的にechoするメソッドなので、出力されている内容をバッファして保持することで、表示内容のHTMLを加工するための文字列として取ってます。
あとは記事内容であるブロックを加工したHTMLを表示するだけです。

結果はこんな感じになりました。
画像4つブロックと2カラムブロックは「この見出しより下のブロック群は別の箇所に表示したい」の下段に移動し、上段のブロック群には表示されていません。

sc_20201207232340.png

上記の仕組みを用いることで、デザイン・HTMLの構造上、記事をどうしても分割表示しなければならないときにも対応できます。
別で入力欄を作るとか、code欄を用いる等の手法はいろいろありますが、バーガエディタのブロックは追加も編集も簡単なので、このブロックは活かしたまま、容易に分割する手法を考えてみました。

あとがき

画像内で「画像4列リンク・テキスト付」ブロックなのに、5列と書いてしまってるのにあとから気づいたけどもういいやってなってそのままなのです。

▲ to Top

▲ to Top