---
title: Register Services in AppServiceProvider — Never new() in Controllers
impact: MEDIUM
impactDescription: direct instantiation prevents mocking in tests and creates hidden dependencies
tags: service-container, dependency-injection, ioc, laravel, testing
---

## Register Services via Service Container — Never new() in Controllers

Laravel's IoC container resolves dependencies automatically via constructor injection. Using `new MyService()` inside a controller or service bypasses the container, making tests hard and coupling impossible to break.

**Wrong:**

```php
class OrderController extends Controller
{
    public function store(StoreOrderRequest $request)
    {
        // Hidden dependencies, impossible to mock
        $service    = new OrderService(new InventoryService(), new PricingService());
        $mailer     = new OrderMailer(new SmtpTransport());
    }
}
```

**Correct:**

```php
// 1. Bind in AppServiceProvider if needed (often not needed — auto-resolved)
class AppServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        $this->app->singleton(PaymentGateway::class, function () {
            return new StripeGateway(config('services.stripe.secret'));
        });

        // Interface → concrete binding
        $this->app->bind(NotifierInterface::class, SlackNotifier::class);
    }
}

// 2. Controller receives via constructor — auto-resolved by container
class OrderController extends Controller
{
    public function __construct(
        private readonly OrderService    $orderService,
        private readonly NotifierInterface $notifier,
    ) {}

    public function store(StoreOrderRequest $request): JsonResponse
    {
        $order = $this->orderService->placeOrder($request->user(), $request->validated());
        $this->notifier->notify("New order #{$order->id}");
        return OrderResource::make($order)->response()->setStatusCode(201);
    }
}

// 3. In tests — swap the binding
$this->app->instance(NotifierInterface::class, $mockNotifier);
```
