Method-Level Dependency Injection for Single-Use Services

📖 2 minutes read

Laravel’s service container supports dependency injection at the method level, not just the constructor. When a service is only used in one controller method, inject it there instead of cluttering the constructor.

The Problem: Constructor Bloat

It’s common to see controllers with constructors packed with dependencies that are only used in one or two methods:

class OrderController extends Controller
{
    public function __construct(
        private OrderService $orderService,
        private PaymentService $paymentService,
        private ShippingService $shippingService, // Only used in ship()
    ) {}
    
    public function index() {
        return $this->orderService->list();
    }
    
    public function ship(Request $request) {
        $this->shippingService->process(...);
    }
}

The ShippingService is instantiated for every request to this controller, even though it’s only used in the ship() method.

The Solution: Method-Level Injection

Inject dependencies directly into the methods that use them:

class OrderController extends Controller
{
    public function __construct(
        private OrderService $orderService,
        private PaymentService $paymentService,
    ) {}
    
    public function index() {
        return $this->orderService->list();
    }
    
    public function ship(Request $request, ShippingService $shippingService) {
        $shippingService->process(...);
    }
}

Laravel automatically resolves method parameters after route parameters. The service container inspects the type hint and provides the appropriate instance.

Benefits

  • Constructor stays focused on shared dependencies used across multiple methods
  • Clear intent — you can see exactly which methods use which services
  • Easier testing — mock only what that specific method needs
  • No unnecessary instantiation — dependencies are only created when the method is called

When to Use Which

Scenario Approach
Service used in 3+ methods Constructor injection
Service used in 1 method only Method injection
Service used in 2 methods Judgment call (lean toward method)

Route Parameters + Dependency Injection

When combining route model binding with method injection, Laravel resolves route parameters first, then dependencies:

// Route: /orders/{order}/ship
public function ship(Order $order, ShippingService $shippingService) {
    // $order comes from route binding
    // $shippingService comes from container
    $shippingService->processOrder($order);
}

Laravel is smart enough to distinguish between route parameters (bound to the route) and type-hinted dependencies (resolved from the container).

Takeaway

Don’t default to constructor injection for everything. If a dependency is only used in one method, inject it there. Your constructor will stay clean, your intent will be clearer, and you’ll avoid unnecessary instantiations.

Daryle De Silva

VP of Technology

11+ years building and scaling web applications. Writing about what I learn in the trenches.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *