Laravel Service Container: Contextual Binding &amp; Tagging | 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)    Service Container Deep Dive: Contextual Binding, Tagging, and Method Injection        On this page       1. [  Beyond app()-&gt;make(): The Container Features You're Probably Underusing ](#beyond-codeapp-gtmakecode-the-container-features-youre-probably-underusing)
2. [  Contextual Binding ](#contextual-binding)
3. [  Contextual Primitives ](#contextual-primitives)
4. [  Tagging ](#tagging)
5. [  Package Integration Pattern ](#package-integration-pattern)
6. [  Method Injection ](#method-injection)
7. [  Practical Use: Pipeline Steps Without Constructor Injection ](#practical-use-pipeline-steps-without-constructor-injection)
8. [  Testing Implications ](#testing-implications)
9. [  Takeaways ](#takeaways)

  ![Service Container Deep Dive: Contextual Binding, Tagging, and Method Injection](https://cdn.msaied.com/417/a4c7c54d0eb57d52836b9d11dd8f9998.png)

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

 Service Container Deep Dive: Contextual Binding, Tagging, and Method Injection 
================================================================================

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

       Table of contents

  9 sections  

1. [  01   Beyond app()-&gt;make(): The Container Features You're Probably Underusing  ](#beyond-codeapp-gtmakecode-the-container-features-youre-probably-underusing)
2. [  02   Contextual Binding  ](#contextual-binding)
3. [  03   Contextual Primitives  ](#contextual-primitives)
4. [  04   Tagging  ](#tagging)
5. [  05   Package Integration Pattern  ](#package-integration-pattern)
6. [  06   Method Injection  ](#method-injection)
7. [  07   Practical Use: Pipeline Steps Without Constructor Injection  ](#practical-use-pipeline-steps-without-constructor-injection)
8. [  08   Testing Implications  ](#testing-implications)
9. [  09   Takeaways  ](#takeaways)

       Beyond `app()->make()`: The Container Features You're Probably Underusing
-------------------------------------------------------------------------

Most Laravel developers know how to bind an interface to a concrete class. Fewer know how to tell the container to resolve *different* implementations depending on *who is asking*, group related services under a tag, or inject dependencies directly into arbitrary methods without a constructor. These three features — contextual binding, tagging, and method injection — unlock a cleaner architecture that scales without a service locator smell.

---

Contextual Binding
------------------

Contextual binding answers the question: "Give class A one implementation, but give class B a different one."

```php
// AppServiceProvider::register()
$this->app
    ->when(OrderExporter::class)
    ->needs(FilesystemInterface::class)
    ->give(fn () => Storage::disk('s3'));

$this->app
    ->when(ReportGenerator::class)
    ->needs(FilesystemInterface::class)
    ->give(fn () => Storage::disk('local'));

```

Both classes type-hint `FilesystemInterface`, but the container resolves the correct disk for each. No factory, no service locator, no `if` chain inside the class.

### Contextual Primitives

You can also inject scalar values contextually:

```php
$this->app
    ->when(StripeGateway::class)
    ->needs('$apiKey')
    ->giveConfig('services.stripe.secret');

```

This keeps environment-specific values out of constructors and config facades inside domain classes.

---

Tagging
-------

Tagging lets you group related bindings and resolve them all at once — perfect for plugin-style architectures, notification channels, or report drivers.

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

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

```

```php
// Resolve all tagged services
class BroadcastService
{
    public function __construct(
        private iterable $notifiers
    ) {}

    public function notify(string $message): void
    {
        foreach ($this->notifiers as $notifier) {
            $notifier->send($message);
        }
    }
}

// Bind the iterable via tagged
$this->app->bind(BroadcastService::class, function ($app) {
    return new BroadcastService($app->tagged('notifiers'));
});

```

`$app->tagged('notifiers')` returns a lazy `TaggedIterator` — services are only resolved when iterated, keeping boot time low.

### Package Integration Pattern

Packages can push their own implementations into a host application's tag without modifying the host's service provider:

```php
// In the package's service provider
$this->app->tag(PushNotifier::class, 'notifiers');

```

The host's `BroadcastService` picks it up automatically.

---

Method Injection
----------------

The container can resolve dependencies for *any* callable, not just constructors. This is how route closures, controller methods, and console commands work internally.

```php
$result = app()->call(
    [ReportService::class, 'generate'],
    ['format' => 'pdf']
);

```

The container resolves `ReportService` and all type-hinted parameters of `generate()`, then merges in the explicitly passed `$format`.

### Practical Use: Pipeline Steps Without Constructor Injection

```php
class EnrichOrderData
{
    public function handle(
        Order $order,
        TaxCalculator $tax, // resolved by container
        CurrencyConverter $fx // resolved by container
    ): Order {
        $order->tax = $tax->calculate($order);
        $order->total = $fx->convert($order->subtotal, $order->currency);
        return $order;
    }
}

// Invoke it
$enriched = app()->call([new EnrichOrderData, 'handle'], ['order' => $order]);

```

This is cleaner than injecting every dependency into the constructor when a step only needs them once.

---

Testing Implications
--------------------

All three features make testing straightforward:

```php
// Swap a contextual binding in a test
app()->when(OrderExporter::class)
    ->needs(FilesystemInterface::class)
    ->give(fn () => new FakeFilesystem);

// Or use the standard swap
app()->instance(FilesystemInterface::class, new FakeFilesystem);

```

Tagged services can be replaced by re-tagging a fake before the test runs. Method injection means you can call pipeline steps directly with mocked dependencies without rebuilding the whole object graph.

---

Takeaways
---------

- **Contextual binding** eliminates factory classes and `if` chains when the same interface needs different implementations in different contexts.
- **Scalar contextual binding** (`needs('$apiKey')`) keeps config out of domain classes cleanly.
- **Tagging** enables open/closed plugin architectures — new implementations register themselves without touching existing code.
- **`$app->tagged()`** is lazy; services are instantiated only when iterated.
- **Method injection via `app()->call()`** is the container's most underused feature for pipeline steps, console commands, and ad-hoc service invocations.
- All three patterns improve testability by keeping the container as the single source of truth for dependency resolution.

 Found this useful?

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

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

  3 questions  

     Q01  When should I use contextual binding instead of a factory class?        Use contextual binding when the decision about which implementation to use is purely based on which class is consuming it, and that decision is stable at boot time. Factories are better when the choice depends on runtime data (e.g., a user's plan or a request parameter). 

      Q02  Does `$app-&gt;tagged()` resolve all services immediately?        No. `$app-&gt;tagged()` returns a lazy TaggedIterator. Each service is resolved from the container only when the iterator reaches it, so unused services in the group are never instantiated. 

      Q03  Can I use method injection outside of controllers and console commands?        Yes. `app()-&gt;call($callable, $parameters)` works on any callable — closures, static methods, instance methods, or invokable classes. The container resolves all type-hinted parameters automatically and merges any explicitly passed values. 

  Continue reading

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

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

 [ ![Laravel Broadcasting with Reverb: Private Channels, Presence, and Auth Guards](https://cdn.msaied.com/416/26c2793902d9db428140205417b2dfb4.png) laravel reverb broadcasting 

### Laravel Broadcasting with Reverb: Private Channels, Presence, and Auth Guards

Go beyond the basics of Laravel Reverb. Learn how to secure private and presence channels, wire up custom auth...

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

 12 Jul 2026     4 min read  

  Read    

 ](https://msaied.com/articles/laravel-broadcasting-with-reverb-private-channels-presence-and-auth-guards) [ ![Cursor Pagination, Chunked Iteration, and Lazy Collections at Scale in Laravel](https://cdn.msaied.com/415/eb2d62822119280148e092d25b36c6ae.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 

 12 Jul 2026     4 min read  

  Read    

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

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

Go beyond basic dispatch: learn how to compose Laravel job batches, build reliable chains with catch callbacks...

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

 12 Jul 2026     3 min read  

  Read    

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

   [  ![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)
