Zum Hauptinhalt springen

Was ist der Service-Container

Der Service-Container von Laravel ist ein Mechanismus zur Verwaltung von Klassenabhängigkeiten und zur Durchführung von Dependency Injection. Dependency Injection bedeutet, dass die von einer Klasse benötigten Abhängigkeiten über den Konstruktor oder gegebenenfalls über Setter-Methoden in die Klasse „injiziert” werden. Sehen Sie sich das folgende Beispiel an:
<?php

namespace App\Http\Controllers;

use App\Services\AppleMusic;
use Illuminate\View\View;

class PodcastController extends Controller
{
    /**
     * Neue Controller-Instanz erzeugen
     */
    public function __construct(
        protected AppleMusic $apple,
    ) {}

    /**
     * Informationen zum angegebenen Podcast anzeigen
     */
    public function show(string $id): View
    {
        return view('podcasts.show', [
            'podcast' => $this->apple->findPodcast($id)
        ]);
    }
}
In diesem Beispiel muss der PodcastController einen Podcast von einer Datenquelle wie Apple Music beziehen. Dazu wird ein Dienst injiziert, der Podcasts abrufen kann. Durch das Injizieren des Dienstes lässt sich beim Testen ein Mock (eine Dummy-Implementierung) des AppleMusic-Dienstes leicht einsetzen.
Ein tiefes Verständnis des Service-Containers ist beim Aufbau großer Laravel-Anwendungen unerlässlich. Es hilft auch dabei, zum Laravel-Core selbst beizutragen.

Zero-Configuration-Auflösung

Wenn eine Klasse nur von anderen konkreten Klassen (keinen Interfaces) abhängt, müssen Sie dem Container nicht mitteilen, wie sie aufzulösen ist. Nehmen Sie zum Beispiel folgenden Code in routes/web.php an:
<?php

class Service
{
    // ...
}

Route::get('/', function (Service $service) {
    dd($service::class);
});
In diesem Beispiel wird die Klasse innerhalb der Routendatei definiert – dies dient nur zu Demonstrationszwecken. In einer echten Anwendung sollten Service-Klassen im Verzeichnis app/Services definiert werden.
Beim Aufruf dieser Route löst Laravel die Service-Klasse automatisch auf und injiziert sie in den Route-Handler. Sie profitieren also von Dependency Injection, ohne eine Konfigurationsdatei anlegen zu müssen. Bei vielen Klassen, die Sie in einer Laravel-Anwendung schreiben – etwa Controller, Event-Listener oder Middleware – werden Abhängigkeiten automatisch über den Container injiziert.

Bindings

Einfache Bindings

Die meisten Bindings werden innerhalb von Service-Providern registriert. In einem Service-Provider können Sie über die Eigenschaft $this->app auf den Container zugreifen.

bind

Mit der bind-Methode registrieren Sie ein Binding, indem Sie einen Klassen- oder Interface-Namen zusammen mit einer Closure übergeben:
use App\Services\Transistor;
use App\Services\PodcastParser;
use Illuminate\Contracts\Foundation\Application;

$this->app->bind(Transistor::class, function (Application $app) {
    return new Transistor($app->make(PodcastParser::class));
});
Die Closure erhält als Argument den Container selbst und kann darüber Unterabhängigkeiten auflösen. Wenn Sie den Container außerhalb eines Service-Providers verwenden möchten, nutzen Sie die App-Fassade:
use App\Services\Transistor;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Support\Facades\App;

App::bind(Transistor::class, function (Application $app) {
    // ...
});
Klassen, die nicht von einem Interface abhängen, müssen nicht an den Container gebunden werden. Der Container kann solche Objekte automatisch über Reflection auflösen.

singleton

Die singleton-Methode bindet eine Klasse oder ein Interface so, dass sie nur ein einziges Mal aufgelöst wird. Ist ein Singleton einmal aufgelöst, gibt der Container bei jedem weiteren Aufruf dieselbe Instanz zurück.
use App\Services\Transistor;
use App\Services\PodcastParser;
use Illuminate\Contracts\Foundation\Application;

$this->app->singleton(Transistor::class, function (Application $app) {
    return new Transistor($app->make(PodcastParser::class));
});

instance

Eine bereits existierende Objektinstanz können Sie über die instance-Methode an den Container binden. Bei nachfolgenden Aufrufen des Containers wird stets diese Instanz zurückgegeben.
use App\Services\Transistor;
use App\Services\PodcastParser;

$service = new Transistor(new PodcastParser);

$this->app->instance(Transistor::class, $service);

Interfaces an Implementierungen binden

Eine der leistungsstärksten Funktionen des Service-Containers ist die Möglichkeit, ein Interface an eine bestimmte Implementierung zu binden. Angenommen, es existiert das Interface EventPusher und die Implementierung RedisEventPusher:
use App\Contracts\EventPusher;
use App\Services\RedisEventPusher;

$this->app->bind(EventPusher::class, RedisEventPusher::class);
Dadurch injiziert der Container in jede Klasse, die eine Implementierung von EventPusher benötigt, den RedisEventPusher. Sie müssen dann lediglich das Interface EventPusher als Type-Hint im Konstruktor angeben:
use App\Contracts\EventPusher;

/**
 * Neue Klasseninstanz erzeugen
 */
public function __construct(
    protected EventPusher $pusher,
) {}
Wenn Sie von Interfaces abhängen, müssen Sie Ihren Code nicht anpassen, wenn Sie die Implementierung austauschen. Das erleichtert das Testen und spätere Änderungen erheblich.

Automatische Auflösung (DI per Type-Hint)

Wenn der Service-Container Klassen wie Controller, Event-Listener oder Middleware auflöst, berücksichtigt er die Type-Hints des Konstruktors und injiziert die Abhängigkeiten automatisch.
<?php

namespace App\Http\Controllers;

use App\Repositories\UserRepository;

class UserController extends Controller
{
    /**
     * Neue Controller-Instanz erzeugen
     */
    public function __construct(
        protected UserRepository $users,
    ) {}
}
Solange UserRepository nicht von einem Interface abhängt, ist keine Registrierung im Container notwendig. Sobald die entsprechende Route aufgerufen wird, löst der Container die Abhängigkeit automatisch auf und injiziert sie in den Controller.

Auflösen aus dem Container

Methode make

Mit der make-Methode können Sie eine Klasseninstanz aus dem Container auflösen:
use App\Services\Transistor;

$transistor = app()->make(Transistor::class);
Falls Abhängigkeiten der Klasse nicht vom Container aufgelöst werden können, lassen sich über die Methode makeWith zusätzliche Argumente übergeben:
$transistor = $this->app->makeWith(Transistor::class, ['id' => 1]);

Automatische Injection

In der Praxis rufen Sie die make-Methode selten direkt auf. Bei Klassen, die vom Container aufgelöst werden – etwa Controller, Event-Listener oder Middleware – reicht es aus, Type-Hints im Konstruktor anzugeben; der Container übernimmt die Injection dann automatisch.

Fassaden und der Container

Die Fassaden von Laravel bieten eine statische Schnittstelle zu Objekten im Container. So ruft beispielsweise Cache::get() intern den Cache-Dienst aus dem Container ab und delegiert den Aufruf an ihn.
use Illuminate\Support\Facades\Cache;

// Aufruf über die Fassade
Cache::get('key');

// Äquivalenter Aufruf direkt über den Container
app('cache')->get('key');
Fassaden sind komfortable Wrapper um den Container. Beim Testen lassen sie sich außerdem durch Mocks ersetzen.
use Illuminate\Support\Facades\Cache;

Cache::shouldReceive('get')
    ->once()
    ->with('key')
    ->andReturn('value');

Konstruktor-Injection – ein praktisches Beispiel

Betrachten wir ein typisches Muster aus einer realen Anwendung.
1

Interface definieren

<?php

namespace App\Contracts;

interface PaymentGateway
{
    public function charge(int $amount, string $token): bool;
}
2

Implementierung erstellen

<?php

namespace App\Services;

use App\Contracts\PaymentGateway;

class StripePaymentGateway implements PaymentGateway
{
    public function charge(int $amount, string $token): bool
    {
        // Zahlung über die Stripe-API abwickeln...
        return true;
    }
}
3

Im Service-Provider binden

use App\Contracts\PaymentGateway;
use App\Services\StripePaymentGateway;

$this->app->singleton(PaymentGateway::class, StripePaymentGateway::class);
4

Injection im Controller entgegennehmen

<?php

namespace App\Http\Controllers;

use App\Contracts\PaymentGateway;
use Illuminate\Http\Request;

class OrderController extends Controller
{
    public function __construct(
        protected PaymentGateway $payment,
    ) {}

    public function store(Request $request)
    {
        $this->payment->charge(
            $request->amount,
            $request->payment_token
        );

        // ...
    }
}
Mit diesem Muster genügt es, das Binding an einer einzigen Stelle zu ändern, wenn Sie den Zahlungsdienst zum Beispiel von Stripe auf einen anderen Anbieter umstellen möchten.

Nächste Schritte

Service-Provider

Erfahren Sie, wie Sie mit Service-Providern Bindings registrieren.
Zuletzt geändert am 13. Juli 2026