EC-CUBE 4の管理画面から作ったページ(URLに 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 という文字列は、.env の ECCUBE_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を書いてみます。
| 項目 | 値 |
|---|---|
| URL | products/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は書き方が異なります。混同しないよう注意してください。
そのまま使うと他のページが404になる理由
ルート名を user_data のまま書くと、作ったページは表示できるのに、他の user_data ページがすべて「ページが見つかりません」になります。理由はEC-CUBEではなく、その土台である Symfony のルーティングの仕組みにあります。
原因:同じ名前のルートは、あとから読まれたほうが前のものを消す
EC-CUBEは app/config/eccube/routes.yaml で、次の順にControllerを読み込みます。
- 1
src/Eccube/Controller(本体のController群) - 2
app/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のカスタマイズに関する記事
- カスタマイズのまとめ
- 新規ページの作成方法(user_dataなし)
- Controllerを使って表示ページにデータを渡す方法
- デバッグモードの設定/解除方法
- 自動更新される新着商品一覧ページ / ブロックを作る方法

