0

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}()
    }
...
  • I wouldn't & didn't in my recent project. Instead, I created a global helper function that would provide the DIC when I needed it. Laravel does similar things. Like the `app()` function. – bassxzero Nov 16 '17 at 18:54
  • Yep that idea was also inspired by Laravel. But the `app()` helper still does `Container::getInstance()`. So it's just an alias? – Александр Клявлин Nov 16 '17 at 19:01
  • A lot of people will tell you not to use a singleton for various reasons. You actually don't have to make your container a singleton, if you can insure that it only gets instantiated once. – bassxzero Nov 16 '17 at 19:04
  • And I dont want to use singleton too. But the only other way I see is to pass it as a dependency through a lot of classes – Александр Клявлин Nov 16 '17 at 19:13

0 Answers0