In Laravel documentation I see example:
$this->app->when(PhotoController::class)
->needs(Filesystem::class)
->give(function () {
return Storage::disk('local');
});
$this->app->when([VideoController::class, UploadController::class])
->needs(Filesystem::class)
->give(function () {
return Storage::disk('s3');
});
https://laravel.com/docs/8.x/container#contextual-binding
I want make the same with diffenet Log channels insead Storage disks.
https://laravel.com/docs/8.x/logging#writing-to-specific-channels
I try:
public function __construct(LoggerInterface $logger) {
$this->logger = $logger;
}
$this->app->when(PhotoController::class)
->needs('log')
->give(function () {
return \Log::channel('telegram');
});
$this->app->when([VideoController::class, UploadController::class])
->needs('log')
->give(function () {
return \Log::channel('slack');
});
But I get error:
NOTICE: PHP message: PHP Fatal error: Uncaught Error: Maximum function nesting level of '256' reached, aborting! in /var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Application.php:792
Stack trace:
#0 /var/www/html/vendor/laravel/framework/src/Illuminate/Container/Container.php(646): Illuminate\Foundation\Application->resolve('Illuminate\\Log\\...')
#1 /var/www/html/app/Providers/AppServiceProvider.php(106): Illuminate\Container\Container->get('Illuminate\\Log\\...')
#2 /var/www/html/vendor/laravel/framework/src/Illuminate/Container/Container.php(805): App\Providers\AppServiceProvider->App\Providers\{closure}(Object(Illuminate\Foundation\Application), Array)
#3 /var/www/html/vendor/laravel/framework/src/Illuminate/Container/Container.php(691): Illuminate\Container\Container->build(Object(Closure))
#4 /var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Application.php(796): Illuminate\Container\Container->resolve('log', Array, true)
#5 /var/www/html/vendor/laravel/framework/src/Illuminate/Container/Container.php(646): in /var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Application.php on line 792
Also I tried:
->needs(LoggerInterface::class)
And the same error.
I do not see examples of how to do this correctly in the documentation. There is nothing about it.
Sidenote: you can bind to an interface, its a common usage, Laravel calls them 'Contracts', see https://laravel.com/docs/8.x/container#binding-interfaces-to-implementations . So that is not the issue here.
Onto the question: I can't quite figure out what I'm doing differently but it works as intended for me:
However, the
'log'
string I dont quite like here. I believe it refers to the name of the so called "facade" for the logger, which brings more cognitive overload to what exactly this refers to. Imho its better to use a full class name here, to be more explicit:You can then use the following snippet in any of your controllers:
In fact you have a couple of options of defining the when/needs/give:
Illuminate\Log\Logger::class
Psr\Log\LoggerInterface::class
(Logger above implements this)Both work, but in both cases you need to use the exact class in your (to be injected) constructor exactly as written. You cannot bind the
LoggerInterface
and typehintLogger
in your controller constructor and expect it to work, it does not recognize inheritance in this way apparently.Another nice thing to know, is that apparently this only works on constructors directly. Controller methods have dependency injection as well but this does not work in combination with the contextual binding. However, it works with the default "simple" bindings as defined here https://laravel.com/docs/7.x/container#binding :
See https://github.com/laravel/framework/issues/6177 (tl;dr: the issue is closed and contextual bindings for controller methods will not be supported in the future).
I hope this information helps you out.