Is it OK to pass DI Contantainer as a dependency through a long chain of classes if only last class needs it exactly?

79 Views Asked by At

I'm trying to implement a router in my application using DI container.

So I have an Application class which loads a config and has a DI container instance. Application has Router as a service class instaniated by DI. Router has an array of Route classes, Route has Action and Action has Target. Target is Closure or controller class name + controller method name. I want to instaniate controller by DI Container because I want to inject some services.

Here is example structure.

class Application {

    private $container;

    private $router;

    public function __construct() {
        $this->container = new DIContainer();
        $this->container->add(SomeService::class, function(){
            return new SomeService($someConfigParams);
        })
        $this->router = $this->container->get(Router::class);
    }

    public function handle($uri){
        return $this->router->handle($uri);
    }

}

class Router {

    private $container;

    private $routes; //Array of Route class filled by addRoute 

    public function __construct (Container $container){
      $this->container = $container;
    }

    public function addRoute($uri){
      $routes[] = $this->container->make(Route::class, ['uri' => $uri]);
    }  

    public function handle ($uri){
      $route = $this->findRoute($uri)->getAction()->run()
    }

}

class Route {

    private $container;

    private $action; //instance of Action class filled by setAction

    public function __construct(Container $container){
        $this->container = $container;
    }

    public function setAction($params){
        $this->action = $this->container->make(Action::class, $params);
    }

    public function getAction(){
        return $this->action();
    }
}

class Action {

    private $container;

    private $target; //Instance of Target class filled by setTarget

    public function __construct(Container $container){
        $this->container = $container;
    }

    public function setTarget(){
        $this->target = $this->container->make(Targer::class)
    }

    public function run(){
        return $this->target->run();
    }
}

class Target {
    private $container;

    private $controllerClass;

    private $controllerMethod;

    public function __construct(Container $container, $class, $method){
        $this->container = $container;
        $this->controllerClass = $class;
        $this->controllerMethod = $method;
    }

    public function run(){
        return $this->container->make($this->controllerClass)->{$this->controllerMethod}();
    }
}

//And finally!

class Controller {

    private $someService;

    public function __construct(SomeService $someService) {
        $this->someService = $someService;
    }

    public function test(){
      return $this->someService->sayHello();
    }

}

new Application()->handle('/controller/test/');

Excuse me for such large code example. But I really wonder is it OK that all the classes in chain depend on $container only because controller needs a service. Or should I just make my Container class global (e.g. singleton) and call it at the last step? Like this:

...
  // Target class
    public function run() {
        return Container::getInstance()->make($this->controllerClass)->{$this->controllerMethod}()
    }
...
0

There are 0 best solutions below