v13.37.0. Para las operaciones básicas, consulta Almacenamiento de archivos; para la carga de providers, consulta Descubrimiento automático de paquetes.
Separar el nombre del driver del nombre del disco
El primer argumento deStorage::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.
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 driverlocal 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.
runningInConsole(), para que el driver esté disponible en peticiones HTTP, Artisan y workers de colas. Además, no conviertas en provider diferido 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.
Los usuarios añaden la configuración al array disks de config/filesystems.php.
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.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.
Por lo tanto, hacer lo siguiente en el mismo proceso no actualiza automáticamente un disco ya creado:
- Cambiar
filesystems.disks.reportsconconfig(). - 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.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.
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.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, verifica por separado lo siguiente.
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
- FilesystemManager: disk, build, resolve, forgetDisk, purge, extend
- FilesystemServiceProvider: registro del Manager como singleton
- Fachada Storage: reemplazo por un disco local mediante fake
- Pruebas oficiales de Laravel: creación y eliminación de discos, closures personalizados