The service container (The Service Container) builds every controller, so constructor and action parameters are resolved by type: Request, bound models, services. Concrete classes need no registration, and contextual attributes such as #[Config] inject configuration values.
public function __construct( // app/Services/Currency.php
#[Config('app.currency', 'USD')] private string $code,
#[Config('app.locale')] string $locale,
) { /* builds a NumberFormatter: format(34.5) returns "$34.50" */ }
public function __construct(private Currency $currency) {} // ProductControllerAn interface needs a binding. The invokable CheckoutController asks for PaymentGateway:
$ curl -s -X POST -H 'Accept: application/json' -d card=4242424242424242 $B/api/checkout/1 \
| jq -r .message | fold -w 90
Target [App\Contracts\PaymentGateway] is not instantiable while building [App\Http\Control
lers\CheckoutController].
$ curl -s -X POST -H 'Accept: application/json' -d card=4242424242424242 $B/api/checkout/1
{"charged":39.99,"card":"**** 4242","ok":true}The second call followed #[Bind(FakeGateway::class)] on the interface (or $this->app->bind(), Binding Interfaces). Inject dependencies rather than calling app() inside actions, so tests can swap them.