Во время работы над проектом Symfony с моей командой мне нужно было внедрить определенные экземпляры Value Object в один из моих сервисов. Сами значения в данном конкретном случае необходимо было установить из значений, предоставленных в нашем файле .env.
Конечно, я мог бы просто передать строковые значения непосредственно в мой сервис и заставить его создавать экземпляры объектов значений в конструкторе, но я хотел посмотреть, можно ли настроить его в файле Services.yaml и внедрить вместо этого полностью созданные объекты. Это позволило бы мне передавать эти экземпляры объектов нескольким сервисам и не повторять создание объекта значения внутри каждого.
Вот как я это сделал...
Наше приложение использует Twilio SDK. У нас есть различные службы, обертывающие вызовы SDK, и им необходимо использовать значения конфигурации, специфичные для нашей среды (ключ API нашей компании для каждой среды и т. д.).
API Twilio использует строковые идентификаторы или SID. С каждым типом SID связан свой двухбуквенный префикс, за которым следуют 32 символа, состоящие из цифр от 0 до 9 и букв от A до F (прописных и строчных букв).
Например:
Я хотел убедиться, что объекты значений для каждого типа SID подтвердили, что переданное значение имеет правильный префикс для этого типа SID, а также убедиться, что строка имеет правильную длину и состоит только из разрешенных символов. .
Каждый из моих типов SID использует одну и ту же логику проверки и функциональность, отличаясь только префиксом типа SID, поэтому имеет смысл создать базовую черту. Если хотите, это может быть абстрактный класс. Мне не нужна концепция TwilioStringIdentifier в приложении как тип параметра или что-то в этом роде, поэтому я предпочитаю здесь Trait абстрактному классу.
Этот трейт определяет абстрактный метод getPrefixForSidType(), который должен реализовать каждый тип SID, предоставляя правильный префикс для этого данного типа. Он также выполняет логику проверки.
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; } }
Классы объектов значений, представляющие каждый из типов SID, просты. Им просто нужно использовать признак TwilioStringIdentifier и определить правильный префикс с помощью метода getPrefixForSidType().
namespace App\Domain\Model\Twilio\Sid; final readonly class AccountSid { use TwilioStringIdentifier; private function getPrefixForSidType(): string { return 'AC'; } }
Другие классы типов SID идентичны, за исключением определенного префикса.
Поскольку эти объекты значений будут использоваться во всем приложении и с различными связанными значениями, а не только с глобальными значениями нашей компании, мне нужен был способ внедрить в службы объект определенного типа, экземпляр которого уже был создан с определенными значениями. в нашем файле .env
Я знал, что Symfony имеет возможность определять сервисы, экземпляры которых будут создаваться через Фабрику, но никогда не видел (насколько я помню) ничего о внедрении объекта, который был результатом вызова метода откуда-то еще. Я также знал, что этим фабричным методам могут передаваться аргументы, но я просто не был уверен, как это сделать с одним экземпляром объекта значения.
Определение службы Symfony позволяет вам дать имя каждой службе. Обычно это делается с использованием имени класса обслуживания:
App\Path\To\My\Service: class: App\Path\To\My\Service arguments: []
Но имя этого сервиса не обязательно должно совпадать с именем класса. Это может быть app.my_service или Foo\Bar\Baz\Service или что-то еще.
Итак, что, если я создам службу с уникальным именем, которая является экземпляром нужного мне объекта значения? Я мог бы передать значение .env в качестве аргумента, а затем добавить этот экземпляр объекта в мои классы обслуживания!
# 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)%']
Затем передайте эти службы (объекты) в мою службу Twilio через их именованные аргументы:
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)%'
Теперь мой класс обслуживания может рассчитывать на получение полностью созданных экземпляров Value Object:
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 ) {} }
Вуаля!
Symfony достаточно гибок и интуитивно понятен, поэтому было легко понять, как это сделать. Поскольку я не смог найти краткое описание того, как сделать это где-либо еще, я решил написать это как справочник для Future Me и всех, кому, возможно, понадобится сделать что-то подобное
Удачи и удачного программирования!
Отказ от ответственности: Все предоставленные ресурсы частично взяты из Интернета. В случае нарушения ваших авторских прав или других прав и интересов, пожалуйста, объясните подробные причины и предоставьте доказательства авторских прав или прав и интересов, а затем отправьте их по электронной почте: [email protected]. Мы сделаем это за вас как можно скорее.
Copyright© 2022 湘ICP备2022001581号-3