Laravel Service Container: Contextual Binding &amp; Tagging | Mohamed Said       [Skip to content](#main)  [ ![](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MH.png) Mohamed SaidLaravel Backend Engineer ](https://msaied.com) - [Home](https://msaied.com)
- [Projects](https://msaied.com/projects)
- [Articles](https://msaied.com/articles)
- [Certificates](https://msaied.com/certificates)
- [About](https://msaied.com#about)

           [  Contact](https://msaied.com#contact) Menu 

Menu
----

Close 

 - [HomeStart here](https://msaied.com)
- [ProjectsCase studies](https://msaied.com/projects)
- [ArticlesEngineering notes](https://msaied.com/articles)
- [CertificatesCredentials](https://msaied.com/certificates)
- [AboutHow I work](https://msaied.com#about)
- [ContactGet in touch](https://msaied.com#contact)

  [Start a conversation](https://msaied.com#contact) [WhatsApp](https://wa.me/201094619204) [Email](mailto:hello@msaied.com) 

 1. [Home](https://msaied.com)
2. /
3. [Articles](https://msaied.com/articles)
4. /
5. Service Container Deep Dive: Contextual Binding, Tagging, and Method Injection

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

 Go beyond basic singleton registration. Learn how Laravel's service container handles contextual binding, tagged services, and method injection to write decoupled, testable code without a single hard dependency.

 ![](https://cdn.msaied.com/01M22N44A70A5MC2S599JP0MPH.webp) [Mohamed Said](https://msaied.com#person) Published 6 Oct 2026 · Updated 6 Oct 2026 · 3 min read

ShareCopy linkCopied

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

  On this page +1. [Beyond app()-&gt;make(): The Container Features Most Developers Skip](#beyond-codeapp-gtmakecode-the-container-features-most-developers-skip)
2. [Contextual Binding](#contextual-binding)
3. [Tagging](#tagging)
4. [Method Injection](#method-injection)
5. [Testing Implications](#testing-implications)
6. [Takeaways](#takeaways)

 Beyond `app()->make()`: The Container Features Most Developers Skip
-------------------------------------------------------------------

Most Laravel developers register a singleton, call `app(MyService::class)`, and move on. The container, however, ships with three underused features that dramatically improve how you model competing implementations, group related services, and inject dependencies into arbitrary callables.

---

### Contextual Binding

Contextual binding lets you resolve *different* concrete implementations of the same interface depending on *which class* is requesting it.

```php
// AppServiceProvider::register()
$this->app
    ->when(OrderController::class)
    ->needs(PaymentGatewayInterface::class)
    ->give(StripeGateway::class);

$this->app
    ->when(SubscriptionController::class)
    ->needs(PaymentGatewayInterface::class)
    ->give(BraintreeGateway::class);

```

Both controllers type-hint `PaymentGatewayInterface`. The container resolves the correct concrete at build time — no service locator, no `if` branches in the constructor.

You can also pass a closure for runtime logic:

```php
$this->app
    ->when(ReportExporter::class)
    ->needs(StorageInterface::class)
    ->give(fn ($app) => $app->make(
        config('exports.driver') === 's3' ? S3Storage::class : LocalStorage::class
    ));

```

This keeps environment-specific wiring in the service provider, not scattered across business logic.

---

### Tagging

Tagging lets you group multiple bindings under a label and resolve them all at once — perfect for plugin systems, notification channels, or rule pipelines.

```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 NotificationDispatcher
{
    public function __construct(
        private iterable $notifiers
    ) {}

    public static function register(Application $app): self
    {
        return new self($app->tagged('notifiers'));
    }

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

```

`$app->tagged()` returns a lazy `TaggedIterator`, so concrete classes are only instantiated when the loop runs. Adding a new notifier is one line in the service provider — zero changes to `NotificationDispatcher`.

---

### Method Injection

The container can resolve and call *any* callable, injecting type-hinted parameters automatically via `call()`.

```php
$result = app()->call([InvoiceService::class, 'generate'], [
    'orderId' => $order->id,
]);

```

The container builds `InvoiceService`, resolves every type-hinted parameter of `generate()`, and merges in the explicitly passed `$orderId`. This is how route model binding and controller method injection work internally.

A practical use case — a command bus that dispatches to handler methods without a rigid interface:

```php
class CommandBus
{
    public function dispatch(object $command): mixed
    {
        $handler = app(get_class($command) . 'Handler');

        return app()->call([$handler, 'handle'], [
            'command' => $command,
        ]);
    }
}

```

Handlers declare whatever services they need as additional parameters; the container satisfies them. No abstract `HandleInterface` required.

---

### Testing Implications

All three features make swapping implementations in tests trivial:

```php
// In a Pest test
beforeEach(function () {
    $this->app->bind(PaymentGatewayInterface::class, FakeGateway::class);
});

```

Contextual bindings can be overridden the same way — re-register in `setUp` and the container prefers the last binding for that context.

---

### Takeaways

- **Contextual binding** eliminates gateway/driver `if` branches from constructors; wiring lives in the service provider.
- **Tagging** enables open/closed plugin systems — add implementations without touching consumers.
- **`app()->call()`** powers method injection for command buses, action classes, and any callable that needs container-resolved dependencies.
- All three features are fully compatible with Laravel's standard `bind`/`singleton` lifecycle and test overrides.
- Prefer explicit contextual bindings over environment checks inside constructors; it keeps business classes environment-agnostic.

- [laravel](https://msaied.com/articles?search=laravel)
- [service-container](https://msaied.com/articles?search=service-container)
- [dependency-injection](https://msaied.com/articles?search=dependency-injection)
- [architecture](https://msaied.com/articles?search=architecture)

 Frequently asked questions 
---------------------------

  Can contextual bindings be scoped to an interface rather than a concrete class?Yes. The `needs()` call accepts any abstract string — an interface, an abstract class, or even a plain string alias. The container matches on whatever the requesting class type-hints in its constructor.

   Does `app()-&gt;tagged()` instantiate all tagged services immediately?No. `tagged()` returns a lazy `TaggedIterator`. Each concrete is resolved only when the iterator reaches it, so you pay no instantiation cost for services that are never iterated.

   How does method injection differ from constructor injection in practice?Constructor injection is resolved once when the class is built. Method injection via `app()-&gt;call()` is resolved per-call, making it useful for action classes or command handlers that need request-scoped services without storing them as properties.

   ![Mohamed Said](https://cdn.msaied.com/01M22N44A70A5MC2S599JP0MPH.webp)About the author
----------------

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

[About](https://msaied.com#about) [GitHub ↗](https://github.com/EG-Mohamed) [LinkedIn ↗](https://www.linkedin.com/in/msaiedm/) [WhatsApp ↗](https://wa.me/201094619204) [Email Address ↗](mailto:hello@msaied.com) [My CV ↗](https://drive.google.com/file/u/0/d/1MF20IPRJyzfy32mhEutjL5EpSls0w2Q8/view)  

   [Previous articleGrant: Enum-Based Roles and Permissions for Laravel](https://msaied.com/articles/grant-enum-based-roles-and-permissions-for-laravel) [Next articleVMPal: Give Your AI Agent a Whole Computer to Work With](https://msaied.com/articles/vmpal-give-your-ai-agent-a-whole-computer-to-work-with)  

   On this page
-------------

1. [Beyond app()-&gt;make(): The Container Features Most Developers Skip](#beyond-codeapp-gtmakecode-the-container-features-most-developers-skip)
2. [Contextual Binding](#contextual-binding)
3. [Tagging](#tagging)
4. [Method Injection](#method-injection)
5. [Testing Implications](#testing-implications)
6. [Takeaways](#takeaways)

 ###  Have a technical challenge?

 Tell me what you’re building. I reply within two working days.

[Start a conversation](https://msaied.com#contact) 

   Related articles
-----------------

 [ ![](https://cdn.msaied.com/747/77f5a18ff2915d631224ade73cf34aa6.png) Laravel · 3 min read### VMPal: Give Your AI Agent a Whole Computer to Work With

6 Oct 2026 ](https://msaied.com/articles/vmpal-give-your-ai-agent-a-whole-computer-to-work-with) [ ![](https://cdn.msaied.com/746/496c70db85d5a374cca5e7f9c0a182a8.png) Laravel · 3 min read### Grant: Enum-Based Roles and Permissions for Laravel

5 Oct 2026 ](https://msaied.com/articles/grant-enum-based-roles-and-permissions-for-laravel) [ ![](https://cdn.msaied.com/743/8998fac3a41451ab3fe1588194e17a43.png) Filament · 3 min read### Securing Filament Plugins with Plumb: Automated Security Scoring for PHP Packages

5 Oct 2026 ](https://msaied.com/articles/securing-filament-plugins-with-plumb-automated-security-scoring-for-php-packages) 

  Have a technical challenge?
----------------------------

Tell me what you’re building. I reply within two working days.

 [Discuss your project ↗](https://msaied.com#contact) 

  © 2026 Mohamed Said · Built with Laravel, meant to last.Senior Backend Engineer specializing in Laravel, scalable SaaS platforms, APIs, and cloud infrastructure. I build secure, high-performance web applications that help businesses grow.

 - [Home](https://msaied.com)
- [Articles](https://msaied.com/articles)
- [Certificates](https://msaied.com/certificates)
- [GitHub](https://github.com/EG-Mohamed)
- [LinkedIn](https://www.linkedin.com/in/msaiedm/)
- [WhatsApp](https://wa.me/201094619204)
- [Email Address](mailto:hello@msaied.com)
- [My CV](https://drive.google.com/file/u/0/d/1MF20IPRJyzfy32mhEutjL5EpSls0w2Q8/view)
- [Sitemap](https://msaied.com/sitemap.xml)
