与我的团队一起开发 Symfony 项目时,我需要将特定的值对象实例注入到我的一项服务中。在这种特殊情况下,值本身需要根据 .env 文件中提供的值进行设置。
当然,我可以将字符串值直接传递到我的服务中,并让服务在构造函数中实例化值对象,但我想看看是否可以在 services.yaml 文件中配置它并注入而是完全实例化的对象。这将允许我将这些对象实例传递给多个服务,而不必在每个服务中重复创建值对象。
这就是我的做法...
我们的应用程序使用 Twilio SDK。我们有各种封装 SDK 调用的服务,它们需要使用我们特定于环境的配置值(我们公司针对每个环境的 API 密钥等)。
Twilio API 使用字符串标识符或 SID。每种类型的 SID 都有一个不同的与之关联的 2 个字母前缀,后跟由数字 0 到 9 以及字母 A 到 F(大写和小写)组成的 32 个字符。
例如:
我想确保每个 SID 类型的值对象都验证传入的值具有该 SID 类型的正确前缀,同时确保字符串的长度正确并且仅由允许的字符组成.
我的每个 SID 类型都使用相同的验证逻辑和功能,仅通过 SID 类型的前缀进行区分,因此创建基本 Trait 是有意义的。如果您愿意,这可以是一个抽象类。我不需要应用程序中的 TwilioStringIdentifier 概念作为参数类型或类似的东西,所以我在这里更喜欢 Trait 而不是 Abstract Class。
此 Trait 确实定义了每个 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 能够定义通过工厂实例化的服务,但从未真正见过(我记得)任何关于注入一个对象的内容,该对象是从其他地方调用方法的结果。我还知道这些工厂方法可以将参数传递给它们,我只是不确定如何使用一个值对象实例来执行此操作。
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 足够灵活且足够直观,很容易弄清楚如何做到这一点。由于我在其他地方找不到执行此操作的快速参考,因此我想我应该将其写下来作为未来我和其他可能需要执行类似操作的人的参考
干杯,祝编码愉快!
免责声明: 提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发到邮箱:[email protected] 我们会第一时间内为您处理。
Copyright© 2022 湘ICP备2022001581号-3