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

# Storage-Treiber in Paketen registrieren und neu auflösen

> Erläutert anhand des FilesystemManagers von Laravel 13 die Registrierung mit Storage::extend(), das Vorhalten von Disk-Instanzen, das erneute Auflösen nach Konfigurationsänderungen und die Prüfung ohne Abhängigkeit von fake.

Wenn ein Paket Speicherfunktionen bereitstellt, trennen Sie **die Registrierung, wie ein Treiber erzeugt wird**, von **der Erzeugung der Disks des Anwenders**. Wenn das bloße Booten des Providers weder externe Verbindungen aufbaut noch Dateioperationen startet, gefährden Sie den Bootvorgang in Umgebungen, die die Funktion nicht nutzen, deutlich seltener.

Diese Seite basiert auf der Implementierung von Laravel Framework `v13.37.0`. Die Grundlagen finden Sie unter [Dateispeicher](/de/filesystem), das Laden von Providern unter [Automatische Paketerkennung](/de/advanced/package-discovery).

## Treibernamen und Disk-Namen trennen

Das erste Argument von `Storage::extend()` ist ein **Treibername**. Das Argument von `Storage::disk()` ist dagegen ein **Disk-Name**, der in `filesystems.disks` definiert ist. Sie können mehrere Disks definieren, die denselben Treiber verwenden.

| Element | Beispiel auf dieser Seite | Aufgabe |
| - | - | - |
| Treibername | `acme-archive-local` | Bezeichner des Callbacks, der Disks erzeugt |
| Disk-Name | `reports`, `exports` | Bezeichner der Konfiguration und der erzeugten Instanz |
| Disk-Konfiguration | `root`, `throw` usw. | Werte, die bei der Erzeugung dieser Disk an den Callback übergeben werden |

`FilesystemManager::resolve()` sucht nach einer eigenen Erzeugungslogik für den konfigurierten `driver` und ruft sie, falls vorhanden, vor den eingebauten Treibern auf. Wenn Sie `local` oder `s3` als Erweiterungsnamen verwenden, ersetzen Sie die bestehende Erzeugungslogik. Wählen Sie daher einen paketspezifischen Namen.

## Im Provider nur die Erzeugungslogik registrieren

Das folgende Minimalbeispiel verwendet die Standard-Erzeugungslogik des lokalen Laravel-Treibers wieder, um den Registrierungsweg zu veranschaulichen. Wenn Sie nur lokal speichern möchten, genügt der normale Treiber `local`. Wenn Sie ein eigenes Backend bereitstellen, ersetzen Sie dies wie im Beispiel der offiziellen Dokumentation durch Code, der einen Flysystem-Adapter aufbaut und ihn in Laravels `FilesystemAdapter` einbettet.

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

namespace Acme\Archive;

use Illuminate\Contracts\Foundation\Application;
use Illuminate\Filesystem\FilesystemAdapter;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\ServiceProvider;
use InvalidArgumentException;

class ArchiveServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        Storage::extend('acme-archive-local', static function (
            Application $app,
            array $config,
        ): FilesystemAdapter {
            if (! is_string($config['root'] ?? null) || $config['root'] === '') {
                throw new InvalidArgumentException('Archive disk requires a root.');
            }

            return $app['filesystem']->createLocalDriver($config);
        });
    }
}
```

Registrieren Sie diesen Provider [auf die übliche Weise im Paket](/de/advanced/package-discovery). Schränken Sie die Registrierung nicht mit `runningInConsole()` ein, damit der Treiber in HTTP-Anfragen, Artisan und Queue-Workern verfügbar ist. Machen Sie außerdem einen Provider, der nur die Erweiterung registriert, nicht zu einem [verzögerten Provider](/de/advanced/deferred-provider).

Der Callback erhält die Anwendung und **die gesamte Konfiguration der aufzulösenden Disk**. Validieren Sie die Konfiguration und erzeugen Sie Clients innerhalb des Callbacks, und führen Sie in `boot()` keine Dateioperationen aus. `createLocalDriver()` im Beispiel kann bei der Erzeugung ein Verzeichnis anlegen. Deshalb ist es wichtig, Registrierungszeitpunkt und Erzeugungszeitpunkt zu trennen.

<Tip>
  `FilesystemManager::extend()` bindet `$this` einer normalen Closure an den Manager. Gehen Sie nicht davon aus, dass Sie `$this` des Providers verwenden können, sondern nutzen Sie wie in diesem Beispiel eine `static`-Closure und das Argument `$app`. So bleiben die Abhängigkeiten explizit.
</Tip>

Anwender fügen die Konfiguration dem Array `disks` in `config/filesystems.php` hinzu.

```php theme={null}
'reports' => [
    'driver' => 'acme-archive-local',
    'root' => storage_path('app/private/reports'),
    'throw' => true,
],
'exports' => [
    'driver' => 'acme-archive-local',
    'root' => storage_path('app/private/exports'),
    'throw' => true,
],
```

Der aufrufende Code gibt den Disk-Namen an, nicht den Treibernamen.

```php theme={null}
use Illuminate\Support\Facades\Storage;

Storage::disk('reports')->put('summary.txt', 'Monthly summary');
```

<Info>
  Dieses Beispiel zeigt nur den Erzeugungsweg für lokale Speicherung. Die Auslieferung von Dateien über `serve` und das Bereitstellen temporärer URLs sind nicht Gegenstand dieser Seite. Wenn Ihr eigener Adapter URL-Erzeugung, Sichtbarkeit, Nur-Lese-Modus oder Ähnliches unterstützt, prüfen Sie die jeweilige Implementierung und die benötigten Abhängigkeitspakete. Gehen Sie nicht davon aus, dass eine Funktion schon dadurch aktiv wird, dass Sie einen Konfigurationsschlüssel übergeben.
</Info>

## Benannte Disks werden nach der Erzeugung vorgehalten

`disk('reports')` ruft den Callback nicht jedes Mal auf. Die Methode `get()` des Managers bevorzugt eine bereits erzeugte Instanz im internen Array `$disks`. Nur wenn die Disk noch nicht erzeugt wurde, läuft der Aufruf über `resolve()`, und das Ergebnis wird mit dem Disk-Namen als Schlüssel vorgehalten.

```mermaid theme={null}
flowchart TD
    A["boot() des Providers"] --> B["Erzeugungslogik mit extend() registrieren"]
    B --> C["Storage::disk('reports')"]
    C --> D{"Wurde reports bereits erzeugt?"}
    D -->|Ja| E["Vorgehaltene Instanz zurückgeben"]
    D -->|Nein| F["driver lesen und Konfiguration an die Erzeugungslogik übergeben"]
    F --> G["Ergebnis als reports vorhalten"]
    G --> E
```

Folglich wird eine bereits erzeugte Disk nicht automatisch aktualisiert, wenn Sie im selben Prozess Folgendes tun:

* `filesystems.disks.reports` mit `config()` ändern.
* `Storage::extend()` für denselben Treibernamen erneut aufrufen.

`FilesystemServiceProvider` registriert auch den Manager selbst als Singleton. Gehen Sie in lang laufenden Prozessen nicht davon aus, dass Disks für jede Anfrage oder jeden Job neu erzeugt werden.

## Nach Konfigurationsänderungen nur die benötigten Disks neu auflösen

Wenn Sie im selben Prozess die Konfiguration ändern und das Ergebnis prüfen, trennen Sie die Aktualisierung der Konfiguration vom Entfernen der vorgehaltenen Disk.

```php theme={null}
use Illuminate\Support\Facades\Storage;

$before = Storage::disk('reports');

config(['filesystems.disks.reports.root' => storage_path('app/private/reports-v2')]);
Storage::forgetDisk('reports');

$after = Storage::disk('reports');
```

`forgetDisk()` entfernt den angegebenen Namen aus dem Vorhalte-Array des Managers. Der nächste Aufruf von `disk()` liest die aktuelle Konfiguration neu ein. In der geprüften Version entfernt auch `purge()` lediglich den angegebenen Namen. Es ruft keine Methode zum Schließen der Verbindung eines eigenen Backends auf.

<Warning>
  Referenzen, die der aufrufende Code hält, wie `$before`, werden dadurch nicht ersetzt. Dasselbe gilt, wenn ein Singleton-Service eine alte Disk hält. Verwenden Sie `forgetDisk()` nicht als Mechanismus, um alle Referenzen oder andere Prozesse auf einmal zu aktualisieren.
</Warning>

Bei Konfigurationsänderungen und Paket-Updates in Produktion bauen Sie den Konfigurationscache im Rahmen des normalen Deployments neu auf und starten auch lang laufende Worker neu. Der Konfigurationscache und das prozessinterne Vorhalte-Array für Disks sind zwei verschiedene Dinge.

### Für temporäre Konfigurationen build verwenden

Wenn Sie die gemeinsam genutzte benannte Konfiguration nicht ändern möchten, können Sie eine [On-Demand-Disk](/de/filesystem) verwenden.

```php theme={null}
use Illuminate\Support\Facades\Storage;

$disk = Storage::build([
    'driver' => 'acme-archive-local',
    'root' => storage_path('app/private/one-off-export'),
    'throw' => true,
]);
```

`build()` ruft `resolve()` direkt mit der angegebenen Konfiguration auf und fügt das Ergebnis nicht dem Vorhalte-Array der benannten Disks hinzu. Der eigene Treiber muss zwar registriert sein, aber jeder Aufruf durchläuft die Erzeugungslogik. Auch bei dynamischen Speicherorten sollten Sie das System nicht so gestalten, dass Speicherorte oder Verbindungsziele uneingeschränkt aus Benutzereingaben gewählt werden können.

## Den Erzeugungsweg getrennt von fake testen

`Storage::fake('reports')` ersetzt diese Disk über die Methode `set()` des Managers durch eine lokale Test-Disk. Das ist praktisch, um Dateioperationen der Anwendung zu testen, **prüft aber nicht, ob die eigene Erzeugungslogik des Pakets korrekt registriert und ausgeführt wird**.

Laden Sie `ArchiveServiceProvider` in Ihrer [Testumgebung für Pakete](/de/advanced/package-testing) und prüfen Sie dann die folgenden Punkte getrennt.

| Test | Was geprüft wird |
| - | - |
| Normaler Boot | Wird keine Disk verwendet, starten weder Konfigurationsvalidierung noch Dateioperationen in der Erzeugungslogik |
| Benannte Disks | `reports` und `exports` erhalten jeweils ihre eigene Konfiguration und sind unterschiedliche Instanzen |
| Erneuter Abruf desselben Namens | Zwei Aufrufe von `disk('reports')` liefern dieselbe Instanz |
| Konfigurationsänderung | Nur die Konfiguration zu ändern, behält dieselbe Instanz bei; nach `forgetDisk()` ist es eine andere Instanz |
| On-Demand | Zwei Aufrufe von `build()` mit derselben Konfiguration liefern unterschiedliche Instanzen |
| Ungültige Konfiguration | In diesem Beispiel löst das Auflösen einer Disk ohne `root` eine Exception aus |
| Tatsächliches Lesen und Schreiben | Speichern, Abrufen und Löschen in einem reinen Testverzeichnis prüfen und anschließend aufräumen |
| Mit Konfigurationscache | Auch in einer frisch gebooteten Prüfanwendung ist der Treiber registriert und auflösbar |

Bei einem eigenen externen Backend trennen Sie Erzeugungstests ohne Netzwerkzugriff von Verbindungstests in einer explizit vorbereiteten Prüfumgebung. Werten Sie einen erfolgreichen fake-Test nicht als Nachweis der Kompatibilität mit dem echten Dienst.

## Checkliste für die langfristige Wartung

* Dokumentieren Sie Treibername, Konfigurationsschlüssel, die API des Rückgabewerts sowie den Umgang mit Exceptions und `throw` als öffentliche Spezifikation.
* Verwenden Sie einen Erweiterungsnamen, der nicht mit Standardtreibern oder anderen Paketen kollidiert.
* Lösen Sie Disks nicht schon beim Booten des Providers auf und verwenden Sie sie dort nicht.
* Unterscheiden Sie bei Konfigurationsänderungen zwischen Konfigurationscache, Vorhalte-Array des Managers, Referenzen im aufrufenden Code und anderen Prozessen.
* Prüfen Sie Erzeugung, erneutes Auflösen und tatsächliche Dateioperationen fortlaufend für jede unterstützte Laravel-Version.

## Herangezogene Primärquellen

* [Offizielle Laravel-Dokumentation: Custom Filesystems](https://github.com/laravel/docs/blob/12080c72699b/filesystem.md#custom-filesystems)
* [FilesystemManager: disk, build, resolve, forgetDisk, purge, extend](https://github.com/laravel/framework/blob/v13.37.0/src/Illuminate/Filesystem/FilesystemManager.php)
* [FilesystemServiceProvider: Singleton-Registrierung des Managers](https://github.com/laravel/framework/blob/v13.37.0/src/Illuminate/Filesystem/FilesystemServiceProvider.php)
* [Storage-Fassade: Ersetzen durch eine lokale Disk mit fake](https://github.com/laravel/framework/blob/v13.37.0/src/Illuminate/Support/Facades/Storage.php)
* [Offizielle Laravel-Tests: Erzeugen und Entfernen von Disks, eigene Closures](https://github.com/laravel/framework/blob/v13.37.0/tests/Filesystem/FilesystemManagerTest.php)


## Related topics

- [Artisan-Befehle in Paketen registrieren und Abhängigkeiten auflösen](/de/advanced/package-commands.md)
- [Event-Listener in Paketen registrieren und cachen](/de/advanced/package-events.md)
- [Hooks nach der Service-Auflösung in Paketen](/de/advanced/package-service-resolution.md)
- [Paket-Routen registrieren und cachen](/de/advanced/package-routes.md)
- [Laravel Pennant](/de/pennant.md)


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