Как заставить Symfony 4 внедрения зависимостей работать с двумя различными сценариями использования ios? - PullRequest
0 голосов
/ 26 февраля 2020

Мы пытаемся найти лучший способ реализации внедрения зависимостей в Symfony проекте с весьма специфическими c проблемными c.

На пользовательском уровне наше приложение полагается на " Учетная запись "doctrine сущность, которая загружается с помощью глобального HTTP_HOST для свойства домена (многодоменное приложение). Переход на домен example.domain.tld загрузит соответствующие сущность и настройки.

На уровне devops нам также необходимо выполнить пакетную работу со сценариями CLI для многих учетных записей одновременно.

Вопрос, с которым мы сталкиваемся, заключается в том, как написать сервисы, которые будут совместимы с обоими потребностями?

Давайте проиллюстрируем это на упрощенном примере. Для уровня пользователя у нас это есть, и все прекрасно работает:

Controller / FileController. php

    public function new(Request $request, FileManager $fileManager): Response
    {
        ...
        $fileManager->addFile($file);
        ...

    }

Service / FileManager. php

    public function __construct(AccountFactory $account)
    {
        $this->account = $account;
    }

Service / AccountFactory. php

    public function __construct(RequestStack $requestStack, AccountRepository $accountRepository)
    {
        $this->requestStack = $requestStack;
        $this->accountRepository = $accountRepository;
    }

    public function createAccount()
    {
        $httpHost = $this->requestStack->getCurrentRequest()->server->get('HTTP_HOST');
        $account = $this->accountRepository->findOneBy(['domain' => $httpHost]);

        if (!$account) {
            throw $this->createNotFoundException(sprintf('No matching account for given host %s', $httpHost));
        }

        return $account;
    }

Теперь, если мы захотим написать следующую консольную команду, произойдет сбой, поскольку FileManager принимает только AccountFactory, а не учетную запись.

$accounts = $accountRepository->findAll();
foreach ($accounts as $account) {
    $fileManager = new FileManager($account);
    $fileManager->addFile($file);
}

Мы могли бы настроить AccountFactory, но это было бы неправильно ... На самом деле это еще хуже, потому что зависимость от учетной записи глубже в услугах.

Кто-нибудь знает, как сделать это правильно?

1 Ответ

1 голос
/ 27 февраля 2020

Рекомендуется создать интерфейс для FileManager и установить этот FileManagerInterface в качестве внедрения зависимости (вместо FileManager). Затем у вас могут быть разные классы, которые следуют одним и тем же правилам интерфейса, но просто имеют другой конструктор.

При таком подходе вы можете реализовать что-то вроде:

Service / FileManager. php

interface FileManagerInterface
{
    // declare the methods that must be implemented
    public function FileManagerFunctionA();
    public function FileManagerFunctionB(ParamType $paramX):ReturnType;
}

FileManagerInterface. php

class FileManagerBase implements FileManagerInterface
{
    // implement the methods defined on the interface
    public function FileManagerFunctionA()
    {
        //... code
    }

    public function FileManagerFunctionB(ParamType $paramX):ReturnType
    {
        //... code
    }
}

FileManagerForFactory. php

class FileManagerForFactory implements FileManagerInterface
{
    // implement the specific constructor for this implementation
    public function __construct(AccountFactory $account)
    {
        // your code here using the account factory object
    }
    // additional code that is needed for this implementation and that is not on the base class
}

FileManagerAnother. php

class FileManagerForFactory implements FileManagerInterface
{
    // implement the specific constructor for this implementation
    public function __construct(AccountInterface $account)
    {
        // your code here using the account object
    }
    // additional code that is needed for this implementation and that is not on the base class
}

Ответ И последнее, но не менее важное:

Controller / FileController. php

public function new(Request $request, FileManagerInterface $fileManager): Response
{
    // ... code using the file manager interface
}

Другой подход, который также выглядит правильно, предполагает, что FileManager зависит от AccountInstance для работы, изменения можно сделать так, чтобы ваша FileManager зависимость имела AccountInstance в качестве зависимости вместо Factory. Просто потому, что на самом деле FileManager не нуждается в фабрике, ему нужен результат, который генерирует фабрика, поэтому, автоматически, FileManager не несет ответственности за всю фабрику.

При таком подходе вы будете только необходимо изменить ваши объявления, такие как:

Service / FileManager. php

public function __construct(AccountInterface $account)
{
    $this->account = $account;
} 

Service / AccountFactory. php

public function createAccount():AccountInterface
{
    // ... your code
}
...