I have a situation where I have a interface that a class in each module need to implement, I don't have a default module, so, my idea is to only use the contextual binding, see code:
$this->app->when('ModuleXController')
->needs('GlobalXContract')
->give('ConcretClassModuleX');
But I'm facing the Target GlobalXContract is not instantiable while building ModuleXController
This works when I do something like:
$this->app->bind('GlobalXContract', 'ConcretClassDefault');
$this->app->when('ModuleXController')
->needs('GlobalXContract')
->give('ConcretClassModuleX');
My question is, is it not more interesting to make optional the default binding when using contextual binding ?
Regards.
I wonder if you're still facing this behaviour on 5.3, can you please update?
@themsaid tested on v5.3.10 and everything ok, firstly I test in v5.2 (latest)
馃憤馃徏 thank you for updating us :)
Experiencing this on 5.5.* :/
a bit different though, the contextual bind is being ignored and the concrete class from the normal bind is being used regardless
The service provider:
$this->app->bind(DummyInterface::class, DummyInterfaceDefaultImpl::class);
$this->app
->when(ContextualTestController::class)
->needs(DummyInterface::class)
->give(DummyInterfaceContextualImpl::class);
Default implementation of an interface with saySomething() method:
class DummyInterfaceDefaultImpl implements DummyInterface
{
public function saySomething()
{
return 'Hey, i\'m from default implementation';
}
}
A separate implementation:
class DummyInterfaceContextualImpl implements DummyInterface
{
public function saySomething()
{
return 'Hi, I\'m from context';
}
}
Controller 1 (default)
class DefaultTestController extends Controller
{
public function test(DummyInterface $dummy)
{
return $dummy->saySomething();
}
}
Controller 2 (some specific context)
class ContextualTestController extends Controller
{
public function test(DummyInterface $dummy)
{
return $dummy->saySomething();
}
}
The routes:
Route::get('default-controller', 'Test\DefaultTestController@test');
Route::get('contextual-controller', 'Test\ContextualTestController@test');
Accessing /default-controller and /contextual-controller both display "Hey, i'm from default implementation"
Ayt, after digging through the container, the concrete class can't be found because Laravel's container relies on the end of the $buildStack property. To keep it short, if the dependency doesn't have an entry in the $buildStack (ex. injecting the dependency through the method) then container can't find the contextual binding.
Solution:
keep your dependency injections on the constructor :/
just for clarification, as someone might stumble upon this issue as I have myself.
This is still an existing issue, but in fact it is not a bug.
The thing is: contextual binding works only if it's used in constructor, dependency injection does not work in "late constructing" of a method.
The new proposal can be found here, tl;dr; it was rejected.
https://github.com/laravel/framework/pull/30542
Most helpful comment
Ayt, after digging through the container, the concrete class can't be found because Laravel's container relies on the end of the $buildStack property. To keep it short, if the dependency doesn't have an entry in the $buildStack (ex. injecting the dependency through the method) then container can't find the contextual binding.
Solution:
keep your dependency injections on the constructor :/