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 contextual binding, tagged services, and method injection let you wire complex graphs cleanly without a single service locator call.

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

ShareCopy linkCopied

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

  On this page +1. [Why the Basics Are Not Enough](#why-the-basics-are-not-enough)
2. [Contextual Binding](#contextual-binding)
3. [Giving a Closure Instead of a Class](#giving-a-closure-instead-of-a-class)
4. [Tagging Services](#tagging-services)
5. [Method Injection](#method-injection)
6. [Passing Extra Primitives](#passing-extra-primitives)
7. [Practical Pattern: Strategy Selector](#practical-pattern-strategy-selector)
8. [Takeaways](#takeaways)

 Why the Basics Are Not Enough
-----------------------------

Most Laravel developers know `app()->bind()` and constructor injection. That covers 80% of cases. The remaining 20% — multiple implementations of the same interface, controller method injection, and runtime-selected strategies — is where the container's real power lives.

---

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

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

```php
// AppServiceProvider::register()
$this->app
    ->when(OrderExporter::class)
    ->needs(StorageContract::class)
    ->give(S3Storage::class);

$this->app
    ->when(ReportArchiver::class)
    ->needs(StorageContract::class)
    ->give(LocalStorage::class);

```

Both classes declare `StorageContract` in their constructors. The container resolves the correct driver per consumer — zero `if` statements, zero service locator calls.

### Giving a Closure Instead of a Class

When the implementation needs runtime data, pass a closure:

```php
$this->app
    ->when(InvoicePdfRenderer::class)
    ->needs(StorageContract::class)
    ->give(function (Application $app) {
        return $app->make(S3Storage::class, [
            'bucket' => config('invoices.bucket'),
        ]);
    });

```

---

Tagging Services
----------------

Tagging lets you resolve *all* implementations of a concept at once — perfect for pipelines, reporters, or notification channels.

```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
// Consume
class AlertDispatcher
{
    /** @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 the tagged group
$this->app
    ->when(AlertDispatcher::class)
    ->needs('$notifiers')
    ->giveTagged('notifiers');

```

Adding a new channel later means registering one class and appending it to the tag — the dispatcher never changes.

---

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

Constructor injection is resolved once at build time. Method injection is resolved per-call, which suits controllers, console commands, and one-off invokables.

```php
// Any public method resolved via app()->call()
class GenerateReport
{
    public function handle(
        Request $request,
        ReportBuilder $builder,
        CacheContract $cache,
    ): JsonResponse {
        $report = $cache->remember(
            'report.' . $request->query('type'),
            3600,
            fn () => $builder->build($request->query('type')),
        );

        return response()->json($report);
    }
}

// Dispatch from a controller or route:
return app()->call([app(GenerateReport::class), 'handle']);

```

Laravel's router already does this for controller actions, but `app()->call()` works on any callable — closures, `[object, method]` pairs, or `'ClassName@method'` strings.

### Passing Extra Primitives

```php
app()->call([GenerateReport::class, 'handle'], [
    'extraParam' => 'value', // merged with container-resolved args
]);

```

The container resolves typed parameters from the IoC graph and fills named primitives from the array.

---

Practical Pattern: Strategy Selector
------------------------------------

Combine contextual binding with a factory to select strategies at runtime:

```php
class PaymentGatewayFactory
{
    public function __construct(
        private readonly Application $app,
    ) {}

    public function for(string $provider): GatewayContract
    {
        return match ($provider) {
            'stripe' => $this->app->make(StripeGateway::class),
            'paddle' => $this->app->make(PaddleGateway::class),
            default  => throw new InvalidArgumentException("Unknown provider: {$provider}"),
        };
    }
}

```

Each gateway can still receive its own contextual dependencies — the factory just delegates resolution to the container.

---

Takeaways
---------

- **Contextual binding** eliminates conditional wiring for consumers of the same interface.
- **Tagged services** enable open/closed extensibility — add implementations without touching consumers.
- **Method injection** via `app()->call()` is useful for invokable actions and command handlers that need per-request dependencies.
- Avoid `app()->make()` inside domain classes; push resolution to service providers and factories.
- The container's `giveTagged()` and `give(Closure)` helpers cover nearly every real-world wiring scenario without a service locator.

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

  When should I use contextual binding instead of a factory class?Use contextual binding when the correct implementation is determined entirely by which class is being constructed — it keeps wiring declarative and in the service provider. Use a factory when the choice depends on runtime data (user input, a database value) that isn't available at container build time.

   Does tagging services affect performance?Tags are resolved lazily; the container only instantiates tagged services when you iterate over them. For most applications the overhead is negligible. If you have dozens of heavy services under one tag, consider wrapping them in a lazy proxy or resolving only the one you need.

   Can method injection be used in Artisan commands?Yes. Define your dependencies as parameters on the `handle()` method of your command class. Laravel resolves them from the container automatically, the same way it does for controller actions.

   ![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 articleLaravel Discount: Coupon Codes, Usage Limits, and Stacking](https://www.msaied.com/public/articles/laravel-discount-coupon-codes-usage-limits-and-stacking) [Next articleContextual Binding and Method Injection in Laravel's Service Container](https://www.msaied.com/public/articles/contextual-binding-and-method-injection-in-laravels-service-container-2)  

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

1. [Why the Basics Are Not Enough](#why-the-basics-are-not-enough)
2. [Contextual Binding](#contextual-binding)
3. [Giving a Closure Instead of a Class](#giving-a-closure-instead-of-a-class)
4. [Tagging Services](#tagging-services)
5. [Method Injection](#method-injection)
6. [Passing Extra Primitives](#passing-extra-primitives)
7. [Practical Pattern: Strategy Selector](#practical-pattern-strategy-selector)
8. [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/740/cce86edc21eddcbdd2f2454fadaf9c70.png)  · 3 min read### The Pipeline Pattern in Laravel: Custom Pipelines Beyond Middleware

5 Oct 2026 ](https://www.msaied.com/public/articles/the-pipeline-pattern-in-laravel-custom-pipelines-beyond-middleware-1) [ ![](https://cdn.msaied.com/739/2d6897fdcdcf090613f96f72a64b8a78.png)  · 4 min read### MySQL Full-Text Search in Laravel: Indexes, Relevance Scoring, and Boolean Mode

4 Oct 2026 ](https://www.msaied.com/public/articles/mysql-full-text-search-in-laravel-indexes-relevance-scoring-and-boolean-mode) [ ![](https://cdn.msaied.com/738/073696a3fefe18bec825beec5ac658f5.png)  · 4 min read### Laravel Queue Rate-Limited Middleware: Throttling Jobs Without Losing Work

4 Oct 2026 ](https://www.msaied.com/public/articles/laravel-queue-rate-limited-middleware-throttling-jobs-without-losing-work) 

  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)
