卓越飞翔博客卓越飞翔博客

卓越飞翔 - 您值得收藏的技术分享站
技术文章73530本站已运行432

将值对象注入到自动装配的 Symfony 服务中

将值对象注入到自动装配的 symfony 服务中

与我的团队一起开发 symfony 项目时,我需要将特定的值对象实例注入到我的一项服务中。在这种特殊情况下,值本身需要根据 .env 文件中提供的值进行设置。

当然,我可以直接将字符串值传递到我的服务中,并让服务在构造函数中实例化值对象,但我想看看是否可以在 services.yaml 文件中配置它并注入完整的- 实例化对象代替。这将允许我将这些对象实例传递给多个服务,而不必在每个服务中重复创建值对象。

这是我的做法...

背景

我们的应用程序使用 twilio sdk。我们有各种封装 sdk 调用的服务,它们需要使用我们特定于环境的配置值(我们公司针对每个环境的 api 密钥等)。

twilio api 使用字符串标识符或 sid。每种类型的 sid 都有一个不同的 2 个字母前缀,后跟由数字 0 到 9 和字母 a 到 f(大写和小写)组成的 32 个字符。

例如:

  • conferencesid 具有前缀 cf,看起来像 cfabcdef0123456789abcdef0123456789
  • callsid 具有前缀 ca,看起来像 caabcdef0123456789abcdef0123456789
  • 还有其他类型的sid,它们都使用相同的格式,仅通过前缀来区分

我想确保每个 sid 类型的值对象都验证传入的值具有该 sid 类型的正确前缀,同时确保字符串的长度正确并且仅由允许的字符组成。

值对象

我的每个 sid 类型都使用相同的验证逻辑和功能,仅通过 sid 类型的前缀进行区分,因此创建基本 trait 是有意义的。如果您愿意,这可以是一个抽象类。我不需要应用程序中的 twiliostringidentifier 概念作为参数类型或类似的东西,所以我在这里更喜欢 trait 而不是 abstract class。

此 trait 确实定义了每个 sid 类型必须实现的抽象方法 getprefixforsidtype(),为该给定类型提供正确的前缀。它还执行验证逻辑。

namespace appdomainmodeltwiliosid;

use assertassert;

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 类

代表每种 sid 类型的值对象类都很简单。他们只需要使用 twiliostringidentifier trait 并通过 getprefixforsidtype() 方法定义正确的前缀。

namespace appdomainmodeltwiliosid;

final readonly class accountsid
{
    use twiliostringidentifier;

    private function getprefixforsidtype(): string
    {
        return 'ac';
    }
}

其他 sid 类型类除了定义的前缀之外都是相同的。

注入实例化对象

因为这些值对象将在整个应用程序中使用并与各种相关的值一起使用,而不仅仅是我们公司的全局值,所以我需要一种方法将特定类型的对象注入到服务中,该对象已经使用我们的.env 文件

我知道 symfony 能够定义通过工厂实例化的服务,但从未真正见过(我记得)任何关于注入一个对象的内容,该对象是从其他地方调用方法的结果。我还知道这些工厂方法可以将参数传递给它们,我只是不确定如何使用一个值对象实例来执行此操作。

定义特定值对象实例

symfony 的服务定义允许您命名每个服务。通常它是通过服务类的名称完成的:

apppathtomyservice:
    class: apppathtomyservice
    arguments: []

但是,该服务名称不必与类名称匹配。它可以是 app.my_service 或 foobarbazservice 或其他。

那么,如果我创建一个具有唯一名称的服务,该名称是我需要的值对象的实例化实例,该怎么办?我可以将 .env 值作为参数传递,然后将该对象实例注入到我的服务类中!

服务.yaml

# create services named with a global "namespace"
globaltwiliosidaccount:
    factory: ['appdomainmodeltwiliosidaccountsid', 'fromstring']
    arguments: ['%env(twilio_account_sid)%']

globaltwiliosidapi:
    factory: ['appdomainmodeltwiliosidapisid', 'fromstring']
    arguments: ['%env(twilio_api_sid)%']

globaltwiliosidapplication:
    factory: ['appdomainmodeltwiliosidapplicationsid', 'fromstring']
    arguments: ['%env(twilio_app_sid)%']

然后通过它们的命名参数将这些服务(对象)传递到我的 twilio 服务中:

appservicevendortwiliotwilioservice:
    arguments:
        $accountsid: '@globaltwiliosidaccount'
        $apisid: '@globaltwiliosidapi'
        $applicationsid: '@globaltwiliosidapplication'
        $apisecret: '%env(twilio_api_secret)%'

现在我的服务类可以接收完全实例化的值对象实例:

twilio服务

namespace AppServiceVendorTwilio;

use AppDomainModelTwilioSidAccountSid;
use AppDomainModelTwilioSidApiSid;
use AppDomainModelTwilioSidApplicationSid;

final readonly class TwilioService
{
    public function __construct(
        private AccountSid $accountSid,
        private ApiSid $apiSid,
        private ApplicationSid $applicationSid,
        private string $apiSecret
    ) {}
}

瞧!

symfony 足够灵活且足够直观,很容易弄清楚如何做到这一点。由于我在其他地方找不到执行此操作的快速参考,因此我想我应该将其写下来作为未来我和其他可能需要执行类似操作的人的参考

干杯,祝编码愉快!

卓越飞翔博客
上一篇: golang框架的限流策略适用于哪些场景?
下一篇: 返回列表
留言与评论(共有 0 条评论)
   
验证码:
隐藏边栏