Laravel Service Container: Contextual Binding &amp; Tagging | 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. Laravel Service Container: Contextual Binding, Tagging, and Method Injection

 Laravel Service Container: Contextual Binding, Tagging, and Method Injection
=============================================================================

 Go beyond basic dependency injection. Learn how contextual binding, container tags, and method injection solve real architectural problems in large Laravel applications without reaching for a service locator.

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

ShareCopy linkCopied

 ![Laravel Service Container: Contextual Binding, Tagging, and Method Injection](https://cdn.msaied.com/231/71797ad08a2a2c3669a4c96040c1720c.png) 

  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. [Tagging: Collecting Related Bindings](#tagging-collecting-related-bindings)
4. [Method Injection](#method-injection)
5. [Contextual Attributes (PHP 8.x)](#contextual-attributes-php-8x)
6. [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 use the container's more surgical tools — contextual binding, tagged groups, and method injection — which can dramatically simplify complex dependency graphs without introducing a service locator.

---

### Contextual Binding

Contextual binding lets you resolve different implementations of the same interface depending on *which class* is requesting it. This is invaluable in multi-channel or multi-driver architectures.

```php
// AppServiceProvider::register()
$this->app
    ->when(OrderNotificationService::class)
    ->needs(NotifierInterface::class)
    ->give(SlackNotifier::class);

$this->app
    ->when(InvoiceNotificationService::class)
    ->needs(NotifierInterface::class)
    ->give(EmailNotifier::class);

```

Both services type-hint `NotifierInterface`. The container resolves the correct driver per consumer — no factory, no `if` chain, no service locator.

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
    ));

```

---

### Tagging: Collecting Related Bindings

Tags let you group related bindings and resolve them all at once — perfect for plugin-style architectures, validators, or pipeline stages.

```php
// Register
$this->app->bind(CsvImporter::class);
$this->app->bind(XmlImporter::class);
$this->app->bind(JsonImporter::class);

$this->app->tag(
    [CsvImporter::class, XmlImporter::class, JsonImporter::class],
    'importers'
);

```

```php
// Resolve all tagged bindings
class ImportOrchestrator
{
    public function __construct(
        private readonly iterable $importers,
    ) {}

    public static function register(Application $app): void
    {
        $app->bind(self::class, fn ($app) => new self(
            $app->tagged('importers')
        ));
    }

    public function handle(string $type, mixed $payload): void
    {
        foreach ($this->importers as $importer) {
            if ($importer->supports($type)) {
                $importer->import($payload);
                return;
            }
        }

        throw new UnsupportedImportTypeException($type);
    }
}

```

Adding a new importer is a one-line registration — no changes to the orchestrator.

---

### Method Injection

`app()->call()` resolves dependencies for any callable, not just constructors. This is useful for one-off invocations where you don't want a full class binding.

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

```

The container resolves all type-hinted parameters automatically, merging them with the explicit `['format' => 'pdf']` array. Works equally well with closures:

```php
$result = app()->call(function (UserRepository $repo, string $format) {
    return $repo->exportAll($format);
}, ['format' => 'csv']);

```

This pattern is used internally by Laravel's route model binding and controller dispatch — you can leverage it in console commands, custom runners, or test helpers.

---

### Contextual Attributes (PHP 8.x)

Laravel 11+ supports `#[\Illuminate\Container\Attributes\Config]` and custom contextual attributes, letting you inject config values or tagged collections declaratively:

```php
use Illuminate\Container\Attributes\Config;

class PaymentGateway
{
    public function __construct(
        #[Config('services.stripe.secret')] private string $secret,
    ) {}
}

```

You can create your own attribute by implementing `ContextualAttribute` and registering a resolver — a clean alternative to constructor pollution with scalar values.

---

### Takeaways

- **Contextual binding** eliminates factory classes and `if/switch` driver selection by letting the container decide per consumer.
- **Tagging** enables open/closed plugin systems — new implementations register themselves without touching orchestrators.
- **`app()->call()`** is underused for ad-hoc dependency resolution in commands, test helpers, and event listeners.
- **Contextual attributes** (Laravel 11+) keep constructors clean when injecting scalar config values.
- Prefer these container features over service locator calls (`app(Foo::class)` inside methods) to keep dependencies explicit and testable.

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

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

  When should I use contextual binding instead of a factory class?Use contextual binding when the variation is purely about which consumer is requesting the dependency and the selection logic is static or config-driven. Factories are better when the variation depends on runtime data (e.g., a value from the database) that isn't available at container build time.

   Does resolving tagged bindings instantiate all of them eagerly?Yes — `$app-&gt;tagged('importers')` returns a generator-like `ContextualBindingBuilder` that resolves each binding on iteration, but all tagged classes will be instantiated as you iterate. If instantiation is expensive, consider lazy wrappers or resolving only the matching implementation.

   Can I use method injection in Artisan commands?Artisan commands use constructor injection for their dependencies. Method injection via `app()-&gt;call()` is available but you'd invoke it manually inside `handle()`. A cleaner pattern is to inject a single orchestrator or action class into the constructor and delegate there.

   ![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 articleLattice: Build Inertia UIs in Pure PHP Without Writing JSX](https://www.msaied.com/public/articles/lattice-build-inertia-uis-in-pure-php-without-writing-jsx) [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-1)  

   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. [Tagging: Collecting Related Bindings](#tagging-collecting-related-bindings)
4. [Method Injection](#method-injection)
5. [Contextual Attributes (PHP 8.x)](#contextual-attributes-php-8x)
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/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)
