【EC-CUBE 4】user_dataを含むページのControllerを新規に作成する方法

EC-CUBE 4の管理画面から作ったページ(URLに user_data が含まれるもの)には、専用のControllerを付けられます。ただしルーティングの書き方が少し特殊で、そのまま書くと他のページが表示できなくなります。書き方と、その落とし穴の回避方法をまとめました。

user_dataを含むページのControllerはどう書く?

ルーティングに %eccube_user_data_route% を使い、テンプレートは @user_data 名前空間から呼びます。

/**
 * @Route("/%eccube_user_data_route%/products/sample", name="user_data_products_sample", priority=10)
 */
public function index(Request $request)
{
    return $this->render('@user_data/products_sample.twig');
}

ポイントは ルート名を user_data にしないことと、priority を指定することの2つです。理由は本文の後半で説明します。

【動作環境】EC-CUBEのバージョン:4.3.0 / サーバー:XServer

エラーの内容を画面で確認できるよう、作業前にデバッグモードを設定しておくのがおすすめです(作業後の解除もお忘れなく)。

デバッグモードの設定/解除方法
目次

管理画面から作ったページのControllerはどこにある?

src/Eccube/Controller の中の UserDataController.php です。

ただしこのControllerは、管理画面から作ったすべてのページを1本で受け持っています。直接書き換えるのはもちろん、app/Customize/Controller へコピーして書き換えるのもおすすめしません。1ページのために全ページの挙動を変えてしまうためです。

特定のページだけ動きを変えたい場合は、そのページ専用のControllerを新しく用意するのがよいでしょう。次の章で作り方を説明します。

Controllerファイルが見当たらないとき

Xserverのファイルマネージャーを使っていると、上記のディレクトリを辿っても UserDataController.php が表示されないことがあります。その場でリロードすると出てくるので、「あるはずのファイルがない」と思ったら一度更新してみてください。

ルーティングの書き方

管理画面から作ったページは、ルーティングの書き方が通常のページと違います。

デフォルトのルーティング

先ほどの UserDataController.php を開くと、ルーティングは次のように書かれています。

/**
 * @Route("/%eccube_user_data_route%/{route}", name="user_data", requirements={"route": "([0-9a-zA-Z_\-]+\/?)+(?<!\/)"}, methods={"GET"})
 */

%eccube_user_data_route% の部分がURL上の user_data にあたり、続く {route} に管理画面で決めたURLが入ります。ルート名はURLに関係なく、すべて user_data という1つの名前になっています。

URLに出る user_data という文字列は、.envECCUBE_USER_DATA_ROUTE で決まっています(初期値が user_data)。ここを書き換えれば別の文字列にできるため、ルーティングにはベタ書きせず %eccube_user_data_route% のまま書くのが安全です。

これにならって書くと、たとえば次のような形になります。

/**
 * @Route("/%eccube_user_data_route%/products/sample", name="user_data")
 */

この書き方は、そのままだと他のページを壊します。ルート名を user_data にしてはいけません。詳しくは 「そのまま使うと他のページが404になる理由」 で説明します。

requirements とは?

requirements は、URLの可変部分(ここでは {route})が満たすべき条件を 正規表現 で指定するものです。

上のコードでは "route": "([0-9a-zA-Z_\-]+\/?)+(?<!\/)" となっています。意味を分解すると次のとおりです。

部分意味
[0-9a-zA-Z_\-]+英数字・アンダースコア・ハイフンが1文字以上続く
\/?そのあとにスラッシュがあってもなくてもよい
(...)+上の組み合わせを何回でも繰り返せる(=スラッシュ区切りの多階層URLを許す
(?<!\/)末尾がスラッシュでは終わらない

この条件に合わないURLは 404エラー になります。

「何回でも繰り返せる」がのちのち効いてきます。 products/sample のようにスラッシュを含むURLも、この1本のルートがまとめて受け止められる、ということです。

Controllerの例

次のURL・ファイル名で管理画面からページを作った場合のControllerを書いてみます。

項目
URLproducts/sample
ファイル名products_sample(.twig)
<?php

namespace Customize\Controller;

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
use Eccube\Controller\AbstractController;

class SampleProductController extends AbstractController
{
    /**
     * @Route("/%eccube_user_data_route%/products/sample", name="user_data_products_sample", priority=10)
     */
    public function index(Request $request)
    {
        return $this->render('@user_data/products_sample.twig');
    }
}

このファイルを app/Customize/Controller へ置くと、以降このページはこのControllerを経由して表示されるようになります。

  • URLやテンプレート名は、実際に管理画面で設定したものに合わせて書き換えてください
  • この時点ではページを表示するだけなので、見た目は何も変わりません
  • クラス名(SampleProductController)とファイル名は一致させます

ここから処理を書き足していくことで、データベースの値を取り出して表示する、アクセス権限をチェックする、条件によって別ページへ飛ばす、といった動きを付けられるようになります。

通常のページとの違いは2か所です。

  • ルーティングに %eccube_user_data_route% を含める
  • テンプレートを @user_data 名前空間から render() で呼ぶ(@Template ではなく)

URLから user_data を取り除いたページのControllerは書き方が異なります。混同しないよう注意してください。

新規ページの作成方法(user_dataなし)

そのまま使うと他のページが404になる理由

ルート名を user_data のまま書くと、作ったページは表示できるのに、他の user_data ページがすべて「ページが見つかりません」になります。理由はEC-CUBEではなく、その土台である Symfony のルーティングの仕組みにあります。

原因:同じ名前のルートは、あとから読まれたほうが前のものを消す

EC-CUBEは app/config/eccube/routes.yaml で、次の順にControllerを読み込みます。

  1. 1src/Eccube/Controller(本体のController群)
  2. 2app/Customize/Controller(自分で追加するController群)

読み込んだルートは1つのまとまりへ順に足されていきますが、このときすでに同じ名前のルートがあると、古いほうは削除されて新しいほうが末尾に置かれます。Symfonyのソースにも、そのための処理だとはっきり書かれています。

つまり、自作のControllerに name="user_data" と書くと、次のことが起こります。

順番起きていること
1本体の UserDataController が読まれ、全ページ共通のルート user_data が登録される
2自作のControllerが読まれ、同じ名前なので1のルートが消される
3残るのは自作の1ページ分のルートだけ。他の user_data ページは行き先を失い404になる

「作ったページだけは正しく表示される」のも、同じ理由です。ルートを乗っ取れているからページが出るのであって、うまくいっているわけではありません。症状は表裏一体です。

対処:名前を変え、優先度を上げる

まずルート名を一意なものへ変えます。これで本体のルートは消えなくなります。

ただし、名前を変えるだけでは今度は自作のページが呼ばれなくなります。ルートは登録された順に上から照合され、最初に一致したもので確定するためです。本体の user_data ルートは先に登録されており、しかも前述のとおりスラッシュを含む多階層URLにも一致するので、/user_data/products/sample は本体側で先に拾われてしまいます。

そこで priority を指定して、自作のルートを先に照合させます。値が大きいほど先に評価されます(指定しない場合は0)。

/**
 * @Route("/%eccube_user_data_route%/products/sample", name="user_data_products_sample", priority=10)
 */

この2点をそろえると、自作のページは専用のControllerで、それ以外のページは本体のControllerで、それぞれ正しく処理されるようになります。

書き方自作ページ他のuser_dataページ
name="user_data" のみ表示されるすべて404
名前を一意にしただけ自作Controllerが呼ばれない表示される
名前を一意にし priority を付ける表示される表示される

ルーティングを書き換えたら、管理画面からキャッシュを削除してください。ルート情報はキャッシュされているため、削除しないと変更が反映されません。

まとめ

管理画面から作ったページのControllerについて紹介しました。表示するだけのControllerでも、そこから Repository やFormTypeを組み合わせていくことで、データベースを扱うページへ育てていけます。

  • デフォルトのControllerは src/Eccube/Controller/UserDataController.php。直接編集も丸ごとコピーもしない
  • ルーティングには %eccube_user_data_route% を使い、テンプレートは @user_data 名前空間から render() で呼ぶ
  • ルート名を user_data にしない。本体のルートを消してしまい、他のページが404になる
  • priority を指定する。指定しないと本体のルートが先に一致し、自作のControllerが呼ばれない
  • 書き換えたら管理画面からキャッシュを削除する

EC-CUBEのカスタマイズに関する記事

この記事に出てきた用語

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次