baserCMS 多言語対応サイト(サブサイト)で個別の404エラーページを正しく出力する方法

baserCMS 多言語対応サイト(サブサイト)で個別の404エラーページを正しく出力する方法

baserCMS4系で、サブサイト機能を利用した多言語サイトを制作しているケースで、各言語ごとに個別の404エラーページを正しく出力することがなかなかむずかしい。

というのも、あるフォーラムのトピックで取り上げられていたイシューの通り、BcBaser->contentsMenu()を利用してグローバルメニューを出力している場合は、正しくサブサイトの言語のグローバルメニューが出力されるが、 BcBaser->globalMenu()とか、BcBaser->getTitle()とか、 BcBaser->crumbsList()とか(たぶん他にもあると思いますが)をdefault、あるいは各言語のエラーページ用のレイアウトファイルで利用している場合は、サブサイト閲覧時に呼び出される404エラーページになぜかメインサイトのコンテンツ(つまり日本語のコンテンツ)が出力されてしまいます。

そこで、BcBaserHelper.phpのBcBaser->contentsMenu()BcBaser->globalMenu()のコードの違いを見比べてみたところ、BcBaser->globalMenu()のコードでは、どうもエラーページで正しくsite_idを参照できていない様で、おそらくBcBaser->getTitle()やらBcBaser->crumbsList()についても同様の事情で上述の不具合が生じているようでした。 また、過去のgitHubのclosedプルリクを漁ってみたところ、イシュー自体は異なるものの、BcBaser->contentsMenu()のみエラーページでsite_idを参照できるように改修された経緯が見当たりました。

以上のことから、BcBaserHelper.phpのこれらの関数について、エラーページでsite_idを参照できるように改修すれば、おそらく問題は解決するのですが、如何せんBcBaserHelper.phpはコアファイルだし、ましてや4系だし、ということでユーザー側でできる解決策を考えてみることにしました。

要件を整理

まず、404エラーページに表示するコンテンツの要件を以下のように整理してみます。

  1. 後述のBcBaser->globalMenu()のコードの改修措置をエラーページ用のレイアウトファイルに反映させるため、エラーページ用のレイアウトファイルを用意する。
  2. パンくずは、BcBaser->crumbsList()を使用しない。
  3. <head></head>内で利用しているタイトル表示は、BcBaser->getTitle()を使用しない。

考察

上述の 1.項で触れたBcBaser->globalMenu()コードの改修措置としてパッと思いつく方法は、利用しているテーマ内(/theme/利用しているテーマ/Helper/BcBaserHelper.phpか/app/View/Helper/BcBaserHelper.phpでもOK)にBcBaserHelper.phpのコピーを置いて、それを独自に改修する方法。ただ、この方法はbaserCMSの非常にコアなBcBaserHelper.phpというヘルパーファイルが後のアップデートから取り残されてしまうリスクを伴うのですが、4系は既にメンテナンスフェーズに入っているため、今後アップデートもおそらくないと思われ、実のところこの方法でも特に問題はないかとも思います。
あるいは、独自のテーマヘルパーを作成する方法があるので、今回はせっかくなので独自テーマヘルパーを作成して対応してみようと思います。
エラーページ用のレイアウトファイルについては、メインサイトのレイアウトのdefault.phpをコピーして、サブサイト用のテーマ内のLayoutsフォルダ内にerror.phpとして配置します。

2.項のパンくずは、エラーページにおいてそれ自体の必要性をあまり感じませんが、表示させるとしたら「トップページ>404 NOT FOUND」程度のことなので、各言語のエラーページ用のレイアウトファイルにHTMLを直書きすれば良いかと思います。

3.項も2.項と同様です。エラーページに<head></head>は、通常のサイトページで考慮するようなSNS用のメタタグなどは必要ないので、これもシンプルにHTMLを直書きすれば良いかと思います。

手順

  • まずは /lib/Baser/View/Helper/BcBaserHelper.phpの1847行目辺りのglobalMenu()のコードを以下のように一部改修し、/theme/利用しているテーマ/app/View/Helper/OwnThemeHelper.phpというファイル名のテーマヘルパー用のファイルとして保存します。
    後は、サブサイト内のレイアウトファイルやエレメントファイルのBcBaser->globalMenu()で呼び出していた箇所をOwnTheme->globalMenu()に変更する。
<?php
class OwnThemeHelper extends BcBaserHelper {
/**
 * BcBaserHelperを継承してテーマ独自のメインイメージを出力する
 * テーマ内のみで使用: $this->OwnTheme->globalMenu()
 */

    /**
     * グローバルメニューを出力する
     *
     * @param array $level 取得する階層(初期値 : 1)
     * @param array $options オプション(初期値 : array())
     *    ※ その他のパラメータについては、View::element() を参照
     * @return void
     */
    public function globalMenu($level = 1, $options = [])
    {
        echo $this->getGlobalMenu($level, $options);
    }

    /**
     * グローバルメニューを取得する
     *
     * @param array $level 取得する階層(初期値 : 1)
     * @param array $options オプション(初期値 : array())
     *    ※ その他のパラメータについては、View::element() を参照
     * @return string
     */
    public function getGlobalMenu($level = 1, $options = [])
    {
        $Content = ClassRegistry::init('Content');
            if (isset($this->request->params['Content']['site_id'])) {
                $siteRoot = $Content->getSiteRoot($this->request->params['Content']['site_id']);
            } else {
                $siteRoots = $Content->find('all', [
                    'conditions' => [
                        'Content.site_root' => true
                    ], 'recursive' => -1]);
                foreach ($siteRoots as $key => $siteRoot) {
                    if ($key === 0){
                        $siteRootSub = $siteRoot;
                        continue;
                    }
                    if (strpos($this->request->here, $siteRoot["Content"]["url"]) !== false) {
                        break;
                    }
                    $siteRoot = $siteRootSub;
                }
            }
            $id = $siteRoot['Content']['id'];
        $currentId = null;
        if (isset($this->request->params['Content']['id'])) {
        $currentId = $id;
        }
        $options = array_merge([
            'tree' => $this->BcContents->getTree($id, $level),
            'currentId' => $currentId,
            'data' => [],
            'cache' => false
        ], $options);

        if (BcUtil::loginUser()) {
            unset($options['cache']);
        } else {
            if ($options['cache'] === false) {
                unset($options['cache']);
            } else {
                $options = array_merge($options, [
                        'cache' => [
                            'time' => Configure::read('BcCache.duration'),
                            'key' => $id]]
                );
            }
        }

        $data = array_merge([
            'tree' => $options['tree'],
            'currentId' => $options['currentId']
        ], $options['data']);
        unset($options['tree'], $options['currentId'], $options['data']);
        return $this->getElement('global_menu', $data, $options);
    }
}
  • 次に用意するファイルは、エラーページ用のレイアウトファイル。/theme/利用しているテーマ/Layouts/default.phpをサンプルにして、/theme/利用しているテーマ/Layouts/error.phpを作成。
    「要件を整理」項で示した通り、パンくずは、BcBaser->crumbsList()を使用せず、例えば以下のようにHTMLを直書きします。

    <div class="bs-crumbs">
    <ul itemscope itemtype="https://schema.org/BreadcrumbList">
    <li itemprop="itemListElement" itemscope itemtype="http://schema.org/ListItem">
    <a href="https://trial.basercms.net/" itemprop="item">
      <span itemprop="name">Home</span></a><span class="separator">&nbsp;&gt;&nbsp;</span>
      <meta itemprop="position" content="1"/>
    </li>
    <li itemprop="itemListElement" itemscope itemtype="http://schema.org/ListItem">
      <span itemprop="name">Error</span>
      <meta itemprop="position" content="2"/>
    </li>
    </ul>
    </div>

    <head></head>内も可能な限りシンプルにして、タイトル表示は、BcBaser->getTitle()を使用せず、例えば<title>404 Not Found|Sample Theme</title>といった具合にHTMLタグを直書きします。

  • 最後に各言語毎のerrorページ用のテンプレートファイル。/lib/Baser/View/Errors/error400.phpをコピーして、/theme/利用しているテーマ/Errors/error400.phpとして保存。サンプルは以下。

<?php
/**
 * baserCMS :  Based Website Development Project <https://basercms.net>
 * Copyright (c) baserCMS Users Community <https://basercms.net/community/>
 *
 * @copyright       Copyright (c) baserCMS Users Community
 * @link            https://basercms.net baserCMS Project
 * @package         Baser.View
 * @since           baserCMS v 0.1.0
 * @license         https://basercms.net/license/index.html
 */
?>

<?php $this->layout = "error"; ?>

    <h2><?php echo $message; ?></h2>
    <p class="error">
        <strong><?php echo __d('baser', 'エラー'); ?>: </strong>
        <?php printf(
            __d('baser', 'アドレス %s に送信されたリクエストは無効です。'),
            "<strong>'{$url}'</strong>"
        ); ?>
    </p>
<?php
if (Configure::read('debug') > 0):
    echo $this->element('exception_stack_trace');
endif;

<?php $this->layout = "error"; ?>とすることで、先に作成した/theme/利用しているテーマ/Layouts/error.phpを参照するようになります。 error500.phpほか、その他のerrorページ用ファイルも適宜同様に作成してください。

もし、使用しているテーマのグローバルメニューがBcBaser->globalMenu()を利用していて、多言語化されているのであれば、是非試してみてください。

追記

改修が反映されない時は、/app/tmp/cacheフォルダをフォルダごと削除して、ブラウザをリロードしてみてください。

コメント


コメントする


zgqsJr