"Se um trabalhador quiser fazer bem o seu trabalho, ele deve primeiro afiar suas ferramentas." - Confúcio, "Os Analectos de Confúcio. Lu Linggong"
Primeira página > Programação > Injetar objetos de valor em um serviço Symfony com conexão automática

Injetar objetos de valor em um serviço Symfony com conexão automática

Publicado em 16/08/2024
Navegar:515

Inject Value Objects Into An Autowired Symfony Service

Enquanto trabalhava em um projeto Symfony com minha equipe, precisei injetar instâncias específicas de Value Object em um de meus serviços. Os próprios valores, neste caso específico, precisavam ser definidos a partir dos valores fornecidos em nosso arquivo .env.

Eu poderia, é claro, simplesmente passar os valores da string diretamente para o meu serviço e fazer com que o serviço instanciasse os objetos de valor no construtor, mas queria ver se era possível configurá-lo no arquivo services.yaml e injetar em vez disso, os objetos totalmente instanciados. Isso me permitiria passar essas instâncias de objetos para vários serviços e não ter que repetir a criação do Objeto de Valor dentro de cada um.

Veja como eu fiz isso...

Fundo

Nosso aplicativo utiliza o Twilio SDK. Temos vários serviços que envolvem as chamadas do SDK e eles precisam usar nossos valores de configuração específicos do ambiente (a chave de API da nossa empresa para cada ambiente, etc.).

A API Twilio usa identificadores de string ou SIDs. Cada tipo de SID possui um prefixo diferente de 2 letras associado a ele, seguido por 32 caracteres compostos pelos dígitos de 0 a 9 e as letras de A a F (maiúsculas e minúsculas).

Por exemplo:

  • Um ConferenceSid tem o prefixo CF e se parece com CFabcdef0123456789abcdef0123456789
  • Um CallSid tem o prefixo CA e se parece com CAabcdef0123456789abcdef0123456789
  • Existem outros tipos de SIDs e todos usam o mesmo formato, diferenciado apenas pelo prefixo

Eu queria ter certeza de que os objetos de valor para cada tipo de SID validavam que o valor passado tinha o prefixo adequado para esse tipo de SID, além de garantir que a string tivesse o comprimento correto e fosse composta apenas dos caracteres permitidos .

Os objetos de valor

Cada um dos meus tipos de SID usa a mesma lógica e funcionalidade de validação, diferenciando apenas pelo prefixo do tipo de SID, então faz sentido criar uma característica base. Esta poderia ser uma classe abstrata, se você preferir. Não preciso do conceito de TwilioStringIdentifier no aplicativo como um tipo de parâmetro ou algo parecido, então prefiro um Trait em vez de uma classe abstrata aqui.

Esta característica define um método abstrato getPrefixForSidType() que cada tipo de SID deve implementar, fornecendo o prefixo adequado para esse tipo específico. Ele também executa a lógica de validação.

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;
    }
}    

As classes SID

As classes Value Object que representam cada um dos tipos de SID são simples. Eles só precisam usar o traço TwilioStringIdentifier e definir o prefixo adequado por meio do método getPrefixForSidType().

namespace App\Domain\Model\Twilio\Sid;

final readonly class AccountSid
{
    use TwilioStringIdentifier;

    private function getPrefixForSidType(): string
    {
        return 'AC';
    }
}

As outras classes de tipo SID são idênticas, exceto pelo prefixo definido.

Injetando os objetos instanciados

Como esses Value Objects serão utilizados em toda a aplicação e com diversos valores associados, e não apenas os valores globais da nossa empresa, eu precisava de uma forma de injetar nos serviços um objeto de um tipo específico que já estivesse instanciado com os valores definidos em nosso arquivo .env

Eu sabia que o Symfony tem a capacidade de definir serviços a serem instanciados por meio de um Factory, mas nunca tinha visto (pelo que me lembro) nada sobre injetar um objeto que fosse o resultado de uma chamada de método de outro lugar. Eu também sabia que esses métodos Factory poderiam ter argumentos passados ​​para eles, só não tinha certeza de como fazer isso com uma instância de objeto de valor.

Definindo a Instância do Objeto de Valor Específico

A definição de serviço do Symfony permite nomear cada serviço. Normalmente isso é feito com o nome da classe de serviço:

App\Path\To\My\Service:
    class: App\Path\To\My\Service
    arguments: []

Mas esse nome de serviço não precisa corresponder ao nome da classe. Poderia ser app.my_service ou Foo\Bar\Baz\Service ou qualquer outra coisa.

Então, e se eu criar um serviço com um nome exclusivo que seja a instância instanciada do objeto de valor que preciso? Eu poderia passar o valor .env como argumento e então ter essa instância do objeto para injetar em minhas classes de serviço!

serviços.yaml

# 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)%']

Em seguida, passe esses serviços (objetos) para meu serviço Twilio por meio de seus argumentos nomeados:

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)%'

Agora minha classe de serviço pode esperar receber as instâncias de Value Object totalmente instanciadas:

TwilioService

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
    ) {}
}

Voilá!

O Symfony é flexível e intuitivo o suficiente para que seja simples descobrir como fazer isso. Como não consegui encontrar uma referência rápida para fazer isso em outro lugar, pensei em escrever isso como uma referência para o Future Me e qualquer outra pessoa que precise fazer algo semelhante

Saúde e boa codificação!

Declaração de lançamento Este artigo foi reproduzido em: https://dev.to/mbadolato/inject-value-objects-into-an-autowired-symfony-service-3an2?1 Se houver alguma violação, entre em contato com [email protected] para excluir isto
Tutorial mais recente Mais>

Isenção de responsabilidade: Todos os recursos fornecidos são parcialmente provenientes da Internet. Se houver qualquer violação de seus direitos autorais ou outros direitos e interesses, explique os motivos detalhados e forneça prova de direitos autorais ou direitos e interesses e envie-a para o e-mail: [email protected]. Nós cuidaremos disso para você o mais rápido possível.

Copyright© 2022 湘ICP备2022001581号-3