[Symfony] .envの値をもとに、注入するオブジェクトを変える
Symfonyはオートワイヤリング機能で、コンストラクタに指定した型を判断し、オブジェクトを自動的に注入してくれます。
コンストラクタに指定した型がインターフェースの場合も自動で実装クラスを見つけ出し注入してくれますが、複数の実装クラスがある場合はどのクラスにするか明記する必要があります。
どのオブジェクトを注入するか、.envの値をもとに切り替えてみます。
インターフェースを作る
サンプルとして、通知を行うクラスをつくろうと思います。まずは通知インターフェースを作ります。
<?php
interface Notifier
{
public function notice(string $message): void;
}
実装クラスを作る
続いて実装クラスを作ります。Slack, Chatwork, 何もしないクラスを作ります。(中身は省略)
<?php
class SlackNotifier implements NotifierInterface
{
public function notice(string $message): void
{
print_r('Slackへ通知');
}
}
<?php
class ChatworkNotifier implements NotifierInterface
{
public function notice(string $message): void
{
print_r('Chatworkへ通知');
}
}
<?php
class NullNotifier implements NotifierInterface
{
public function notice(string $message): void
{
print_r('なにもしない');
}
}
NotifierInterfaceを持つクラスを作る
NotifierInterfaceを持つクラスを作ります。今回はCommandにしてみます。
<?php
namespace App\Command;
use App\Service\NotifierInterface;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
#[AsCommand(
name: 'app:notice',
description: 'Add a short description for your command',
)]
class NoticeCommand extends Command
{
public function __construct(private NotifierInterface $notifier, string $name = null)
{
parent::__construct($name);
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$this->notifier->notice('メッセージ');
return Command::SUCCESS;
}
}
設定する
まず、.envに切り替え用の環境変数 NOTICE_MODE
を用意します。
NOTICE_MODE=slack
続いて、services.yamlに.envの値を使ったパラメータ notice_mode
と、注入の設定を記述します。
parameters:
notice_mode: '%env(NOTICE_MODE)%'
services:
... 中略 ...
App\Command\NoticeCommand:
arguments:
$notifier: '@=parameter("notice_mode") == "slack" ? service("App\\Service\\SlackNotifier") : parameter("notice_mode") == "chatwork" ? service("App\\Service\\ChatworkNotifier") : service("App\\Service\\NullNotifier")'
@=
から始める値はExpression Language Componentというものを利用して、条件によって注入するクラスを変更することができます。
特殊な関数が利用でき、parameter()
と service()
を使うことができ、 parameter()
は設定されているパラメータの値を、 service()
は設定・もしくはオートワイヤリングされているサービスを返します。
例えば、parameter("notice_mode") == "slack" ? A : B
と指定することで、パラメータの notice_mode
が 'slack' の場合Aを、ちがう場合はBを選択してくれます。
Bの部分に parameter("notice_mode") == "chatwork" ? C : D
のように、さらに条件分岐させることができます。
この設定では以下のようにオブジェクトが注入されます。
notice_modeの値 | 注入されるオブジェクトのクラス |
---|---|
slack | SlackNotifier |
chatwork | ChatworkNotifier |
その他 | NullNotifier |
ですが、このままでは動かず。。
ExpressionLanguageをインストールする
デフォルトではExpressionLanguage Componentがインストールされていないので、インストールします。
composer require expression-language
結果
これで、動作するようになります。
# .envのNOTICE_MODEがslackの場合
bin/console app:notice
Slackへ通知
# .envのNOTICE_MODEがchatworkの場合
bin/console app:notice
Chatworkへ通知
# .envが未設定、slack/chatwork以外の場合
bin/console app:notice
なにもしない
Discussion