<?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.
Related snips
package com.example.myapp
import android.app.Application
import dagger.hilt.android.HiltAndroidApp
@HiltAndroidApp
Dependency injection with Hilt
{
"private": true,
"scripts": {
"dev": "vite",
"build": "vite build"
},
Laravel mix/Vite for asset compilation
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
Laravel database migrations for schema management
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
Laravel form requests for validation
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
Laravel soft deletes for data retention
<?php
namespace App\Notifications;
use App\Models\Post;
use Illuminate\Bus\Queueable;
Laravel notifications for multi-channel messaging
Share this code
Here's the card — post it anywhere.