Внедрение параметров в конструктор с помощью PHP-DI

Я изо всех сил пытаюсь заставить внедрение зависимости работать так, как я ожидаю -

Я пытаюсь внедрить класс Api, который должен знать, к какому серверу подключаться конкретному пользователю. Это означает, что переопределение свойств конструктора в файле конфигурации бесполезно, поскольку каждому пользователю может потребоваться подключиться к другому серверу.

class MyController {
    private $api;

    public function __construct(Api $api) {
        $this->api = $api;
    }
}

class Api {
     private $userServerIp;

     public function __construct($serverip) {
         $this->userServerIp = $serverip;
     }
}

Как я могу ввести этот класс с правильными параметрами? Можно ли как-то переопределить определение? Есть ли какой-нибудь способ получить класс, вызвав контейнер с параметрами?

Чтобы (надеюсь) уточнить - я пытаюсь вызвать контейнер для создания экземпляра объекта, передавая ему параметры, которые в противном случае были бы в определении.

2 ответа

Решение

Так как IP зависит от пользователя, у вас, вероятно, есть некоторая логика, которая выполняет отображение user=>serverIP. Это может быть чтение из базы данных или простой шард на основе идентификатора, или что-то еще. С этой логикой вы можете построить ApiFactory сервис, который создает Api для конкретного пользователя:

class ApiFactory {

    private function getIp(User $user) {
        // simple sharding between 2 servers based on user id
        // in a real app this logic is probably more complex - so you will extract it into a separate class

        $ips = ['api1.example.com', 'api2.example.com'];
        $i = $user->id % 2;
        return $ips[$i];
    }

    public function createForUser(User $user) {
        return new Api($this->getIp($user);
    }
}

Теперь вместо инъекций Api в ваш контроллер вы можете ввести ApiFactory (при условии, что ваш контроллер знает пользователя, для которого ему нужен экземпляр Api)

class MyController {
    private $apiFactory;

    public function __construct(ApiFactory $apiFactory) {
        $this->apiFactory = $apiFactory;
    }

    public function someAction() {
        $currentUser = ... // somehow get the user - might be provided by your framework, or might be injected as well
        $api = $this->apiFactory->createForUser($currentUser);
        $api->makeSomeCall();
    }
}

Я не уверен, что полностью понимаю ваш вопрос, но вы можете настроить свой Api класс как это:

return [
    'Foo' => function () {
        return new Api('127.0.0.1');
    },
];

Посмотрите на документацию для большего количества примеров или деталей: http://php-di.org/doc/php-definitions.html


Редактировать:

return [
    'foo1' => function () {
        return new Api('127.0.0.1');
    },
    'foo2' => function () {
        return new Api('127.0.0.2');
    },
];
Другие вопросы по тегам