概要
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 のメールアドレスを使うと、実在してしまうことがあるので注意が必要です。







