Есть ли способ переопределить значение контейнера Symfony с помощью аргумента консоли?

avatar
TravisCarden
1 июля 2021 в 21:37
137
0
2

tl;dr: У меня есть конфигурация сервиса Symfony, которая связывает строковое значение по умолчанию с именованным параметром, и я хочу переопределить его в процессе консоли с помощью значения параметра командной строки.

Я работаю над библиотекой Composer (php-tuf/composer-stager), которая предоставляет классы с открытыми интерфейсами и консольным приложением Symfony, которое предоставляет им интерфейс оболочки. Для API кода я хочу привязать значения по умолчанию (строковые) к нескольким аргументам службы, чтобы клиентам не приходилось включать аргументы в каждый вызов моих доменных служб, но они все же могли переопределять значения по умолчанию в своей собственной конфигурации служб.

Но в моем собственном консольном приложении я хочу иметь возможность переопределять эти значения по умолчанию на основе параметров командной строки, которые может указать пользователь. Например, я хочу, чтобы $string по умолчанию имело значение lorem, но я хочу, чтобы моя консоль переопределяла значение по умолчанию с помощью параметра, например. --string=ipsum. Но к тому времени, когда консольное приложение обрабатывает пользовательский ввод, я не могу найти нигде, чтобы изменить значение параметра в контейнере. Вот упрощенный пример кода:

.
<?php
// bin/example.php, my console command front script

$containerBuilder = new ContainerBuilder();
$loader = new YamlFileLoader($containerBuilder, new FileLocator());
$loader->load(__DIR__ . '/../config/services.yml');

// This is the last chance to override defaults before the container
// gets compiled, but I'm reluctant to do so because I would have
// to parse the command line options myself, which would be ugly if
// not unwise.

$containerBuilder->compile();
$application = $containerBuilder->get(Application::class);
return $application->run();
# config/services.yml

services:
    _defaults:
        bind:
            $string: lorem
            
    # These classes constitute my public interface, 
    # and they take the $string parameter.
    My\App\Domain\:
        resource: '../src/Domain/*'

    # These are the Console commands. They need to
    # change the value of $string before it gets
    # injected into the domain classes above.
    My\App\Console\Commands\:
        resource: '../src/Console/Commands/*'
<?php
// src/Domain/Example.php

// By the time this class is instantiated, the container has already been
// compiled, and it's received the $string variable from the services default.
class Example
{
    public function __construct(string $string)
    {
        $this->string = $string;
    }

    public function printString()
    {
        print $this->string;
    }
}
// src/Console/Application.php

// My Applilcation class is the first place (I think) I have access to
// console command input, but the container has already been compiled and
// services injected into my domain objects by the time it's loaded.
final class Application extends \Symfony\Component\Console\Application {}
<?php
// src/Console/Command/ExampleCommand.php

class ExampleCommand
{
    public function __construct(Example $example)
    {
        // At this point the Example class has ALREADY had the
        // $string value injected from the services config.
        $this->example = $example;
    }

    public function execute(InputInterface $input, OutputInterface $output): int
    {
        // ...but I don't get access to the '--override' option until here.
        $override = $input->getOption('override');

        // I'm not WILLING to take $string as a method parameter like this:
        $this->example->printString($override);
        
        // I don't really WANT to add a setter method to the domain (Example)
        // class: It would be too easy for a developer to forget to do this in
        // new commands and introduce bugs.
        $this->example->setString($override);
        $this->example->printString();
        
        // I want the Example class to have gotten the overridden value in
        // the first place so I can call it without every class having to
        // worry about it:
        $this->example->printString();
    }
}

Есть идеи?

Вы можете увидеть мою текущую кодовую базу по адресу https://github.com/php-tuf/composer-stager/tree/v0.1.0.

Примечание. В настоящее время мое приложение должно поддерживать Symfony 4 и 5.

Источник
john Smith
3 июля 2021 в 10:54
0

Я бы использовал метод установки для класса домена. Или, глядя на репозиторий, вы найдете способ переопределить в AbstractCommand, чтобы разработчики, которые просто расширяют его, не беспокоились о том, чтобы забыть

Cerad
3 июля 2021 в 11:58
0

Могу поклясться, что прокомментировал это вчера. Ну что ж. После того, как контейнер скомпилирован, вы не можете вносить в него изменения. Конец истории. Вместо этого рассмотрите возможность использования ExampleFactory.

Ответы (0)