rdlowrey / auryn

IoC Dependency Injector
MIT License
723 stars 65 forks source link

Pass custom arguments to a constructor on $injector->execute() #162

Closed plashenkov closed 1 year ago

plashenkov commented 7 years ago

Hello!

In execute() method we can pass parameters to a callable, but cannot pass parameters to a class constructor.

Example:

class Foo
{
    public function __construct($constructorArg)
    {
        var_dump($constructorArg);
    }

    public function someMethod($methodArg)
    {
        var_dump($methodArg);
    }
}

$injector->execute('Foo::someMethod', [
    'constructorArg' => 'some-value',
    'methodArg' => 'another-value'
]);

How is it possible to get Auryn inject non-class arguments into a class constructor? Real life example: I have many controllers; I would like to use arguments injection in constructor as well as in methods. I do not want to call define() for each controller in the app. Sure in theory I can parse the 'Foo::someMethod' string myself and instantiate the controller object, but parsing is already nice done inside Auryn (execute() and buildExecutable()), I do not want to duplicate this functionality. Maybe there is some beautiful method to achieve this?

If not, does it make sense to apply args in execute() to a constructor too, not only to a method call?

Danack commented 7 years ago

How is it possible to get Auryn inject non-class arguments into a class constructor?

$injector->defineParam($name, $value);

I have many controllers; I would like to use arguments injection in constructor as well as in methods. I do not want to call define() for each controller in the app

Where are the args coming from, that you need to define them per controller? Or is this just in a HTTP router scenario?

plashenkov commented 7 years ago

Yep. Router. Args are args from a route. They go to a controller method as well as to a controller itself to provide the user a controller method (let's say $this->getRouteParam('paramName')) located at parent controller class. I.e. the user can get arguments from the route handler (by injecting $args parameter) or by using getRouteParam() method.

Of course, all this can be designed in a different way. For example, through a separate class Request, which will provide route arguments (as well as other methods). Then user can inject this class where he wants it.

Despite this, we yet unable to fully control all constructor arguments when object is created during execute(). defineParam() is global, some local control maybe necessary. Maybe optional third param $constructorArgs = [] for execute() which will be used only for object instantiation (to split up callable's args from constructor's args). What do you think?

Regarding the router scenario: if you can advice something about the right way to do this, I will be grateful. How would you do this (and do in your projects)? Thanks!

Danack commented 7 years ago

Yep. Router. Args are args from route.

In that case, this is what most people do, for what I understand to be your requirements:

        $route = $router->match($request);

        // When route is like "/products/:product_id" and the visitor is on "/product/5"
        // $route->getParams() will return "['product_id' => 5]"
        foreach ($route->getPararms() as $key => $value) {
            $injector->defineParam($key, $value);
        }

       $injector->execute($route->getCallable());

Of course, all this can be designed in a different way. For example, through a separate class Request,

I'm fond of making a more specific type to represent a bag of variables, like variable map, as that makes it easier to unit test controllers.

plashenkov commented 7 years ago

$injector->defineParam($key, $value);

Well, after that these parameters will be available everywhere, not only for this object and this object's method. In each class which will be instantiated via injector further.

This is why I find it useful to have an additional argument in execute() which will allow to exhaustively control instantiating of an object inside this execute() method.

Danack commented 1 year ago

I'm going to close this issue on the grounds that:

$route_injector = clone $injector;
$route_injector->defineParam($key, $value);
$route_injector->execute($route->getCallable());