0

In a MVC context, I have a controller that depends on a service, the service in turn depends on a data_source (in the specific case, a client to fetch data from a third-party API).

In order to instantiate the service with a mock data_source when testing, the service's constructor expects a data_source. The same holds for the controller, whose constructor expects a service.

When creating a controller, I want to pass it a request object as well, because I'd prefer this

new Controller(request, service).action_name

to this

new Controller(service).action_name(request)

Achieving this without using any container for Dependency Injection is trivial.

What I don't understand is how to do so using php-di

My objective is to have the service injected into the controller by the container, while passing the request object to the controller myself.

UPDATE 1

This is my ApplicationController

namespace DEC;


class ApplicationController {

    private $service;
    private $request;

    public function __construct(Foo $service, $request) {
        $this->service= $service;
        $this->request = $request;
    }


    public function index() {
        $out = $this->service->foo();
        $out .= $this->request->method();
        return $out;
    }

}

Foo follows

namespace DEC;

class Foo {

    public function __construct() {
    }

    public function foo() {
        return "FOO";
    }
}

This my Request

namespace DEC;

class Foo {

    public function __construct() {
    }

    public function foo() {
        return "FOO";
    }
}

And this is my attempt to get DI to work as I'd like:

$container = ContainerBuilder::buildDevContainer();
$response = $container->call([ApplicationController::class, 'index'], [
            'request' => new Request('GET')
]);
echo $response;

This is the error I get:

Entry "DEC\ApplicationController" cannot be resolved: Parameter $request of __construct() has no value defined or guessable
Full definition:
Object (
    class = DEC\ApplicationController
    scope = singleton
    lazy = false
    __construct(
        $service = get(DEC\Foo)
        $request = #UNDEFINED#
    )
)

N.B.: the error stays the same if I type-hint the request and/or switch the order of params in the constructor

Looking at the error, I infer that the ::call() solution proposed by Matthew Napoli works if I instantiate the controller with just the service and pass the request as a parameter for the action method.

Does this mean that I can't rely on the container for "partial" injection?

UPDATE 2

For the solution described in this update, please look at my own answer to the question

4

3 回答 3

0

目前还不是很清楚你尝试了什么,但这应该调用 action 方法并将请求传递给它(并解析控制器及其所有依赖项):

$container->call([MyController::class, 'action_name'], [
    'request' => $request,
]);

在此处阅读更多信息:http call(): //php-di.org/doc/container.html#call

于 2017-04-28T16:15:40.850 回答
0

关于请求:

不要键入提示 $request 参数:

public function __construct($request, /*...*/) {}

或者为它键入一个 RequestInterface:

public function __construct(RequestInterface $request, /*...*/) {}

在这两种情况下,DIC 都无法自动创建请求实例。然后你可以自己注入。

关于服务:

使用具体的类型提示它,例如“服务”。然后,DIC 将自动创建一个服务实例及其所有依赖项(data_source)。

或者使用接口(如“ServiceInterface”)对其进行类型提示,并使用 PHP 定义中的别名在 DIC 中为其设置一个条目。像这样的东西:

return [
    'ServiceInterface' => DI\get('<NAMESPACE-TO>\Service'),
];

看:

PHP-DI“限制”在http://php-di.org/doc/autowiring.html#limitations

PHP-DI “PHP 定义 - 别名”,位于http://php-di.org/doc/php-definitions.html#aliases

希望这有帮助。

于 2017-04-30T16:27:16.403 回答
0

我设法通过在请求控制器之前在容器中设置我的请求来做到这一点:

$container->set('DEC\Request', new Request('GET'));
$controller = $container->get('DEC\ApplicationController');
$response = $controller->index();
于 2017-05-05T08:53:43.197 回答