baserCMS 4系テーマの5系化(step 2) - 「Omotenashi2」テーマヘルパーを作ってみる。

以前にOmotenashi2テーマのテーマヘルパーを提案した経緯(baserCMS 「Omotenashi-2」 テーマヘルパーを使ってみては?!という提案) はさておき、「4系テーマの5系化」ステップ2は、この時に提案したテーマヘルパーを今回は5系にマイグレートしてみるという目論みです。ですが、理由は後述しますが、5系対応「Omotenashi2」テーマヘルパーを作ってみよう、ということにどうもなりそうです。
ひとまず、テーマヘルパーファイルについて、先の4系の記事から復習も兼ねて若干のまとめ。
このテーマは、トップページのスライダー用のHTML出力を変更する目的で、BcBaserHelper.phpをコアファイルからテーマフォルダ内にコピー、改修して利用されています。テーマ自体のリリース時期が古いため、最近の4系baserCMS(4.7.3以降)では、システム要件がphp8.1に対応したこともあって、更新されないテーマフォルダ内のBcBaserHelper.phpが別のエラーを起こしている状態です。
そこで、BcBaserHelper.phpをコアファイルからテーマフォルダ内にコピーするのではなく、代わりにテーマ(独自)ヘルパーを作ってみてはどうか、というのが前回の提案でした。
5系のシステム要件も、php8.1以降ということなので尚更です。
前回の4系の場合、手順としては、
- 不具合を生じている
theme/omotenashi2/Helper/BcBaserHelper.php
ファイルを削除し、代わりに作成したテーマヘルパー(Omotenashi2Helper.php)を作成し、theme/omotenashi2/Helper/Omotenashi2Helper.php
の位置に配置する。 - その上で、レイアウトファイル(
theme/omotenashi2/Layouts/default.php
)上のmainImage()関数の呼び出し箇所をBcBaser->mainImage()からOmotenashi2->mainImage()に変更する。
というものでした。
詳細
さて、ここから今回の本題、5系化の対応と手順です。
5系では、BcBaserHelper.phpは、/vendor/baserproject/baser-core/src/View/Helper/BcBaserHelper.php
にあるのですが、改めてBcBaserHelper.phpのコードを紐解いてみると、管理画面内の「テーマ設定」に係る部分、つまりmainImage()やlogo()などのメソッドは、bc-theme-configという名称でプラグイン化されていて、/vendor/baserproject/bc-theme-config/src/View/Helper/BcThemeConfigHelper.php
)に継承されていました。
なので、このBcThemeConfigHelper.phpをベースにテーマヘルパーのOmotenashi2Helper.phpを作成することになります。
テーマヘルパーは、(5系テーマガイド - 「テーマヘルパー」)を参考に、前回と同様に「Omotenashi-2」テーマで改修されていたBcBaserHelper.phpのmainImage()関数のHTML出力部分のコードをサンプルにしつつ、以下のように書き直します。
修正箇所は、「修正箇所:」のコメントアウトをしていますので、オリジナルのBcThemeConfigHelper.phpと比較してご確認ください。
<?php
/**
* baserCMS : Based Website Development Project <https://basercms.net>
* Copyright (c) NPO baser foundation <https://baserfoundation.org/>
*
* @copyright Copyright (c) NPO baser foundation
* @link https://basercms.net baserCMS Project
* @since 5.0.0
* @license https://basercms.net/license/index.html MIT License
*/
namespace Omotenashi2\View\Helper; /* 修正箇所:*/
use BaserCore\Utility\BcUtil;
use Cake\Core\Plugin;
use Cake\ORM\TableRegistry;
use Cake\View\Helper;
use BaserCore\Annotation\UnitTest;
use BaserCore\Annotation\NoTodo;
use BaserCore\Annotation\Checked;
/**
* 修正箇所:BcBaserHelperを継承して、BcThemeConfigHelperに倣ってテーマ独自のメインイメージ用のタグを出力する
* テーマ内のみで使用: $this->Omotenashi2->mainImage()
*/
#[\AllowDynamicProperties]
class Omotenashi2Helper extends Helper
{
/**
* Helper
*
* @var string[]
*/
public array $helpers = [
'BaserCore.BcBaser'
];
/**
* メインイメージを出力する
*
* メインイメージは管理画面のテーマ設定にて指定
*
* @param array $options オプション
* - `all`: 全ての画像を出力する。
* - `num`: 指定した番号の画像を出力する。all を true とした場合は、出力する枚数となる。
* - `id` : all を true とした場合、UL タグの id 属性を指定できる。
* - `class` : all を true とした場合、UL タグの class 属性を指定できる。
* ※ その他の、パラメーターは、 BcBaserHelper->getThemeImage() を参照
* @return void
* @checked
* @noTodo
* @unitTest
*/
/* 修正箇所: ul、liタグをdivタブに変更し、class追加。*/
public function mainImage($options = [])
{
$options = array_merge([
'num' => 1,
'all' => false,
'id' => 'MainImage',
'class' => false
], $options);
if ($options['all']) {
$id = $options['id'];
$class = $options['class'];
$num = $options['num'];
unset($options['all']);
unset($options['id']);
unset($options['class']);
$tag = '';
for($i = 1; $i <= $num; $i++) {
$options['num'] = $i;
$themeImage = $this->getThemeImage('main_image', $options);
if ($themeImage) {
$tag .= '<div class="' . $class . '__item">' . $themeImage . '</div>' . "\n";
}
}
$ulAttr = '';
if ($id !== false) {
$ulAttr .= ' id="' . $id . '"';
}
if ($class !== false) {
$ulAttr .= ' class="' . $class . '"';
}
echo '<div' . $ulAttr . '>' . "\n" . $tag . "\n" . '</div>';
} else {
echo $this->getThemeImage('main_image', $options);
}
}
/**
* テーマ画像を取得する
*
* @param string $name テーマ画像名( log or main_image )
* @param array $options オプション(初期値 :array())
* - `num` : main_imageの場合の番号指定(初期値 : '')
* - `thumb`: サムネイルを取得する(初期値 : false)
* - `class`: 画像に設定する class 属性(初期値 : '')
* - `popup`: ポップアップリンクを指定(初期値 : false)
* - `alt` : 画像に設定する alt 属性。リンクの title 属性にも設定される。(初期値 : テーマ設定で設定された値)
* - `link` : リンク先URL。popup を true とした場合、オリジナルの画像へのリンクとなる。(初期値 : テーマ設定で設定された値)
* - `maxWidth : 最大横幅(初期値 : '')
* - `maxHeight: 最大高さ(初期値 : '')
* - `width : 最大横幅(初期値 : '')
* - `height: 最大高さ(初期値 : '')
* - `noimage:
* - `output:
* @return string $tag テーマ画像のHTMLタグ
* @checked
* @noTodo
*/
public function getThemeImage($name, $options = [])
{
$themeConfigsTable = TableRegistry::getTableLocator()->get('BcThemeConfig.ThemeConfigs');
$data = $themeConfigsTable->getKeyValue();
$url = $imgPath = $uploadUrl = $uploadThumbUrl = $originUrl = '';
$thumbSuffix = '_thumb';
$dir = WWW_ROOT . 'files' . DS . 'theme_configs' . DS;
$themeDir = Plugin::path(BcUtil::getCurrentTheme());
$imgDir = $themeDir . 'webroot' . DS . 'img' . DS;
$num = '';
if (!empty($options['num'])) {
$num = '_' . $options['num'];
}
$options = array_merge([
'thumb' => false,
'class' => '',
'popup' => false,
'alt' => $data[$name . '_alt' . $num]?? null,
'link' => $data[$name . '_link' . $num]?? null,
'maxWidth' => '',
'maxHeight' => '',
'width' => '',
'height' => '',
'noimage' => '', // 画像がなかった場合に表示する画像
'output' => '', // 出力タイプ tag ,url を指定、未指定(or false)の場合は、tagで出力(互換性のため)
], $options);
$name = $name . $num;
if (!empty($data[$name])) {
$pathinfo = pathinfo($data[$name]);
$uploadPath = $dir . $data[$name];
$uploadThumbPath = $dir . $pathinfo['filename'] . $thumbSuffix . '.' . $pathinfo['extension'];
$uploadUrl = '/files/theme_configs/' . $data[$name];
$uploadThumbUrl = '/files/theme_configs/' . $pathinfo['filename'] . $thumbSuffix . '.' . $pathinfo['extension'];
}
if (!empty($data[$name])) {
if (!$options['thumb']) {
if (file_exists($uploadPath)) {
$imgPath = $uploadPath;
$url = $uploadUrl;
}
} else {
if (file_exists($uploadThumbPath)) {
$imgPath = $uploadThumbPath;
$url = $uploadThumbUrl;
}
}
$originUrl = $uploadUrl;
}
if (!$url) {
$exts = ['png', 'jpg', 'gif'];
foreach($exts as $ext) {
if (file_exists($imgDir . $name . '.' . $ext)) {
$url = BcUtil::getCurrentTheme() . '.' . $name . '.' . $ext;
$imgPath = $imgDir . $name . '.' . $ext;
$originUrl = $this->BcBaser->Url->assetUrl(BcUtil::getCurrentTheme() . '.img/' . $name . '.' . $ext);
}
}
}
// noimage が設定されていれば、画像がなくても処理を続ける
if (!$url) {
if ($options['noimage']) {
$url = $options['noimage'];
} else {
return '';
}
}
// outputがURLなら、URLを返す
if ($options['output'] == 'url') {
return $url;
}
$imgOptions = [];
if ($options['class']) {
$imgOptions['class'] = $options['class'];
}
if ($options['alt']) {
$imgOptions['alt'] = $options['alt'];
}
if ($options['maxWidth'] || $options['maxHeight']) {
$imginfo = getimagesize($imgPath);
$widthRate = $heightRate = 0;
if ($options['maxWidth']) {
$widthRate = $imginfo[0] / $options['maxWidth'];
}
if ($options['maxHeight']) {
$heightRate = $imginfo[1] / $options['maxHeight'];
}
if ($widthRate > $heightRate) {
if ($options['maxWidth'] && $imginfo[0] > $options['maxWidth']) {
$imgOptions['width'] = $options['maxWidth'];
}
} else {
if ($options['maxHeight'] && ($imginfo[1] > $options['maxHeight'])) {
$imgOptions['height'] = $options['maxHeight'];
}
}
}
if ($options['width']) {
$imgOptions['width'] = $options['width'];
}
if ($options['height']) {
$imgOptions['height'] = $options['height'];
}
$tag = $this->BcBaser->getImg($url, $imgOptions);
if ($options['link'] || $options['popup']) {
$linkOptions = [];
if ($options['popup']) {
$linkOptions['rel'] = 'colorbox';
$link = $originUrl;
} elseif ($options['link']) {
$link = $options['link'];
if (!empty($this->_View->getRequest()->getAttribute('currentSite')->alias)) {
if (empty($this->_View->getRequest()->getAttribute('currentSite')->same_main_url)) {
$link = '/' . $this->_View->getRequest()->getAttribute('currentSite')->alias . $link;
}
}
}
if ($options['alt']) {
$linkOptions['title'] = $options['alt'];
}
$linkOptions['escapeTitle'] = false;
$tag = $this->BcBaser->getLink($tag, $link, $linkOptions);
}
return $tag;
}
}
作成したテーマヘルパーは、/plugins/Omotenashi2/src/View/Helper/Omotenashi2Helper.php
に配置します。
あとは、4系と同様にレイアウトファイル(plugins/Omotenashi2/templates/layout/default.php
)上のmainImage()関数の呼び出し箇所(ステップ1の記事のdefault.phpサンプルコードの133行目)を以下のようにBcBaser->mainImage()からOmotenashi2->mainImage()に変更します。
<?php $this->Omotenashi2->mainImage(['num' => 5, 'all' => true, 'class' => 'mainVisualList']) ?>
今回も前回同様、規約(CakePHP 5.x)に沿って、以下の点に注意して作成します。
- ヘルパーファイル名とclass名は、同一にする。
- ヘルパーファイル名(class名)は、既存のヘルパーファイル名と重複しない。
- ヘルパーファイル名(class名)は、アッパーキャメルケースで記述する。
さて、次回ステップ3は、ブログ周りのマイグレートの予定です。なんとなく、ステップ4あたりで完結できればと思っています。