Alors que je travaillais sur un projet Symfony avec mon équipe, j'avais besoin d'injecter des instances Value Object spécifiques dans l'un de mes services. Les valeurs elles-mêmes, dans ce cas particulier, devaient être définies à partir des valeurs fournies dans notre fichier .env.
Je pourrais, bien sûr, simplement transmettre les valeurs de chaîne directement dans mon service et demander au service d'instancier les objets de valeur dans le constructeur, mais je voulais voir s'il était possible de le configurer dans le fichier services.yaml et d'injecter les objets entièrement instanciés à la place. Cela me permettrait de transmettre ces instances d'objet à plusieurs services et de ne pas avoir à répéter la création de l'objet de valeur à l'intérieur de chacun.
Voici comment j'ai procédé...
Notre application utilise le SDK Twilio. Nous disposons de divers services encapsulant les appels du SDK et ils doivent utiliser les valeurs de configuration spécifiques à notre environnement (la clé API de notre entreprise pour chaque environnement, etc.).
L'API Twilio utilise des identifiants de chaîne, ou SID. Chaque type de SID est associé à un préfixe différent de 2 lettres, suivi de 32 caractères composés des chiffres de 0 à 9 et des lettres de A à F (majuscules et minuscules).
Par exemple :
Je voulais m'assurer que les objets de valeur pour chaque type SID validaient que la valeur transmise avait le préfixe approprié pour ce type SID, tout en m'assurant que la chaîne avait la bonne longueur et n'était composée que des caractères autorisés. .
Chacun de mes types SID utilise la même logique et fonctionnalité de validation, se différenciant uniquement par le préfixe du type SID, il est donc logique de créer un trait de base. Cela pourrait être une classe abstraite si vous préférez. Je n'ai pas besoin du concept de TwilioStringIdentifier dans l'application en tant que type de paramètre ou quelque chose comme ça, donc je préfère ici un trait à une classe abstraite.
Ce trait définit une méthode abstraite getPrefixForSidType() que chaque type SID doit implémenter, fournissant le préfixe approprié pour ce type donné. Il exécute également la logique de validation.
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; } }
Les classes Value Object représentant chacun des types SID sont simples. Il leur suffit d'utiliser le trait TwilioStringIdentifier et de définir le préfixe approprié via la méthode getPrefixForSidType().
namespace App\Domain\Model\Twilio\Sid; final readonly class AccountSid { use TwilioStringIdentifier; private function getPrefixForSidType(): string { return 'AC'; } }
Les autres classes de types SID sont identiques à l'exception de leur préfixe défini.
Parce que ces Objets de Valeur seront utilisés tout au long de l'application et avec diverses valeurs associées, et pas seulement les valeurs globales de notre entreprise, j'avais besoin d'un moyen d'injecter dans les services un objet d'un type spécifique déjà instancié avec les valeurs définies dans notre fichier .env
Je savais que Symfony avait la capacité de définir des services à instancier via une Factory mais je n'avais jamais vraiment vu (d'après mes souvenirs) quoi que ce soit sur l'injection d'un objet qui était le résultat d'un appel de méthode depuis ailleurs. Je savais aussi que ces méthodes Factory pouvaient recevoir des arguments, je ne savais tout simplement pas comment faire cela avec une seule instance d'objet de valeur.
La définition de service de Symfony vous permet de nommer chaque service. Généralement, cela se fait avec le nom de la classe Service :
App\Path\To\My\Service: class: App\Path\To\My\Service arguments: []
Mais ce nom de service ne doit pas nécessairement correspondre au nom de la classe. Cela pourrait être app.my_service ou FooBarBazService ou autre.
Et si je crée un service avec un nom unique qui est l'instance instanciée de l'objet de valeur dont j'ai besoin ? Je pourrais transmettre la valeur .env comme argument, puis injecter cette instance d'objet dans mes classes de service !
# 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)%']
Ensuite, transmettez ces services (objets) dans mon service Twilio via leurs arguments nommés :
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)%'
Maintenant, ma classe de service peut s'attendre à recevoir les instances d'objet de valeur entièrement instanciées :
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 ) {} }
Voila !
Symfony est suffisamment flexible et intuitif pour qu'il soit simple de comprendre comment procéder. Comme je n'ai pas trouvé de référence rapide pour faire cela ailleurs, j'ai pensé écrire ceci comme référence pour Future Me et toute autre personne qui pourrait avoir besoin de faire quelque chose de similaire
Bravo et bon codage !
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!