Maison > développement back-end > tutoriel php > Injecter des objets de valeur dans un service Symfony autocâblé

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

王林
Libérer: 2024-08-11 11:16:32
original
574 Les gens l'ont consulté

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;
    }
}    
Copier après la connexion

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';
    }
}
Copier après la connexion

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

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: []
Copier après la connexion

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 !

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)%']
Copier après la connexion

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)%'
Copier après la connexion

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
    ) {}
}
Copier après la connexion

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!

source:dev.to
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal