チームで Symfony プロジェクトに取り組んでいるときに、サービスの 1 つに特定の値オブジェクト インスタンスを注入する必要がありました。この特定のケースでは、値自体は .env ファイルで提供される値から設定する必要がありました。
もちろん、文字列値をサービスに直接渡して、サービスがコンストラクターで値オブジェクトをインスタンス化できるようにすることもできますが、services.yaml ファイルで構成してインジェクトできるかどうかを確認したかったのです。代わりに、完全にインスタンス化されたオブジェクトが使用されます。これにより、それらのオブジェクト インスタンスを複数のサービスに渡すことができ、それぞれのサービス内で値オブジェクトの作成を繰り返す必要がなくなります。
これが私がやった方法です...
私たちのアプリケーションは Twilio SDK を利用しています。 SDK 呼び出しをラップするさまざまなサービスがあり、それらは環境固有の設定値 (環境ごとの当社の API キーなど) を使用する必要があります。
Twilio API は文字列識別子 (SID) を使用します。 SID の各タイプには、それに関連付けられた異なる 2 文字のプレフィックスがあり、その後に数字 0 ~ 9 と文字 A ~ F (大文字と小文字) で構成される 32 文字が続きます。
例えば:
各 SID タイプの値オブジェクトが、渡された値にその SID タイプの適切なプレフィックスがあることを検証し、文字列が正しい長さであり、許可された文字のみで構成されていることを確認したかったのです。 .
各 SID タイプは同じ検証ロジックと機能を使用し、SID タイプのプレフィックスによってのみ区別されるため、基本特性を作成することは理にかなっています。必要に応じて、これを抽象クラスにすることもできます。アプリにはパラメータ型などとして TwilioStringIdentifier の概念は必要ないので、ここでは抽象クラスよりもトレイトのほうが好きです。
この特性は、各 SID タイプが実装する必要がある抽象メソッド getPrefixForSidType() を定義し、その特定のタイプに適切なプレフィックスを提供します。また、検証ロジックも実行します。
namespace App\Domain\Model\Twilio\Sid; use Assert\Assert; trait TwilioStringIdentifier { private readonly string $sid; abstract private function getPrefixForSidType(): string; public static function fromString(string $string): self { return new self($string); } public function __construct(string $sid) { Assert::that($sid) ->startsWith($this->getPrefixForSidType()) ->length(34) ->regex('/^[a-zA-Z]{2}[0-9a-fA-F]{32}$/') ; $this->sid = $sid; } public function asString(): string { return $this->sid; } }
各 SID タイプを表す値オブジェクト クラスは単純です。必要なのは、TwilioStringIdentifier Trait を使用し、getPrefixForSidType() メソッドで適切なプレフィックスを定義することだけです。
namespace App\Domain\Model\Twilio\Sid; final readonly class AccountSid { use TwilioStringIdentifier; private function getPrefixForSidType(): string { return 'AC'; } }
他の SID タイプ クラスは、定義されたプレフィックスを除いて同一です。
これらの値オブジェクトはアプリケーション全体で使用され、会社のグローバル値だけでなく、さまざまな値が関連付けられて使用されるため、定義された値ですでにインスタンス化されている特定のタイプのオブジェクトをサービスに注入する方法が必要でした。 .env ファイル内
Symfony には Factory 経由でインスタンス化されるサービスを定義できる機能があることは知っていましたが、他の場所からのメソッド呼び出しの結果であるオブジェクトの注入については (私の記憶では) まったく見たことがありませんでした。また、これらの Factory メソッドに引数を渡すことができることも知っていましたが、1 つの Value Object インスタンスでこれを行う方法がわかりませんでした。
Symfony のサービス定義では、各サービスに名前を付けることができます。通常、これはサービス クラスの名前:
で行われます。
App\Path\To\My\Service: class: App\Path\To\My\Service arguments: []
ただし、そのサービス名はクラス名と一致する必要はありません。 app.my_service や Foo\Bar\Baz\Service などでも構いません。
それでは、必要な値オブジェクトのインスタンス化されたインスタンスである一意の名前を持つサービスを作成したらどうなるでしょうか? .env 値を引数として渡して、そのオブジェクト インスタンスをサービス クラスに注入することができます!
# Create services named with a Global "namespace" Global\Twilio\Sid\Account: factory: ['App\Domain\Model\Twilio\Sid\AccountSid', 'fromString'] arguments: ['%env(TWILIO_ACCOUNT_SID)%'] Global\Twilio\Sid\Api: factory: ['App\Domain\Model\Twilio\Sid\ApiSid', 'fromString'] arguments: ['%env(TWILIO_API_SID)%'] Global\Twilio\Sid\Application: factory: ['App\Domain\Model\Twilio\Sid\ApplicationSid', 'fromString'] arguments: ['%env(TWILIO_APP_SID)%']
次に、名前付き引数を介してこれらのサービス (オブジェクト) を Twilio サービスに渡します:
App\Service\Vendor\Twilio\TwilioService: arguments: $accountSid: '@Global\Twilio\Sid\Account' $apiSid: '@Global\Twilio\Sid\Api' $applicationSid: '@Global\Twilio\Sid\Application' $apiSecret: '%env(TWILIO_API_SECRET)%'
これで、サービス クラスは完全にインスタンス化された値オブジェクト インスタンスを受け取ることが期待できるようになりました:
namespace App\Service\Vendor\Twilio; use App\Domain\Model\Twilio\Sid\AccountSid; use App\Domain\Model\Twilio\Sid\ApiSid; use App\Domain\Model\Twilio\Sid\ApplicationSid; final readonly class TwilioService { public function __construct( private AccountSid $accountSid, private ApiSid $apiSid, private ApplicationSid $applicationSid, private string $apiSecret ) {} }
出来上がり!
Symfony は十分に柔軟で直観的であるため、これを行う方法を理解するのは簡単でした。これを行うためのクイックリファレンスが他に見つからなかったので、Future Me や同様のことを行う必要がある他の人のためのリファレンスとしてこれを書いておこうと思いました
乾杯、コーディングを楽しんでください!
免責事項: 提供されるすべてのリソースの一部はインターネットからのものです。お客様の著作権またはその他の権利および利益の侵害がある場合は、詳細な理由を説明し、著作権または権利および利益の証拠を提出して、電子メール [email protected] に送信してください。 できるだけ早く対応させていただきます。
Copyright© 2022 湘ICP备2022001581号-3