<?php
namespace App\Providers;
use App\Contracts\PaymentGateway;
use App\Services\StripePaymentGateway;
use App\Services\PayPalPaymentGateway;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
public function register(): void
{
// Bind interface to implementation
$this->app->bind(PaymentGateway::class, StripePaymentGateway::class);
// Singleton - one instance per request
$this->app->singleton(CacheManager::class, function ($app) {
return new CacheManager($app['cache']);
});
// Contextual binding
$this->app->when(OrderController::class)
->needs(PaymentGateway::class)
->give(StripePaymentGateway::class);
$this->app->when(SubscriptionController::class)
->needs(PaymentGateway::class)
->give(PayPalPaymentGateway::class);
}
}
<?php
namespace App\Http\Controllers;
use App\Contracts\PaymentGateway;
use App\Models\Order;
use Illuminate\Http\Request;
class OrderController extends Controller
{
// Constructor injection
public function __construct(
private PaymentGateway $paymentGateway
) {}
// Method injection
public function store(Request $request, OrderRepository $repository)
{
$order = $repository->create($request->validated());
$payment = $this->paymentGateway->charge(
$order->total,
$request->payment_method
);
if ($payment->successful()) {
$order->update(['status' => 'paid']);
}
return redirect()->route('orders.show', $order);
}
}
Laravel's service container manages class dependencies and performs dependency injection automatically. When type-hinting interfaces or classes in constructors, Laravel resolves them from the container. I bind interfaces to implementations in service providers, allowing swapping implementations without changing code. Singleton bindings create one instance shared across requests. Contextual binding provides different implementations based on where they're injected. The app() helper resolves instances manually when auto-injection isn't possible. Method injection works in controllers and jobs—Laravel resolves parameters automatically. This inversion of control pattern makes code testable and decoupled. The container is Laravel's foundation, powering facades, automatic resolution, and the entire framework.