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

# Registro y nueva resolución de drivers de Storage en paquetes

> A partir del FilesystemManager de Laravel 13, explica el registro con Storage::extend(), la retención de instancias de discos, la nueva resolución tras cambios de configuración y la verificación sin depender de fake.

Cuando quieras ofrecer funciones de almacenamiento desde un paquete, separa **el registro de cómo se crea un driver** de **la creación de los discos del usuario**. Si diseñas el paquete para que el simple arranque del provider no abra conexiones externas ni inicie operaciones con archivos, será más difícil romper el arranque en entornos que no usan la función.

Esta página se basa en la implementación de Laravel Framework `v13.37.0`. Para las operaciones básicas, consulta [Almacenamiento de archivos](/es/filesystem); para la carga de providers, consulta [Descubrimiento automático de paquetes](/es/advanced/package-discovery).

## Separar el nombre del driver del nombre del disco

El primer argumento de `Storage::extend()` es un **nombre de driver**. En cambio, el argumento de `Storage::disk()` es un **nombre de disco** definido en `filesystems.disks`. Puedes definir varios discos que usen el mismo driver.

| Elemento | Ejemplo en esta página | Función |
| - | - | - |
| Nombre de driver | `acme-archive-local` | Identificador del callback que crea los discos |
| Nombre de disco | `reports`, `exports` | Identificador de la configuración y de la instancia creada |
| Configuración del disco | `root`, `throw`, etc. | Valores que se pasan al callback al crear ese disco |

`FilesystemManager::resolve()` busca una lógica de creación personalizada que corresponda al `driver` de la configuración y, si existe, la llama antes que los drivers integrados. Usar `local` o `s3` como nombre de extensión reemplaza la lógica de creación existente, así que elige un nombre propio de tu paquete.

## Registrar en el provider solo la lógica de creación

El siguiente ejemplo mínimo reutiliza la lógica de creación del driver local estándar de Laravel para entender el flujo de registro. Si solo necesitas almacenamiento local, basta con el driver `local` habitual. Si ofreces un backend propio, sustitúyelo por código que construya un adaptador de Flysystem y lo envuelva en el `FilesystemAdapter` de Laravel, como en el ejemplo de la documentación oficial.

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

Registra este provider [en el paquete de la forma habitual](/es/advanced/package-discovery). No limites el registro con `runningInConsole()`, para que el driver esté disponible en peticiones HTTP, Artisan y workers de colas. Además, no conviertas en [provider diferido](/es/advanced/deferred-provider) un provider cuya única tarea es registrar la extensión.

El callback recibe la aplicación y **la configuración completa del disco que se está resolviendo**. Valida la configuración y crea los clientes dentro del callback, y no realices operaciones con archivos en `boot()`. El `createLocalDriver()` del ejemplo puede crear un directorio en el momento de la creación, por lo que es importante separar el momento del registro del momento de la creación.

<Tip>
  `FilesystemManager::extend()` vincula el `$this` de un closure normal al Manager. En lugar de suponer que puedes usar el `$this` del provider, usa un closure `static` y el argumento `$app` como en este ejemplo para que las dependencias queden explícitas.
</Tip>

Los usuarios añaden la configuración al array `disks` de `config/filesystems.php`.

```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,
],
```

El código que lo usa indica el nombre del disco, no el nombre del driver.

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

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

<Info>
  Este ejemplo solo muestra el flujo de creación para almacenamiento local. La entrega de archivos mediante `serve` y la provisión de URLs temporales quedan fuera de su alcance. Si tu adaptador propio admite generación de URLs, visibilidad, modo de solo lectura u otras funciones, revisa cada implementación y los paquetes de dependencias necesarios. No supongas que una función se activa solo por pasar una clave de configuración.
</Info>

## Los discos con nombre se retienen tras su creación

`disk('reports')` no llama al callback cada vez. El método `get()` del Manager da prioridad a la instancia ya creada que se encuentra en su array interno `$disks`. Solo si el disco aún no se ha creado pasa por `resolve()`, y el resultado se retiene usando el nombre del disco como clave.

```mermaid theme={null}
flowchart TD
    A["boot() del provider"] --> B["Registrar la lógica de creación con extend()"]
    B --> C["Storage::disk('reports')"]
    C --> D{"¿Ya se creó reports?"}
    D -->|Sí| E["Devolver la instancia retenida"]
    D -->|No| F["Leer driver y pasar la configuración a la lógica de creación"]
    F --> G["Retener el resultado como reports"]
    G --> E
```

Por lo tanto, hacer lo siguiente en el mismo proceso no actualiza automáticamente un disco ya creado:

* Cambiar `filesystems.disks.reports` con `config()`.
* Volver a llamar a `Storage::extend()` para el mismo nombre de driver.

`FilesystemServiceProvider` también registra el propio Manager como singleton. En procesos de larga duración, no supongas que los discos se recrean siempre en cada petición o job.

## Volver a resolver solo los discos necesarios tras cambiar la configuración

Cuando cambies la configuración y verifiques el resultado dentro del mismo proceso, separa la actualización de la configuración de la eliminación del disco retenido.

```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()` elimina el nombre indicado del array de retención del Manager. La siguiente llamada a `disk()` vuelve a leer la configuración actual. En la versión revisada, `purge()` también se limita a eliminar el nombre indicado. No llama a ningún método de cierre de conexión de un backend propio.

<Warning>
  Las referencias que conserva el código que lo usa, como `$before`, no se reemplazan. Lo mismo ocurre si un servicio singleton conserva un disco antiguo. No uses `forgetDisk()` como mecanismo para actualizar de una vez todas las referencias u otros procesos.
</Warning>

Para cambios de configuración y actualizaciones de paquetes en producción, regenera la caché de configuración como parte del despliegue habitual y reinicia también los workers de larga duración. La caché de configuración y el array de retención de discos dentro del proceso son cosas distintas.

### Usar build para configuraciones temporales

Si no quieres modificar la configuración con nombre compartida, puedes usar un [disco bajo demanda](/es/filesystem).

```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()` llama directamente a `resolve()` con la configuración indicada y no añade el resultado al array de retención de discos con nombre. El driver personalizado debe estar registrado, pero cada llamada pasa por la lógica de creación. Aunque uses ubicaciones de almacenamiento dinámicas, no diseñes el sistema para que el destino de almacenamiento o de conexión pueda elegirse sin restricciones a partir de la entrada del usuario.

## Probar el flujo de creación por separado de fake

`Storage::fake('reports')` reemplaza ese disco por un disco local de pruebas mediante el método `set()` del Manager. Es útil para probar las operaciones con archivos de la aplicación, pero **no verifica que la lógica de creación personalizada del paquete se registre y se ejecute correctamente**.

Después de cargar `ArchiveServiceProvider` en tu [entorno de pruebas del paquete](/es/advanced/package-testing), verifica por separado lo siguiente.

| Prueba | Qué se comprueba |
| - | - |
| Arranque normal | Si no se usa ningún disco, no se inician la validación de configuración ni las operaciones con archivos de la lógica de creación |
| Discos con nombre | `reports` y `exports` reciben cada uno su configuración y son instancias distintas |
| Nueva obtención del mismo nombre | Llamar dos veces a `disk('reports')` devuelve la misma instancia |
| Cambio de configuración | Cambiar solo la configuración mantiene la misma instancia; tras `forgetDisk()` se obtiene una instancia distinta |
| Bajo demanda | Llamar dos veces a `build()` con la misma configuración devuelve instancias distintas |
| Configuración no válida | En este ejemplo, resolver un disco sin `root` lanza una excepción |
| Lectura y escritura reales | Verificar guardado, obtención y borrado en un directorio exclusivo para pruebas y limpiarlo al terminar |
| Con caché de configuración | El driver está registrado y se puede resolver también en una aplicación de verificación recién arrancada |

Con un backend externo propio, separa las pruebas de creación que no usan la red de las pruebas de conexión en un entorno de verificación preparado explícitamente. No tomes el éxito con fake como prueba de compatibilidad con el servicio real.

## Lista de comprobación para el mantenimiento a largo plazo

* Documenta como especificación pública el nombre del driver, las claves de configuración, la API del valor devuelto y el tratamiento de excepciones y de `throw`.
* Usa un nombre de extensión que no entre en conflicto con los drivers estándar ni con otros paquetes.
* No resuelvas ni uses discos solo por arrancar el provider.
* Ante cambios de configuración, distingue entre la caché de configuración, el array de retención del Manager, las referencias del código que lo usa y otros procesos.
* Verifica de forma continua la creación, la nueva resolución y las operaciones reales con archivos en cada versión de Laravel compatible.

## Fuentes primarias consultadas

* [Documentación oficial de Laravel: sistemas de archivos personalizados](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: registro del Manager como singleton](https://github.com/laravel/framework/blob/v13.37.0/src/Illuminate/Filesystem/FilesystemServiceProvider.php)
* [Fachada Storage: reemplazo por un disco local mediante fake](https://github.com/laravel/framework/blob/v13.37.0/src/Illuminate/Support/Facades/Storage.php)
* [Pruebas oficiales de Laravel: creación y eliminación de discos, closures personalizados](https://github.com/laravel/framework/blob/v13.37.0/tests/Filesystem/FilesystemManagerTest.php)


## Related topics

- [Registro de comandos Artisan de paquetes y resolución de dependencias](/es/advanced/package-commands.md)
- [Hooks de resolución de servicios en paquetes](/es/advanced/package-service-resolution.md)
- [Registro y caché de listeners de eventos en paquetes](/es/advanced/package-events.md)
- [Registro de rutas y caché en paquetes](/es/advanced/package-routes.md)
- [Diseño del ciclo de vida de los servicios de paquetes](/es/advanced/package-service-lifetimes.md)


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