Laravel Service Container Deep Dive | Mohamed Said        [  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MH.png)   Mohamed Said Laravel Backend Engineer  ](https://msaied.com) [ Home ](https://msaied.com) [ Projects ](https://msaied.com/projects) [ Articles  ](https://msaied.com/articles) [ Certificates ](https://msaied.com/certificates) [ Contact ](https://msaied.com#contact-section) 

       [  ](https://github.com/EG-Mohamed)       

 [ Home ](https://msaied.com) [ Projects ](https://msaied.com/projects) [ Articles ](https://msaied.com/articles) [ Certificates ](https://msaied.com/certificates) [ Contact ](https://msaied.com#contact-section) 

  [ home ](https://msaied.com)    [ articles ](https://msaied.com/articles)    Laravel Service Container: Contextual Binding, Tagging, and Method Injection        On this page       1. [  Beyond app()-&gt;make(): The Container Features Most Teams Ignore ](#beyond-codeapp-gtmakecode-the-container-features-most-teams-ignore)
2. [  Contextual Binding ](#contextual-binding)
3. [  Tagging: Collecting Related Bindings ](#tagging-collecting-related-bindings)
4. [  Method Injection ](#method-injection)
5. [  Avoiding the Service Locator Anti-Pattern ](#avoiding-the-service-locator-anti-pattern)
6. [  Takeaways ](#takeaways)

  ![Laravel Service Container: Contextual Binding, Tagging, and Method Injection](https://cdn.msaied.com/357/6ee56a71454e019ced2bd70790e9ca60.png)

  #laravel   #service-container   #dependency-injection   #architecture  

 Laravel Service Container: Contextual Binding, Tagging, and Method Injection 
==============================================================================

     4 Jul 2026      3 min read    ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said  

       Table of contents

1. [  01   Beyond app()-&gt;make(): The Container Features Most Teams Ignore  ](#beyond-codeapp-gtmakecode-the-container-features-most-teams-ignore)
2. [  02   Contextual Binding  ](#contextual-binding)
3. [  03   Tagging: Collecting Related Bindings  ](#tagging-collecting-related-bindings)
4. [  04   Method Injection  ](#method-injection)
5. [  05   Avoiding the Service Locator Anti-Pattern  ](#avoiding-the-service-locator-anti-pattern)
6. [  06   Takeaways  ](#takeaways)

 Beyond `app()->make()`: The Container Features Most Teams Ignore
----------------------------------------------------------------

Most Laravel developers register a singleton, call `app(MyService::class)`, and move on. The container is capable of far more nuanced wiring — and using it well is the difference between a codebase that scales and one that turns into a ball of mud.

### Contextual Binding

Contextual binding answers the question: *"What should the container inject when class A asks for interface X, versus when class B asks for the same interface?"*

```php
// AppServiceProvider::register()
$this->app
    ->when(ReportExporter::class)
    ->needs(StorageDriver::class)
    ->give(S3StorageDriver::class);

$this->app
    ->when(LocalPreviewGenerator::class)
    ->needs(StorageDriver::class)
    ->give(LocalStorageDriver::class);

```

Both classes declare `__construct(StorageDriver $storage)`. The container resolves the correct concrete without a single `if` statement in application code. This is particularly powerful in multi-tenant SaaS where different panels may need different implementations of the same interface.

You can also supply a closure for runtime logic:

```php
$this->app
    ->when(InvoicePdfRenderer::class)
    ->needs(FontResolver::class)
    ->give(fn ($app) => new FontResolver(
        config('pdf.font_path'),
        $app->make(CacheManager::class)
    ));

```

### Tagging: Collecting Related Bindings

Tags let you group bindings under a label and resolve all of them at once — ideal for plugin architectures, report generators, or notification channels.

```php
// Register
$this->app->bind(SlackNotifier::class);
$this->app->bind(EmailNotifier::class);
$this->app->bind(WebhookNotifier::class);

$this->app->tag(
    [SlackNotifier::class, EmailNotifier::class, WebhookNotifier::class],
    'notifiers'
);

```

```php
// Resolve all tagged bindings
class NotificationDispatcher
{
    /** @param iterable $notifiers */
    public function __construct(
        private readonly iterable $notifiers
    ) {}

    public function dispatch(Notification $notification): void
    {
        foreach ($this->notifiers as $notifier) {
            $notifier->send($notification);
        }
    }
}

// Wire it up
$this->app->bind(NotificationDispatcher::class, fn ($app) =>
    new NotificationDispatcher($app->tagged('notifiers'))
);

```

`$app->tagged()` returns a lazy `TaggedIterator`, so bindings are not instantiated until the loop runs. Adding a new notifier is a one-line tag registration — no modification to `NotificationDispatcher`.

### Method Injection

The container can resolve dependencies for arbitrary method calls, not just constructors. This is how route model binding and controller methods work internally.

```php
class ReportController
{
    public function generate(
        Request $request,
        ReportBuilder $builder,   // injected by container
        AuditLogger $audit        // injected by container
    ): JsonResponse {
        $audit->log('report.generate', $request->user());
        return response()->json($builder->build($request->validated()));
    }
}

```

You can invoke this pattern yourself with `app()->call()`:

```php
$result = app()->call(
    [new SomeService(), 'handle'],
    ['extraParam' => 'value']  // merged with container-resolved args
);

```

This is useful in console commands, job handlers, or anywhere you want to defer resolution until call time rather than construction time.

### Avoiding the Service Locator Anti-Pattern

The temptation after learning `app()->make()` is to call it everywhere. Resist it. Inject through constructors or method signatures so dependencies are explicit and mockable in tests:

```php
// Bad: hidden dependency
class OrderProcessor
{
    public function process(Order $order): void
    {
        $mailer = app(Mailer::class); // invisible, untestable
        $mailer->send(...);
    }
}

// Good: declared dependency
class OrderProcessor
{
    public function __construct(private readonly Mailer $mailer) {}

    public function process(Order $order): void
    {
        $this->mailer->send(...);
    }
}

```

The container wires it; your class stays ignorant of the container.

### Takeaways

- **Contextual binding** resolves the same interface to different concretes per consumer — no conditionals in application code.
- **Tagging** enables open/closed plugin architectures; new implementations register themselves without touching existing classes.
- **Method injection via `app()->call()`** defers resolution to invocation time and keeps constructors lean.
- **Never call `app()` inside domain classes** — that couples your domain to the framework and kills testability.
- Service providers are the correct place for all container wiring; keep them focused and split large providers by domain.

 Found this useful?

          [  ](https://twitter.com/intent/tweet?url=https%3A%2F%2Fmsaied.com%2Farticles%2Flaravel-service-container-contextual-binding-tagging-and-method-injection-2&text=Laravel+Service+Container%3A+Contextual+Binding%2C+Tagging%2C+and+Method+Injection) [  ](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fmsaied.com%2Farticles%2Flaravel-service-container-contextual-binding-tagging-and-method-injection-2) 

 Frequently Asked Questions 
----------------------------

  3 questions  

     Q01  When should I use contextual binding instead of just registering two separate interfaces?        Use contextual binding when multiple consumers share the same interface contract but need different implementations — for example, two storage drivers that satisfy the same interface. Creating separate interfaces for each concrete would violate interface segregation. Contextual binding keeps the interface unified while letting the container handle the routing. 

      Q02  Does `app()-&gt;tagged()` instantiate all bindings immediately?        No. `app()-&gt;tagged()` returns a lazy `TaggedIterator`. Each binding is resolved only when the iterator reaches it, so you pay no instantiation cost for notifiers or handlers that are never iterated in a given request. 

      Q03  Can I use contextual binding inside a package service provider?        Yes. Package service providers have full access to `$this-&gt;app` and can register contextual bindings in their `register()` method. Just ensure your package documents which interfaces it expects the host application to have bound, so consumers know what to provide. 

  Continue reading

 More Articles 
---------------

 [ View all    ](https://msaied.com/articles) 

 [ ![PostgreSQL JSONB in Laravel: Indexing, Querying, and Schema-less Columns Done Right](https://cdn.msaied.com/356/592e07bf3b46443cb950d9bb31725ba6.png) laravel postgresql eloquent 

### PostgreSQL JSONB in Laravel: Indexing, Querying, and Schema-less Columns Done Right

JSONB columns can replace entire pivot tables or EAV nightmares — if you index them correctly and query throug...

  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said 

 3 Jul 2026     3 min read  

  Read    

 ](https://msaied.com/articles/postgresql-jsonb-in-laravel-indexing-querying-and-schema-less-columns-done-right) [ ![Cursor Pagination, Chunked Iteration, and Lazy Collections at Scale in Laravel](https://cdn.msaied.com/355/3a6df23a2c16b740843260134fad7c63.png) laravel eloquent performance 

### Cursor Pagination, Chunked Iteration, and Lazy Collections at Scale in Laravel

Offset pagination breaks under large datasets. Learn how cursor pagination, chunked iteration, and lazy collec...

  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said 

 3 Jul 2026     4 min read  

  Read    

 ](https://msaied.com/articles/cursor-pagination-chunked-iteration-and-lazy-collections-at-scale-in-laravel-1) [ ![Job Batching, Chaining, and Rate-Limited Middleware in Laravel Queues](https://cdn.msaied.com/353/89d47dc6b618d5435f9d7f333b75e922.png) laravel queues jobs 

### Job Batching, Chaining, and Rate-Limited Middleware in Laravel Queues

Go beyond basic dispatch: learn how to compose Laravel job batches with callbacks, chain dependent jobs safely...

  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said 

 3 Jul 2026     3 min read  

  Read    

 ](https://msaied.com/articles/job-batching-chaining-and-rate-limited-middleware-in-laravel-queues-2) 

   [  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MH.png)   Mohamed Said Laravel Backend Engineer  ](https://msaied.com)Senior Backend Engineer specializing in Laravel, scalable SaaS platforms, APIs, and cloud infrastructure. I build secure, high-performance web applications that help businesses grow.

Explore

- [Home](https://msaied.com)
- [Projects](https://msaied.com/projects)
- [Articles](https://msaied.com/articles)
- [Certificates](https://msaied.com/certificates)
- [Contact](https://msaied.com#contact-section)

Connect

- [   hello@msaied.com ](mailto:hello@msaied.com)
- [   +20 109 461 9204 ](tel:+201094619204)

© 2026 Mohamed Said. All rights reserved.

 [  ](https://github.com/EG-Mohamed) [  ](https://www.linkedin.com/in/msaiedm/) [  ](https://wa.me/201094619204) [  ](mailto:hello@msaied.com) [  ](https://drive.google.com/file/u/0/d/1MF20IPRJyzfy32mhEutjL5EpSls0w2Q8/view)
