DEV Community

Cover image for What is Dependency Injection in Laravel?
LaraCopilot
LaraCopilot

Posted on

What is Dependency Injection in Laravel?

Dependency Injection (DI) in Laravel is a powerful design pattern and technique that allows objects to receive their dependencies from external sources rather than creating them internally. Laravel's implementation of dependency injection, powered by its Service Container, provides automatic resolution of class dependencies, making code more modular, testable, and maintainable.

What is Dependency Injection

Dependency injection in Laravel

At its core, dependency injection is about supplying an object with its dependencies rather than having the object create them itself. This approach promotes loose coupling between classes and follows the Inversion of Control (IoC) principle, where the control of dependency creation is transferred from the consuming class to an external entity.

Traditional Approach vs Dependency Injection

Without Dependency Injection:

phpclass UserController 
{
    private $userService;

    public function __construct() 
    {
        // Hard-coded dependency creation
        $this->userService = new UserService();
    }

    public function show($id) 
    {
        return $this->userService->getUser($id);
    }
}
Enter fullscreen mode Exit fullscreen mode

With Dependency Injection:

phpclass UserController 
{
    private $userService;

    *// Dependency is injected via constructor*
    public function __construct(UserService $userService) 
    {
        $this->userService = $userService;
    }

    public function show($id) 
    {
        return $this->userService->getUser($id);
    }
}
Enter fullscreen mode Exit fullscreen mode

In the second example, the UserController doesn't create the UserService itself—it receives it as a dependency from Laravel's Service Container.

Types of Dependency Injection in Laravel

Laravel supports three primary types of dependency injection, each serving different use cases:

Constructor Injection

Constructor injection is the most common form where dependencies are injected via the class constructor. Laravel automatically resolves these dependencies when instantiating the class:


phpclass UserController extends Controller
{
    protected $userRepository;

    public function __construct(UserRepository $userRepository)
    {
        $this->userRepository = $userRepository;
    }

    public function index()
    {
        return $this->userRepository->all();
    }
}
Enter fullscreen mode Exit fullscreen mode

Laravel's Service Container uses PHP Reflection to analyze the constructor parameters and automatically provides the required dependencies.

Method Injection

Method injection allows dependencies to be injected directly into specific controller methods. This is particularly useful when a dependency is only needed for certain actions:

phpclass UserController extends Controller
{
    public function store(Request $request, UserService $userService)
    {
        $userService->create($request->validated());
        return redirect('/users');
    }

    public function update(Request $request, string $id, UserService $userService)
    {
        $userService->update($id, $request->validated());
        return redirect('/users');
    }
}
Enter fullscreen mode Exit fullscreen mode

Method injection is especially common for injecting the Request object and other method-specific dependencies.

Property Injection

Property injection involves setting dependencies directly on class properties, though this is less commonly used in Laravel applications

Automatic Resolution and Zero Configuration

One of Laravel's most powerful features is automatic resolution, which allows the framework to resolve dependencies without explicit configuration. Laravel can automatically resolve classes that:

  • Have no dependencies
  • Only depend on concrete classes (not interfaces)
  • Have dependencies that can themselves be automatically resolved

For example, this route automatically resolves the Service class:

phpRoute::get('/', function (Service $service) {
    dd($service::class);
});
Enter fullscreen mode Exit fullscreen mode

Laravel uses PHP Reflection to examine class constructors, identify their dependencies, and recursively resolve them. This zero configuration resolution means developers can focus on building features rather than managing complex dependency configurations.

Benefits of Dependency Injection

Enhanced Testability

Dependency injection significantly improves code testability by allowing easy substitution of dependencies with mock implementations during testing. Instead of hard-coded dependencies, you can inject test doubles:

php*// In tests*
public function test_user_creation()
{
    $mockUserService = Mockery::mock(UserService::class);
    $mockUserService->shouldReceive('create')->once()->andReturn(true);

    $this->app->instance(UserService::class, $mockUserService);

    *// Test controller logic*
}
Enter fullscreen mode Exit fullscreen mode

Laravel provides built-in mocking capabilities that integrate seamlessly with dependency injection.

Improved Modularity and Flexibility

Dependency injection promotes loose coupling between classes, making applications more modular. Classes don't need to know how to create their dependencies, making them easier to maintain and modify. Studies indicate that applications using dependency injection are 30% easier to test and maintain.

Better Code Organization

By separating object creation from business logic, dependency injection leads to cleaner, more focused code. Each class has a single responsibility, and dependencies are clearly defined in constructors or method signatures.

Interface Binding and Mocking

Laravel's dependency injection system excels when working with interfaces. You can bind interfaces to concrete implementations in Service Providers:

phppublic function register()
{
    $this->app->bind(PaymentServiceInterface::class, StripePaymentService::class);
}
Enter fullscreen mode Exit fullscreen mode

This approach allows you to easily swap implementations and create mock objects for testing:

php*// In tests*
$mock = Mockery::mock(PaymentServiceInterface::class);
$mock->shouldReceive('processPayment')->once()->andReturn(true);
$this->app->instance(PaymentServiceInterface::class, $mock);
Enter fullscreen mode Exit fullscreen mode

Real-World Applications and Best Practices

Service Classes and Business Logic

Dependency injection is particularly valuable when creating service classes that encapsulate business logic. For example, a payment processing system:

phpclass PaymentService
{
    public function __construct(
        private StripeGateway $stripeGateway,
        private Logger $logger
    ) {}

    public function processPayment($amount, $token)
    {
        $this->logger->info("Processing payment for $amount");
        return $this->stripeGateway->charge($amount, $token);
    }
}
Enter fullscreen mode Exit fullscreen mode

Repository Pattern Implementation

Dependency injection works excellently with the Repository Pattern, allowing easy swapping of data sources:

phpclass UserController extends Controller
{
    public function __construct(private UserRepositoryInterface $userRepository) {}

    public function index()
    {
        return $this->userRepository->paginate(15);
    }
}
Enter fullscreen mode Exit fullscreen mode

Performance and Best Practices

When to Use Manual Instantiation

While dependency injection is powerful, there are cases where manual instantiation with the new keyword is appropriate:

  • Value objects or simple data containers
  • Local objects with short lifespans
  • Standard PHP objects like DateTime or SplFileInfo

The general rule is: if an object represents a service or has complex configuration, use dependency injection. If it's a simple, local object, manual instantiation is fine.

Performance Considerations

Laravel's automatic resolution adds minimal overhead due to its efficient caching mechanisms. The framework caches reflection information to avoid repeated analysis of class constructors. For most applications, the benefits of maintainable, testable code far outweigh any minor performance costs.

Route-Level Dependency Injection

Laravel also supports dependency injection directly in routes, making it easy to inject services into route closures or single-action controllers:

phpRoute::get('/users', function (UserService $userService) {
    return $userService->getAllUsers();
});
Enter fullscreen mode Exit fullscreen mode

This feature extends Laravel's dependency injection capabilities beyond controllers to any part of the application that the Service Container manages.

Dependency Injection in Laravel represents a sophisticated approach to managing object dependencies that promotes clean architecture, testable code, and flexible application design. Through automatic resolution, interface binding, and seamless integration with Laravel's ecosystem, it enables developers to build maintainable applications while focusing on business logic rather than dependency management.

Top comments (0)