„Wenn ein Arbeiter seine Arbeit gut machen will, muss er zuerst seine Werkzeuge schärfen.“ – Konfuzius, „Die Gespräche des Konfuzius. Lu Linggong“
Titelseite > Programmierung > Wertobjekte in einen automatisch verdrahteten Symfony-Dienst einfügen

Wertobjekte in einen automatisch verdrahteten Symfony-Dienst einfügen

Veröffentlicht am 16.08.2024
Durchsuche:833

Inject Value Objects Into An Autowired Symfony Service

Während ich mit meinem Team an einem Symfony-Projekt arbeitete, musste ich bestimmte Value Object-Instanzen in einen meiner Dienste einfügen. Die Werte selbst mussten in diesem speziellen Fall anhand der in unserer .env-Datei bereitgestellten Werte festgelegt werden.

Ich könnte die Zeichenfolgenwerte natürlich einfach direkt an meinen Dienst übergeben und den Dienst die Wertobjekte im Konstruktor instanziieren lassen, aber ich wollte sehen, ob es möglich ist, sie in der Datei „services.yaml“ zu konfigurieren und einzufügen stattdessen die vollständig instanziierten Objekte. Dies würde es mir ermöglichen, diese Objektinstanzen an mehrere Dienste zu übergeben und die Erstellung des Wertobjekts nicht in jedem einzelnen Dienst wiederholen zu müssen.

So habe ich es gemacht...

Hintergrund

Unsere Anwendung nutzt das Twilio SDK. Wir haben verschiedene Dienste, die die SDK-Aufrufe umschließen, und sie müssen unsere umgebungsspezifischen Konfigurationswerte verwenden (den API-Schlüssel unseres Unternehmens für jede Umgebung usw.).

Die Twilio-API verwendet String-Identifikatoren oder SIDs. Jedem SID-Typ ist ein anderes Präfix aus zwei Buchstaben zugeordnet, gefolgt von 32 Zeichen bestehend aus den Ziffern 0 bis 9 und den Buchstaben A bis F (Groß- und Kleinschreibung).

Zum Beispiel:

  • Eine ConferenceSid hat das Präfix CF und sieht aus wie CFabcdef0123456789abcdef0123456789
  • Eine CallSid hat das Präfix CA und sieht aus wie CAabcdef0123456789abcdef0123456789
  • Es gibt andere Arten von SIDs, die alle dasselbe Format verwenden und sich nur durch das Präfix unterscheiden.

Ich wollte sicherstellen, dass die Wertobjekte für jeden SID-Typ validieren, dass der übergebene Wert das richtige Präfix für diesen SID-Typ hat, und außerdem sicherstellen, dass die Zeichenfolge die richtige Länge hat und nur aus den zulässigen Zeichen besteht .

Die Wertobjekte

Jeder meiner SID-Typen verwendet dieselbe Validierungslogik und Funktionalität und unterscheidet sich nur durch das Präfix des SID-Typs. Daher ist es sinnvoll, ein Basismerkmal zu erstellen. Wenn Sie möchten, könnte dies auch eine abstrakte Klasse sein. Ich brauche das Konzept eines TwilioStringIdentifier in der App nicht als Parametertyp oder ähnliches, daher bevorzuge ich hier ein Trait gegenüber einer Abstract Class.

Dieses Merkmal definiert eine abstrakte Methode getPrefixForSidType(), die jeder SID-Typ implementieren muss und das richtige Präfix für diesen bestimmten Typ bereitstellt. Es führt auch die Validierungslogik aus.

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

Die SID-Klassen

Die Wertobjektklassen, die jeden der SID-Typen darstellen, sind einfach. Sie müssen lediglich das TwilioStringIdentifier Trait verwenden und das richtige Präfix über die Methode getPrefixForSidType() definieren.

namespace App\Domain\Model\Twilio\Sid;

final readonly class AccountSid
{
    use TwilioStringIdentifier;

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

Die anderen SID-Typklassen sind bis auf ihr definiertes Präfix identisch.

Injizieren der instanziierten Objekte

Da diese Wertobjekte in der gesamten Anwendung und mit verschiedenen zugeordneten Werten und nicht nur mit den globalen Werten unseres Unternehmens verwendet werden, brauchte ich eine Möglichkeit, in Dienste ein Objekt eines bestimmten Typs einzufügen, das bereits mit den definierten Werten instanziiert wurde in unserer .env-Datei

Ich wusste, dass Symfony die Möglichkeit hat, Dienste zu definieren, die über eine Factory instanziiert werden sollen, hatte aber (soweit ich mich erinnere) noch nie etwas über das Einfügen eines Objekts gesehen, das das Ergebnis eines Methodenaufrufs von irgendwo anders war. Ich wusste auch, dass an diese Factory-Methoden Argumente übergeben werden könnten, ich war mir nur nicht sicher, wie ich das mit einer Value Object-Instanz machen sollte.

Definieren der spezifischen Wertobjektinstanz

Mit der Dienstdefinition von Symfony können Sie jeden Dienst benennen. Normalerweise geschieht dies mit dem Namen der Service-Klasse:

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

Dieser Dienstname muss jedoch nicht mit dem Klassennamen übereinstimmen. Es könnte app.my_service oder Foo\Bar\Baz\Service oder was auch immer sein.

Was passiert also, wenn ich einen Dienst mit einem eindeutigen Namen erstelle, der die instanziierte Instanz des von mir benötigten Wertobjekts ist? Ich könnte den .env-Wert als Argument übergeben und dann diese Objektinstanz in meine Serviceklassen einfügen!

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

Übergeben Sie diese Dienste (Objekte) dann über ihre benannten Argumente an meinen Twilio-Dienst:

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

Jetzt kann meine Serviceklasse damit rechnen, die vollständig instanziierten Value Object-Instanzen zu erhalten:

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 ist flexibel genug und intuitiv genug, dass es einfach war, herauszufinden, wie das geht. Da ich nirgendwo eine schnelle Referenz dafür finden konnte, dachte ich, ich schreibe dies als Referenz für Future Me und alle anderen auf, die möglicherweise etwas Ähnliches tun müssen

Prost und viel Spaß beim Codieren!

Freigabeerklärung Dieser Artikel ist abgedruckt unter: https://dev.to/mbadolato/inject-value-objects-into-an-autowired-symfony-service-3an2?1 Bei Verstößen wenden Sie sich zum Löschen bitte an [email protected] Es
Neuestes Tutorial Mehr>

Haftungsausschluss: Alle bereitgestellten Ressourcen stammen teilweise aus dem Internet. Wenn eine Verletzung Ihres Urheberrechts oder anderer Rechte und Interessen vorliegt, erläutern Sie bitte die detaillierten Gründe und legen Sie einen Nachweis des Urheberrechts oder Ihrer Rechte und Interessen vor und senden Sie ihn dann an die E-Mail-Adresse: [email protected] Wir werden die Angelegenheit so schnell wie möglich für Sie erledigen.

Copyright© 2022 湘ICP备2022001581号-3