EC-CUBE (Symfony)のCLIで送ったメールのURLがlocalhostになる
概要
EC-CUBE(4系以上)において、バッチ処理などからメールを送信したい場面があります。そんなとき、Webサイトが https://ec-cube.test なのに、メールに記載されるURLが http://localhost になってしまうことがあります。
TL;DR
- CLIから実行すると、URLが localhost になってしまう
- services.yaml に設定を記載するとよい
- RequestStackを使うなど、別の方法も有効かも
localhost になってしまう例
バッチ処理で使われるようなコマンドを想定して、次の簡単なサンプルコードを実装してみます。
<?php namespace Customize\Command; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; class TestMailCommand extends Command { protected static $defaultName = 'test:mail'; public function __construct(private UrlGeneratorInterface $urlGenerator) { parent::__construct(); } protected function execute(InputInterface $input, OutputInterface $output): int { $io = new SymfonyStyle($input, $output); $url = $this->urlGenerator->generate('cart', [], UrlGeneratorInterface::ABSOLUTE_URL); $io->success($url); return 0; } }
これを bin/console test:mail
コマンドで実行すると、 ホストが localhost のURLが表示されてしまいます。
ここでは例のため UrlGeneratorInterface
で取得した絶対URLをコンソールに表示しているだけですが、Twigテンプレートなどでメールを送信する場合でも同様です。
HTTPのリクエストから実行されていないので、フレームワークが使っているドメインなどを認識できないためこうなります。
① services.yamlに絶対URLを指定
TL;DRに書いたように、このlocalhost問題は設定ファイルに絶対URLを記載することで解決できます。app/Customize/Resource/config/services.yaml に次の項目を追加します
framework: router: default_uri: https://ec-cube.test
そして、上と同じように test:mail
を実行するとこのようになります。
localhostではなく、設定したURLが表示されるようになりました。ちなみにこれはなんてことはなく、ただのSymfonyのドキュメントに書いてある方法です。
https://symfony.com/doc/6.4/routing.html#generating-urls-in-commands
② RequestContextを指定する例
Symfonyの設定ではなく、URLを生成するときのコードで対応することもできます。URLを生成するときにHTTPのリクエストコンテキストがないためlocalhostになってしまうので、RequestContextを設定することで対応する方法です。
上とほとんど同じですが、このようなコードになります。
<?php namespace Customize\Command; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Component\Routing\RequestContext; class TestMailCommand extends Command { protected static $defaultName = 'test:mail'; public function __construct( private UrlGeneratorInterface $urlGenerator, private RequestContext $requestContext, ) { parent::__construct(); } protected function execute(InputInterface $input, OutputInterface $output): int { $io = new SymfonyStyle($input, $output); $this->requestContext->setHost('hoge.localhost'); $this->requestContext->setHttpPort(8080); $url = $this->urlGenerator->generate('cart', [], UrlGeneratorInterface::ABSOLUTE_URL); $io->success($url); return 0; } }
こうすることで、表示されるURLがhttp://hoge.localhost:8080/cart
になります。この方法ではservices.yamlを編集する必要はありません。
弊社で開発・販売している「かご落ち通知メールプラグイン」というEC-CUBEプラグインがあります。カートに入ったまま放置されている商品についてメールでお知らせする機能で、メール送信はcronなどでバッチ処理を起動する想定となっています。このプラグインのメール送信部分は、RequestContextを使うなどservices.yamlによらない方法で実装しました。管理画面でプラグインの初期設定をしてもらうタイミングで、他の項目とあわせてトップページURLを保存する仕様になっています。
最後に
EC-CUBEやSymfonyに限りませんが、URLの生成は意外と罠が多いです。最後に紹介したRequestStackを使った方法では、EC-CUBEをサブディレクトリで運用しているときや、UrlGeneratorではなくAssetのURLを生成するときなどでは、意図しないURLになってしまうことがあります。結局はちゃんと確認するしかありません。
余談ですが、この記事で使った ec-cube.test
や hoge.localhost
のはテスト・開発用途で予約済みトップレベルドメインなので、安心して使えるドメインです。こういった例で適当な .com のドメインや Gmail のメールアドレスを使うと、実在してしまうことがあるので注意が必要です。