> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# Hooks nach der Service-Auflösung in Paketen

> Anhand von callAfterResolving() in Laravel 13 erläutert: wie Sie noch nicht aufgelöste und bereits aufgelöste Services erweitern und worauf Sie bei Singletons, gewöhnlichen Bindings und erneuter Registrierung achten müssen.

Wenn ein Paket einem Laravel-Service Funktionalität hinzufügt, soll die Instanz oft erst bei Bedarf erzeugt werden – und gleichzeitig soll die Konfiguration auch dann greifen, wenn die Instanz bereits existiert. Die protected-Methode `callAfterResolving()` des Service Providers verbindet genau diese beiden Anforderungen.

Diese Seite untersucht die Implementierung in Laravel Framework `v13.35.0` und vertieft die Erweiterung aus `boot()`, die in der [Laravel-Paketentwicklung](/de/advanced/package-development) beschrieben ist. Es handelt sich nicht um einen Hook, der auf den vollständigen Start der Anwendung wartet, sondern um einen Hook für die Auflösung eines bestimmten Service.

## Warten, falls noch nicht aufgelöst – sofort ausführen, falls bereits aufgelöst

`ServiceProvider::callAfterResolving()` arbeitet in zwei Schritten:

1. Es registriert den Callback über `afterResolving()` des Containers.
2. Ist `resolved()` true, holt es den Service mit `make()` und ruft den Callback zusätzlich sofort auf.

Ist der Service noch nicht aufgelöst, ruft die Methode selbst kein `make()` für ihn auf. Bei einer gewöhnlichen Auflösung erstellt der Container das Objekt, wendet die Extender an, ruft die `resolving`-Callbacks auf und anschließend die `afterResolving`-Callbacks.

```mermaid theme={null}
flowchart TD
    A["callAfterResolving() in boot()"] --> B["In afterResolving registrieren"]
    B --> C{"Ziel bereits resolved()?"}
    C -->|Nein| D["Ziel jetzt nicht erzeugen"]
    D --> E["Ziel wird später aufgelöst"]
    E --> F["Registrierten Callback ausführen"]
    C -->|Ja| G["Ziel mit make() holen"]
    G --> H["Callback zusätzlich sofort ausführen"]
```

| Methode | Bei Registrierung nicht aufgelöst | Bei Registrierung bereits aufgelöstes Singleton |
| - | - | - |
| Direkt konfigurieren mit `$this->app->make()` | Wird sofort erzeugt | Konfiguriert die vorhandene Instanz |
| `$this->app->afterResolving()` | Wartet auf künftige Auflösungen | Die Registrierung allein konfiguriert die vorhandene Instanz nicht |
| `$this->callAfterResolving()` | Wartet auf künftige Auflösungen | Konfiguriert auch die vorhandene Instanz sofort |

Beim gewöhnlichen Abruf eines Singletons gibt der Container die gespeicherte Instanz frühzeitig zurück, sodass `afterResolving` nicht bei jedem Abruf ausgelöst wird. Wichtig ist: Wenn Sie lediglich `afterResolving()` registrieren, entgeht Ihrer Konfiguration ein Singleton, das bereits vor der Registrierung erzeugt wurde.

## Der Validation-Factory eine Regel hinzufügen

Als Beispiel für eine paketeigene String-Regel registrieren wir die Regel nach der Auflösung von `validator`. Der offizielle `ValidationServiceProvider` registriert diesen Schlüssel als Singleton, und der Provider selbst unterstützt Lazy Loading.

```php theme={null}
<?php

namespace Acme\Courier;

use Illuminate\Support\ServiceProvider;
use Illuminate\Validation\Factory;

class CourierServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        $this->callAfterResolving('validator', function (Factory $factory): void {
            $factory->extend(
                'courier_tracking_code',
                static function ($attribute, $value, $parameters, $validator): bool {
                    return is_string($value)
                        && preg_match('/\ACR-[0-9]{8}\z/', $value) === 1;
                },
                'The :attribute must be a valid courier tracking code.',
            );
        });
    }
}
```

In der nutzenden Anwendung wird sie zusammen mit gewöhnlichen Regeln verwendet.

```php theme={null}
$request->validate([
    'tracking_code' => ['required', 'string', 'courier_tracking_code'],
]);
```

Das `extend()` in diesem Beispiel ist die API zur Regelregistrierung von `Illuminate\Validation\Factory`. Es ist eine andere Methode als das weiter unten beschriebene `extend()` des Containers. Da gewöhnliche benutzerdefinierte Regeln etwa bei leeren Werten nicht ausgeführt werden können, legen Sie die Pflichtangabe mit `required` fest. Details zum Entwurf der Regel selbst finden Sie unter [Benutzerdefinierte Validierungsregeln](/de/advanced/custom-validation-rules).

`Factory::extend()` überschreibt das Array-Element mit demselben Regelnamen, sodass sich die Einträge nicht vermehren, wenn dieselbe Logik wie in diesem Beispiel erneut registriert wird. Versehen Sie öffentlich bereitgestellte Regelnamen jedoch mit einem paketspezifischen Präfix, damit Sie keine Regeln anderer Pakete überschreiben.

<Info>
  Der Callback wird ausgeführt, wenn die Factory aufgelöst wird. Die Prüf-Closure der Regel wird erst ausgeführt, wenn der Validator später den jeweiligen Wert validiert. Bei der Registrierung des Hooks werden keine Eingabewerte validiert.
</Info>

## Zielschlüssel und Prüfung auf Auflösung abstimmen

Bei den gewöhnlichen Auflösungsereignissen des Containers werden nicht nur Callbacks ausgewählt, die zum registrierten Schlüssel passen, sondern auch solche, die zum Typ des abgerufenen Objekts passen. Das von `callAfterResolving()` sofort verwendete `resolved($name)` prüft dagegen für den um Aliase normalisierten Schlüssel, ob ein Auflösungs-Flag oder eine gespeicherte Instanz vorhanden ist. Es durchsucht nicht alle bereits erzeugten Objekte.

Dass ein bestimmtes Interface oder eine Klasse bereits aufgelöst ist, bedeutet daher nicht zwingend, dass `resolved()` auch für einen anderen Schlüssel true liefert. Prüfen Sie das tatsächliche Binding und die Aliase des Ziel-Service und testen Sie den bereits aufgelösten Fall mit demselben Schlüssel. Im obigen Beispiel wird `validator` angegeben, das der offizielle Provider registriert.

Außerdem umfasst `resolved()` auch die Aussage „wurde in der Vergangenheit aufgelöst“. Es bedeutet nicht, dass die aktuelle Instanz zwingend noch vorhanden ist.

## Keine API, die „genau einmal ausführen“ garantiert

`callAfterResolving()` lässt den Callback registriert. Auch nach der sofortigen Ausführung wird der registrierte Callback ausgeführt, wenn das Objekt künftig neu aufgelöst wird. Ruft man die Methode erneut auf, wird zudem ein weiterer Callback hinzugefügt.

Besondere Vorsicht ist geboten, wenn ein gewöhnliches, nicht geteiltes Binding bereits aufgelöst wurde. In der untersuchten Implementierung läuft dann Folgendes ab:

1. Der neue Callback wird registriert.
2. Da `resolved()` true ist, wird `make()` aufgerufen.
3. `make()` erzeugt ein neues Objekt, und dessen Auflösungsereignis führt den Callback aus.
4. Auf dasselbe von `make()` zurückgegebene Objekt wird der Callback zusätzlich sofort angewendet.

Auf diesem Weg wird der soeben registrierte Callback zweimal auf dasselbe Objekt angewendet. Übertragen Sie einen Entwurf, der nur den Abruf von Singletons berücksichtigt, nicht unverändert auf gewöhnliche Bindings.

<Warning>
  Versenden Sie im Callback keine E-Mails, rufen Sie keine externen APIs auf, lösen Sie keine Abrechnungen aus und fügen Sie keine Listener bedingungslos hinzu. Beschränken Sie den Callback darauf, Konfiguration idempotent auf den Service anzuwenden, und gestalten Sie ihn so, dass sich Seiteneffekte bei erneuter Registrierung oder Auflösung nicht doppeln.
</Warning>

## Zum Ersetzen von Objekten eine andere API verwenden

Der Rückgabewert eines `afterResolving`-Callbacks wird nicht dazu verwendet, das vom Container zurückgegebene Objekt zu ersetzen. Wenn Sie einen Decorator zurückgeben und den Service selbst austauschen möchten, ziehen Sie das `extend()` des Containers in Betracht. Die Closure dieser API muss vertragsgemäß den geänderten Service zurückgeben.

Ebenso ist `callAfterResolving()` kein Mechanismus, der alle Abhängigkeiten aktualisiert, die bereits in anderen Objekten gespeichert sind. Wenn es darum geht, Abhängigkeiten beim erneuten Binden zu aktualisieren, prüfen Sie `rebinding()` in der offiziellen Dokumentation sowie den Entwurf der betroffenen Klasse.

Die Anforderungen an Provider, die Services tatsächlich verzögert laden, finden Sie unter [DeferrableProvider](/de/advanced/deferred-provider). Einen Hook zu verwenden und den Provider, der diesen Hook registriert, selbst verzögert zu laden, sind zwei verschiedene Dinge.

## Bei Updates zu prüfende Kombinationen

Prüfen Sie vor einem Paket-Release nicht nur die gewöhnliche Startreihenfolge, sondern auch den Fall, dass ein anderer Provider den Service zuvor bereits verwendet hat.

| Fall | Zu prüfen |
| - | - |
| Hook registrieren, während das Ziel noch nicht aufgelöst ist | Die Registrierung allein erzeugt das Ziel nicht, und die Konfiguration greift bei der ersten Auflösung |
| Singleton zuerst auflösen, dann registrieren | Die Konfiguration wird sofort auf dieselbe Instanz angewendet |
| Konfiguriertes Singleton mit gewöhnlichem `make()` erneut abrufen | Dieselbe Instanz wird zurückgegeben, und Auflösungs-Callbacks werden nicht doppelt ausgeführt |
| Für ein bereits früher aufgelöstes gewöhnliches Binding registrieren | Auch die zweifache Anwendung durch `make()` bei der Registrierung und den expliziten Aufruf richtet keinen Schaden an |
| Mehrfache Hook-Registrierung oder Neuerzeugung von Instanzen | Gleiche Regeln, Listener, Pfade usw. sammeln sich nicht unbeabsichtigt an |
| Unterstützte Laravel-Version aktualisieren | Binding-Schlüssel, Sharing-Verhalten und Ausführungsweg der Hooks erneut prüfen |

Wenn Sie mit einem statischen Flag festhalten, dass etwas anwendungsweit bereits einmal verwendet wurde, kann die Konfiguration für Services fehlen, die in einem neuen Container oder in Tests neu erzeugt werden. Stellen Sie die Idempotenz der Konfiguration möglichst auf Ebene des Zielobjekts oder des zu registrierenden Schlüssels sicher.

## Verwandte Seiten

<Columns cols={2}>
  <Card title="Paket-Views überschreiben und aktualisieren" icon="eye" href="/de/advanced/package-views">
    Ein Beispiel, wie loadViewsFrom() View-Namensräume über einen Hook nach der Auflösung registriert.
  </Card>

  <Card title="Paketübersetzungen überschreiben und aktualisieren" icon="language" href="/de/advanced/package-translations">
    Die Registrierung von Namensräumen im Translator und der Umgang mit bereits geladenen Übersetzungen.
  </Card>
</Columns>

## Herangezogene Primärquellen

* [Offizielle Laravel-Dokumentation: Paketentwicklung](https://github.com/laravel/docs/blob/13.x/packages.md)
* [Offizielle Laravel-Dokumentation: Container events, Extending bindings, Rebinding](https://github.com/laravel/docs/blob/13.x/container.md#container-events)
* [Offizielle Laravel-Dokumentation: Benutzerdefinierte Validierungsregeln](https://github.com/laravel/docs/blob/13.x/validation.md#custom-validation-rules)
* [ServiceProvider: callAfterResolving() und seine Verwendung bei der Ressourcenregistrierung](https://github.com/laravel/framework/blob/v13.35.0/src/Illuminate/Support/ServiceProvider.php)
* [Container: resolved(), resolve(), afterResolving()](https://github.com/laravel/framework/blob/v13.35.0/src/Illuminate/Container/Container.php)
* [ValidationServiceProvider: Singleton-Registrierung von validator](https://github.com/laravel/framework/blob/v13.35.0/src/Illuminate/Validation/ValidationServiceProvider.php)
* [Validation Factory: Regelregistrierung mit extend()](https://github.com/laravel/framework/blob/v13.35.0/src/Illuminate/Validation/Factory.php)


## Related topics

- [Laravel-Paketentwicklung](/de/advanced/package-development.md)
- [Weiterführende Themen](/de/advanced/index.md)
- [Deferred Service Provider](/de/advanced/deferred-provider.md)
- [Session-Hooks](/de/packages/laravel-copilot-sdk/hooks.md)
- [Öffentliche Paket-Assets veröffentlichen und aktualisieren](/de/advanced/package-assets.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.