"Si un ouvrier veut bien faire son travail, il doit d'abord affûter ses outils." - Confucius, "Les Entretiens de Confucius. Lu Linggong"
Page de garde > La programmation > Injecter des objets de valeur dans un service Symfony autocâblé

Injecter des objets de valeur dans un service Symfony autocâblé

Publié le 2024-08-16
Parcourir:361

Inject Value Objects Into An Autowired Symfony Service

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é...

Arrière-plan

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:

  • Un ConferenceSid a le préfixe CF et ressemble à CFabcdef0123456789abcdef0123456789
  • Un CallSid a le préfixe CA et ressemble à CAabcdef0123456789abcdef0123456789
  • Il existe d'autres types de SID et ils utilisent tous le même format, différenciés uniquement par le préfixe

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. .

Les objets de valeur

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 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.

Injection des objets instanciés

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 également que ces méthodes Factory pouvaient recevoir des arguments, mais je ne savais tout simplement pas comment procéder avec une seule instance d'objet de valeur.

Définition de l'instance d'objet de valeur spécifique

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 Foo\Bar\Baz\Service ou autre.

Alors, que se passe-t-il 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 !

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

Transmettez ensuite 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 :

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

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 susceptible d'avoir besoin de faire quelque chose de similaire

Bravo et bon codage !

Déclaration de sortie Cet article est reproduit sur : https://dev.to/mbadolato/inject-value-objects-into-an-autowired-symfony-service-3an2?1 En cas de violation, veuillez contacter [email protected] pour supprimer il
Dernier tutoriel Plus>

Clause de non-responsabilité: Toutes les ressources fournies proviennent en partie d'Internet. En cas de violation de vos droits d'auteur ou d'autres droits et intérêts, veuillez expliquer les raisons détaillées et fournir une preuve du droit d'auteur ou des droits et intérêts, puis l'envoyer à l'adresse e-mail : [email protected]. Nous nous en occuperons pour vous dans les plus brefs délais.

Copyright© 2022 湘ICP备2022001581号-3