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}()
}
...