Mientras trabajaba en un proyecto de Symfony con mi equipo, necesitaba inyectar instancias específicas de objetos de valor en uno de mis servicios. Los valores en sí, en este caso particular, debían establecerse a partir de los valores proporcionados en nuestro archivo .env.
Por supuesto, podría simplemente pasar los valores de cadena directamente a mi servicio y hacer que el servicio cree una instancia de los objetos de valor en el constructor, pero quería ver si era posible configurarlo en el archivo services.yaml e inyectarlo. en su lugar, los objetos con instancias completas. Esto me permitiría pasar esas instancias de objetos a múltiples servicios y no tener que repetir la creación del Objeto de Valor dentro de cada uno.
Así es como lo hice...
Nuestra aplicación utiliza Twilio SDK. Tenemos varios servicios que envuelven las llamadas del SDK y necesitan usar nuestros valores de configuración específicos del entorno (la clave API de nuestra empresa para cada entorno, etc.).
La API de Twilio utiliza identificadores de cadena o SID. Cada tipo de SID tiene un prefijo diferente de 2 letras asociado, seguido de 32 caracteres compuestos por los dígitos del 0 al 9 y las letras de la A a la F (mayúsculas y minúsculas).
Por ejemplo:
Quería asegurarme de que los objetos de valor para cada tipo de SID validaran que el valor pasado tuviera el prefijo adecuado para ese tipo de SID, además de asegurarme de que la cadena tuviera la longitud correcta y solo estuviera compuesta por los caracteres permitidos. .
Cada uno de mis tipos de SID utiliza la misma lógica y funcionalidad de validación, diferenciándose solo por el prefijo del tipo de SID, por lo que tiene sentido crear un rasgo base. Esta podría ser una clase abstracta si lo prefiere. No necesito el concepto de TwilioStringIdentifier en la aplicación como tipo de parámetro ni nada por el estilo, así que aquí prefiero un rasgo a una clase abstracta.
Este rasgo define un método abstracto getPrefixForSidType() que cada tipo de SID debe implementar, proporcionando el prefijo adecuado para ese tipo determinado. También realiza la lógica de validación.
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; } }
Las clases de objetos de valor que representan cada uno de los tipos de SID son simples. Solo necesitan usar el rasgo TwilioStringIdentifier y definir el prefijo adecuado mediante el método getPrefixForSidType().
namespace App\Domain\Model\Twilio\Sid; final readonly class AccountSid { use TwilioStringIdentifier; private function getPrefixForSidType(): string { return 'AC'; } }
Las otras clases de tipo SID son idénticas excepto por su prefijo definido.
Debido a que estos objetos de valor se usarán en toda la aplicación y con varios valores asociados, y no solo los valores globales de nuestra empresa, necesitaba una forma de inyectar en los servicios un objeto de un tipo específico que ya estuviera instanciado con los valores definidos. en nuestro archivo .env
Sabía que Symfony tiene la capacidad de definir servicios para ser instanciados a través de Factory, pero nunca había visto (que yo recuerde) nada sobre inyectar un objeto que fuera el resultado de una llamada a un método desde otro lugar. También sabía que a estos métodos de Factory se les podían pasar argumentos, pero no estaba seguro de cómo hacer esto con una instancia de Value Object.
La definición de servicio de Symfony te permite nombrar cada servicio. Normalmente se hace con el nombre de la clase de Servicio:
App\Path\To\My\Service: class: App\Path\To\My\Service arguments: []
Pero el nombre de ese servicio no tiene que coincidir con el nombre de la clase. Podría ser app.my_service o Foo\Bar\Baz\Service o lo que sea.
Entonces, ¿qué pasa si creo un servicio con un nombre único que sea la instancia instanciada del objeto de valor que necesito? ¡Podría pasar el valor .env como argumento y luego tener esa instancia de objeto para inyectarla en mis clases de servicio!
# 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)%']
Luego pase esos servicios (objetos) a mi servicio Twilio a través de sus argumentos con nombre:
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)%'
Ahora mi clase de servicio puede esperar recibir las instancias de objetos de valor con instancias completas:
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á!
Symfony es lo suficientemente flexible e intuitivo como para que fuera sencillo descubrir cómo hacerlo. Como no pude encontrar una referencia rápida para hacer esto en ningún otro lugar, pensé en escribir esto como referencia para Future Me y cualquier otra persona que necesite hacer algo similar
¡Salud y feliz codificación!
Descargo de responsabilidad: Todos los recursos proporcionados provienen en parte de Internet. Si existe alguna infracción de sus derechos de autor u otros derechos e intereses, explique los motivos detallados y proporcione pruebas de los derechos de autor o derechos e intereses y luego envíelos al correo electrónico: [email protected]. Lo manejaremos por usted lo antes posible.
Copyright© 2022 湘ICP备2022001581号-3