Laravel Service Container: Contextual Binding &amp; Injection | 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)    Contextual Binding and Method Injection in Laravel's Service Container        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)

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

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

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

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

       Table of contents

1. [  01   Why the Basics Are Not Enough  ](#why-the-basics-are-not-enough)
2. [  02   Contextual Binding  ](#contextual-binding)
3. [  03   Giving a Closure Instead of a Class  ](#giving-a-closure-instead-of-a-class)
4. [  04   Tagging Services  ](#tagging-services)
5. [  05   Method Injection  ](#method-injection)
6. [  06   Passing Extra Primitives  ](#passing-extra-primitives)
7. [  07   Practical Pattern: Strategy Selector  ](#practical-pattern-strategy-selector)
8. [  08   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.

 Found this useful?

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

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

  3 questions  

     Q01  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. 

      Q02  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. 

      Q03  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. 

  Continue reading

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

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

 [ ![Cursor Pagination and Lazy Collections at Scale in Laravel](https://cdn.msaied.com/536/3aab48ef4a4eaa26a3267637dc2ec8c7.png) laravel eloquent performance 

### Cursor Pagination and Lazy Collections at Scale in Laravel

Offset pagination breaks under large datasets. Learn how Laravel's cursor pagination and lazy collections let...

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

 11 Aug 2026     3 min read  

  Read    

 ](https://msaied.com/articles/cursor-pagination-and-lazy-collections-at-scale-in-laravel) [ ![Livewire v3.8.4 Released: Octane Memory Leak Fix and Fetch Redirect Handling](https://cdn.msaied.com/534/fdb2d91db2cb26fba0788d205b663031.png) livewire laravel octane 

### Livewire v3.8.4 Released: Octane Memory Leak Fix and Fetch Redirect Handling

Livewire v3.8.4 ships two important backports: a fix for a computed property listener memory leak under Larave...

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

 10 Aug 2026     3 min read  

  Read    

 ](https://msaied.com/articles/livewire-v384-released-octane-memory-leak-fix-and-fetch-redirect-handling) [ ![Laravel artisan dev: Run Server, Queue, Logs, and Vite in One Command](https://cdn.msaied.com/533/88ab98460b08aed42d6688eaa02a9620.png) Laravel Artisan Laravel 13 

### Laravel artisan dev: Run Server, Queue, Logs, and Vite in One Command

Laravel 13.16 introduced a first-party `php artisan dev` command that replaces the old Composer script, runnin...

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

 10 Aug 2026     3 min read  

  Read    

 ](https://msaied.com/articles/laravel-artisan-dev-run-server-queue-logs-and-vite-in-one-command) 

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