Search code examples
phpdependency-injectionslimslim-3

Slim Framework 3 - How to inject $logger to route controller


I am using Slim Framework 3. I want to inject $logger defined in dependencies.php into a Router Controller class. Below is what I do, is there a better way?

routes.php

$app->get('/test', function($request, $response, $args){
  $controller = new AccountController($this->get('logger'));
  return $controller->test($request, $response, $args);
});

AccountController

class AccountController{

    private $logger;
    function __construct($logger){
        $this->logger = $logger;
    }

    public function test($request, $response, $args){
        $this->logger->info('i am inside controller');
        return $response->withHeader('Content-Type', 'application/json')->write('test');
    }
}

In Slim Framework 3 documentation, the proper way of using a Route Controller should be:

$app->get('/test', 'AccountController:test');

But how do I inject $logger into AccountController when I choose to code my Route Controller in this more "elegant" way?


Solution

  • According to the container resolution docs, you should be able to access your logger through the container, inside your controller:

    AccountController

    class AccountController
    {
        protected $ci;
    
        //Constructor
        public function __construct(ContainerInterface $ci) 
        {
            $this->ci = $ci;
        }
    
        public function test($request, $response, $args)
        {
            $this->ci->get('logger')->info('i am inside controller');
            return $response->withHeader('Content-Type', 'application/json')->write('test');
        }
    }
    

    When you call $app->get('/test', 'AccountController:test');, Slim should automatically pass the container into AccountController's constructor.

    That being said, this is more of a convenience feature than an example of great design. As Rob Allen explains in his answer, you can achieve better modularity, and thus more easily tested code (if you're using unit tests), by injecting the controllers into the application container, rather than injecting the container into each controller.

    Take a look at his example Slim application. If you look at, for example AuthorController, you can see how with this design controller classes no longer depend on the magical container providing all the services. Instead, you explicitly state which services each controller will need in the constructor. This means you can more easily mock the individual dependencies in testing scenarios.