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

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

Menu
----

Close 

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

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

 1. [Home](https://www.msaied.com/public)
2. /
3. [Articles](https://www.msaied.com/public/articles)
4. /
5. Contextual Binding and Method Injection in Laravel's Service Container

 Contextual Binding and Method Injection in Laravel's Service Container
=======================================================================

 Go beyond basic dependency injection. Learn how Laravel's contextual binding, tagged services, and method injection let you wire complex graphs cleanly without a service locator.

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

ShareCopy linkCopied

 ![Contextual Binding and Method Injection in Laravel's Service Container](https://cdn.msaied.com/170/396bdacb111411b734f3018920214c20.png) 

  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 Services](#tagging-services)
4. [Method Injection](#method-injection)
5. [Practical Pattern: Pipeline Stage Injection](#practical-pattern-pipeline-stage-injection)
6. [Takeaways](#takeaways)

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

Every Laravel developer knows `bind` and `singleton`. Fewer reach for contextual binding, tagging, or method injection — features that eliminate a surprising amount of conditional wiring code.

### Contextual Binding

Contextual binding answers the question: *"What should the container resolve 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(FilesystemInterface::class)
    ->give(fn () => Storage::disk('s3'));

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

```

Both classes declare `__construct(FilesystemInterface $fs)`. The container resolves the correct disk without either class knowing about the other or about config flags. No `if (app()->environment(...))` inside a constructor.

You can also bind a primitive this way:

```php
$this->app
    ->when(StripeWebhookProcessor::class)
    ->needs('$secret')
    ->give(config('services.stripe.webhook_secret'));

```

This keeps secrets out of the class body while remaining fully testable — swap the binding in a test service provider and the constructor still receives a plain string.

### Tagging Services

Tagging lets you resolve a *group* of implementations at once. This is the clean alternative to a hand-rolled registry array.

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

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

```

```php
// Consume
class BroadcastAlert
{
    /** @param iterable $notifiers */
    public function __construct(
        private readonly iterable $notifiers
    ) {}

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

```

```php
// Wire it
$this->app->bind(BroadcastAlert::class, function ($app) {
    return new BroadcastAlert($app->tagged('notifiers'));
});

```

`$app->tagged('notifiers')` returns a lazy generator — implementations are only instantiated when iterated. Adding a new notifier is a one-line tag registration; `BroadcastAlert` never changes.

### Method Injection

The container can resolve dependencies for *any* callable, not just constructors. `app()->call()` accepts a class-method pair, a closure, or an invokable:

```php
class ReportController
{
    public function generate(
        Request $request,
        ReportBuilder $builder,  // resolved by container
        PdfRenderer $renderer    // resolved by container
    ): Response {
        $report = $builder->build($request->validated());
        return response($renderer->render($report));
    }
}

// In a test or CLI command:
$response = app()->call(
    [app(ReportController::class), 'generate'],
    ['request' => $fakeRequest]
);

```

This is how Laravel resolves route controller methods internally. You can use the same mechanism in console commands, event listeners, or pipeline stages without pulling the container into the class itself.

### Practical Pattern: Pipeline Stage Injection

```php
class EnrichOrderData
{
    public function handle(
        Order $order,
        Closure $next,
        TaxCalculator $tax,   // injected by app()->call()
        FraudScorer $fraud
    ): Order {
        $order->tax_amount = $tax->calculate($order);
        $order->fraud_score = $fraud->score($order);
        return $next($order);
    }
}

```

When you drive the pipeline with `app()->call([$stage, 'handle'], $payload)`, each stage gets its own dependencies resolved fresh — no constructor pollution, no service locator.

### Takeaways

- **Contextual binding** removes environment checks and config reads from constructors, keeping classes ignorant of their deployment context.
- **Tagging** replaces hand-rolled registries with a lazy, extensible collection resolved by the container.
- **Method injection via `app()->call()`** is not just a framework internal — use it in pipelines, commands, and test helpers to keep classes lean.
- All three features are fully compatible with constructor injection; they compose rather than replace it.
- Prefer registering contextual bindings in a dedicated `ContextualBindingServiceProvider` to keep `AppServiceProvider` readable.

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

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

  Does contextual binding work with interfaces resolved via constructor injection automatically?Yes. When the container autowires a constructor it checks contextual bindings first. If the consuming class has a contextual rule for the requested type, that rule wins over any global binding.

   Is `app()-&gt;tagged()` eager or lazy?It returns a generator, so tagged implementations are instantiated only when you iterate over them. This means unused notifiers in a broadcast scenario cost nothing.

   Can I use method injection in Artisan commands?Yes. Override `handle()` with any type-hinted dependencies beyond the default Command signatures and Laravel will resolve them via the container automatically, the same way it does for controller methods.

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

[Mohamed Said](https://www.msaied.com/public#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://www.msaied.com/public#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 articlePostgreSQL JSONB in Laravel: Indexing, Querying, and Casting Without the Chaos](https://www.msaied.com/public/articles/postgresql-jsonb-in-laravel-indexing-querying-and-casting-without-the-chaos) [Next articleRead/Write Splitting, Connection Pooling, and Sticky Reads in Laravel](https://www.msaied.com/public/articles/readwrite-splitting-connection-pooling-and-sticky-reads-in-laravel)  

   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 Services](#tagging-services)
4. [Method Injection](#method-injection)
5. [Practical Pattern: Pipeline Stage Injection](#practical-pattern-pipeline-stage-injection)
6. [Takeaways](#takeaways)

 ###  Have a technical challenge?

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

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

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

 [ ![](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://www.msaied.com/public/articles/securing-filament-plugins-with-plumb-automated-security-scoring-for-php-packages) [ ![](https://cdn.msaied.com/742/2d02018669cdeedccb5de2efb898f0ee.png) Filament · 3 min read### Filament v3.3.56 Released: File Hash Names and Livewire Upload Fix

5 Oct 2026 ](https://www.msaied.com/public/articles/filament-v3356-released-file-hash-names-and-livewire-upload-fix) [ ![](https://cdn.msaied.com/741/5b55c123ad08e4d34e1f4b99ad6a428b.png)  · 3 min read### Filament v4 Schema-Based Forms, Infolists, and the Unified Schema API

5 Oct 2026 ](https://www.msaied.com/public/articles/filament-v4-schema-based-forms-infolists-and-the-unified-schema-api-5) 

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

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

 [Discuss your project ↗](https://www.msaied.com/public#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://www.msaied.com/public)
- [Articles](https://www.msaied.com/public/articles)
- [Certificates](https://www.msaied.com/public/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://www.msaied.com/public/sitemap.xml)
