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

# Extensión de conexiones de base de datos en paquetes

> Analiza DB::extend y Connection::resolverFor de Laravel 13 y explica la prioridad entre nombres de conexión y nombres de driver, la retención de conexiones, su regeneración y las pruebas de paquetes.

Para extender las conexiones de base de datos desde un paquete, separa **el registro de la lógica de creación** de **la selección de la conexión que se usará**. Diséñalo de modo que la simple instalación no reemplace la conexión predeterminada de la aplicación ni todas las conexiones MySQL.

Esta página se basa en la revisión del código fuente oficial de Laravel Framework `v13.40.0`. Para lo básico de la configuración de conexiones, consulta [Configuración de la base de datos](https://kawax.biz/es/database.md), y para la carga de providers, [Descubrimiento de paquetes](https://kawax.biz/es/advanced/package-discovery.md).

## Elegir el punto de extensión

La creación de conexiones de base de datos ofrece puntos de extensión con alcances distintos.

| Punto de extensión | Alcance | Argumentos del callback |
| - | - | - |
| `DB::extend('接続名', $resolver)` | La conexión con el nombre indicado | Array de configuración, nombre de la conexión |
| `DB::extend('ドライバー名', $resolver)` | Las conexiones que usan ese driver. Sin embargo, la extensión por nombre de conexión tiene prioridad | Array de configuración, nombre de la conexión |
| `Connection::resolverFor('sqlite', $resolver)` | Las Connection de ese driver creadas por el ConnectionFactory estándar | PDO o Closure de creación, nombre de la BD, prefijo de tablas, array de configuración |

El valor devuelto por el callback que pasas a `DB::extend()` no es un PDO ni un Query Builder, sino un `Illuminate\Database\Connection`. `DatabaseManager::configure()` exige esta clase.

> [!WARNING]
>
> El `extend()` de la base de datos no sigue la misma convención de llamada que el `Illuminate\Support\Manager` común. El primer argumento del callback no es el contenedor de la aplicación, sino **el array de configuración de la conexión**. No reutilices tal cual los ejemplos de extensión de otros drivers.

## La extensión por nombre de conexión tiene prioridad sobre el nombre de driver

`DatabaseManager::makeConnection()` obtiene la configuración y luego elige la lógica de creación en el siguiente orden.

```mermaid theme={null}
flowchart TD
    A["Indicar el nombre de conexión con DB::connection"] --> B{"¿Existe una conexión ya creada?"}
    B -->|Sí| C["Devolver la Connection retenida"]
    B -->|No| D["Obtener la configuración y analizar la URL"]
    D --> E{"¿Hay una extensión para el nombre de conexión?"}
    E -->|Sí| F["Ejecutar el resolver del nombre de conexión"]
    E -->|No| G{"¿Hay una extensión para el driver?"}
    G -->|Sí| H["Ejecutar el resolver del driver"]
    G -->|No| I["Crear con el ConnectionFactory estándar"]
    F --> J["Configurar eventos, reconexión, etc. y retener"]
    H --> J
    I --> J
```

* `DB::extend('reporting', ...)` se aplica a `database.connections.reporting`.
* `DB::extend('mysql', ...)` se aplica a las conexiones cuyo `driver` es `mysql` y que no tienen una extensión por nombre de conexión.
* Ambos usan el mismo registro de extensiones. Ten en cuenta también los conflictos entre nombres de conexión y nombres de driver, y usa nombres propios del paquete.
* `extend()` no crea el array de configuración. Si solo registras la extensión y no existe la configuración de la conexión con ese nombre, se lanzará una excepción.

## Seleccionar explícitamente un nombre de driver propio

El siguiente es un ejemplo mínimo que conecta el nombre propio `acme-sqlite` con la creación de una conexión SQLite estándar. **No es un ejemplo de implementación de un nuevo protocolo de BD ni de un dialecto SQL**. Muestra la frontera entre el registro y la selección al distribuir un adaptador propio.

Se asume una estructura en la que `Acme\DatabaseBridge\` corresponde a `src/` mediante PSR-4 de Composer, y el archivo se coloca en `src/DatabaseBridgeServiceProvider.php`.

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

namespace Acme\DatabaseBridge;

use Illuminate\Database\Connection;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\ServiceProvider;

final class DatabaseBridgeServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        $app = $this->app;

        DB::extend('acme-sqlite', function (array $config, string $name) use ($app): Connection {
            $config['driver'] = 'sqlite';

            return $app->make('db.factory')->make($config, $name);
        });
    }
}
```

`db.factory` es el binding del `ConnectionFactory` compartido por Laravel. Como se llama directamente al Factory estándar, no se vuelve a la selección de extensiones del DatabaseManager y se puede reutilizar la creación diferida del PDO y de la Connection de SQLite.

Este callback se ejecuta la primera vez que se resuelve la conexión. El `boot()` del provider solo registra la lógica de creación y no ejecuta pruebas de conexión ni migraciones. Como provider normal no diferido, haz que se cargue antes de que la conexión objetivo se resuelva por primera vez.

La aplicación que lo usa añade la conexión al array `connections` existente en `config/database.php`. No cambia el valor predeterminado `default`.

```php theme={null}
'acme_reports' => [
    'driver' => 'acme-sqlite',
    'database' => database_path('reports.sqlite'),
    'prefix' => '',
    'foreign_key_constraints' => true,
],
```

Prepara el archivo SQLite de antemano y comprueba los permisos de escritura antes de usarlo. No diseñes el paquete para que cree el archivo con solo cargarse.

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

$connection = DB::connection('acme_reports');
$rows = $connection->select('select 1 as value');
```

Lo que el usuario pasa es el nombre de conexión `acme_reports`, no el nombre de driver `acme-sqlite`. En el ejemplo, `driver` se convierte a `sqlite` antes de pasarlo al Factory, por lo que `getDriverName()` tras la creación también devuelve `sqlite`.

> [!TIP]
>
> Si vuelves a llamar a `DB::connection($name)` dentro del resolver, regresas a la creación de la misma conexión, que todavía no está retenida. Para evitar la recursión, crea la Connection con el Factory o con tu propio adaptador.

## Reemplazar por una subclase de Connection es otro alcance

Para reemplazar la propia clase Connection de un driver existente, existe `Connection::resolverFor()`. El callback **puede recibir una Closure que crea el PDO de forma diferida**, en lugar de un PDO ya conectado.

Sin embargo, este registro se guarda en una propiedad estática de la clase Connection. Si lo registras para `sqlite`, afectará a todas las conexiones SQLite nuevas creadas por el Factory estándar, incluido el ejemplo `acme-sqlite` anterior. No puede tratarse como un reemplazo para un único nombre de conexión.

* En el callback, devuelve una subclase de Connection para la BD objetivo. Con la Connection base por sí sola no se conservan el Grammar, el Processor y demás elementos propios de la BD.
* El Factory separa `createConnector()` de `createConnection()`. Registrar solo `resolverFor()` no proporciona un conector PDO para un driver desconocido.
* Si el resolver propio de `DB::extend()` no usa el Factory estándar, tampoco pasa por la comprobación de `resolverFor()` dentro del Factory.
* Volver a registrar el mismo driver lo sobrescribe. Comprueba los conflictos con otros paquetes y que no se arrastre entre pruebas dentro del mismo proceso PHP.

Los paquetes que quieran limitar el impacto deberían considerar primero `DB::extend()` con un nombre de conexión o de driver propio.

## Distinguir reconnect y purge

El DatabaseManager retiene las Connection creadas por nombre de conexión. Aunque cambies la configuración o el resolver, los cambios no se reflejan automáticamente en las conexiones ya creadas.

| Operación | Efecto sobre la Connection retenida |
| - | - |
| `DB::extend()` | Registra o sobrescribe la lógica de creación para la próxima vez. El objeto existente se mantiene |
| `DB::disconnect($name)` | Desconecta el PDO, pero conserva la Connection |
| `DB::reconnect($name)` | Si está retenida, transfiere el PDO y similares desde una conexión recién creada. Devuelve la Connection original |
| `DB::purge($name)` | Desconecta y la excluye de lo que retiene el Manager. Se vuelve a crear en el siguiente `connection()` |
| `DB::forgetExtension($key)` | Elimina el registro del resolver. No elimina la configuración ni las conexiones ya creadas |

En particular, `reconnect()` no es una API que reemplace por completo la clase o el array de configuración de la Connection retenida. Si quieres reflejar cambios en la clase extendida o en la configuración de la conexión, ejecuta `purge()` en un momento seguro sin transacciones en curso y luego obtén una nueva conexión.

> [!WARNING]
>
> `purge()` no sustituye por la nueva las referencias a la Connection que retengan otros servicios o variables. Además, `::read`, `::write` y `::direct` en el nombre de conexión son claves de retención distintas, así que no asumas que purgar el nombre base las elimina todas a la vez.
>
> No reescribas a la ligera la configuración de una conexión compartida en ejecución, por ejemplo para cambiar de tenant. En workers de larga duración, comprueba no solo la actualización de la caché de configuración, sino también el reinicio del proceso y el impacto sobre los servicios retenidos.

## Probar el registro y la creación del paquete

Con [Orchestra Testbench](https://kawax.biz/es/advanced/package-testing.md), carga el provider y selecciona realmente el driver propio. El siguiente es un ejemplo de `tests/DatabaseBridgeTest.php`. El entorno de ejecución necesita PDO y el driver de SQLite.

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

namespace Acme\DatabaseBridge\Tests;

use Acme\DatabaseBridge\DatabaseBridgeServiceProvider;
use Illuminate\Database\SQLiteConnection;
use Orchestra\Testbench\TestCase;

final class DatabaseBridgeTest extends TestCase
{
    protected function getPackageProviders($app): array
    {
        return [DatabaseBridgeServiceProvider::class];
    }

    protected function defineEnvironment($app): void
    {
        $app['config']->set('database.default', 'testing');
        $app['config']->set('database.connections.acme_reports', [
            'driver' => 'acme-sqlite',
            'database' => ':memory:',
            'prefix' => '',
        ]);
    }

    public function test_registration_does_not_create_connections(): void
    {
        $manager = $this->app->make('db');

        $this->assertSame('testing', $manager->getDefaultConnection());
        $this->assertArrayNotHasKey('acme_reports', $manager->getConnections());
    }

    public function test_connection_is_reused_and_purge_creates_a_new_one(): void
    {
        $manager = $this->app->make('db');
        $first = $manager->connection('acme_reports');

        $this->assertInstanceOf(SQLiteConnection::class, $first);
        $this->assertSame('acme_reports', $first->getName());
        $this->assertSame('sqlite', $first->getDriverName());
        $this->assertSame(1, (int) $first->selectOne('select 1 as value')->value);
        $this->assertSame($first, $manager->connection('acme_reports'));
        $this->assertSame($first, $manager->reconnect('acme_reports'));

        $manager->purge('acme_reports');

        $second = $manager->connection('acme_reports');

        $this->assertNotSame($first, $second);
        $this->assertSame(1, (int) $second->selectOne('select 1 as value')->value);
    }
}
```

Como `:memory:` pierde su contenido al desconectarse, esta prueba no espera que se conserven tablas ni datos. En un paquete real, añade también lo siguiente:

* Que el resolver por nombre de conexión tenga prioridad sobre el resolver por nombre de driver.
* Que volver a registrar el mismo nombre y `forgetExtension()` no reemplacen las Connection ya creadas.
* Las excepciones cuando falta configuración obligatoria y el comportamiento cuando falla la conexión.
* Las funciones que te comprometes a ofrecer, como migraciones, transacciones o separación read/write en las BD compatibles.
* Que el registro y el estado de las conexiones no se mezclen en workers de larga duración ni al combinarse con varios paquetes.

## Puntos de comprobación para el mantenimiento

* ¿Se activa mediante un nombre propio (opt-in) sin sobrescribir en silencio la conexión predeterminada ni los drivers integrados?
* ¿Evitas meter Closures u objetos de conexión en la caché de configuración y colocas la lógica de creación en el provider?
* ¿Gestionas los destinos de conexión y las credenciales como configuración de aplicación de confianza?
* ¿Has vuelto a comprobar los argumentos de `extend()`, el orden de creación del Factory y lo que transfiere `reconnect()` en las versiones de Laravel soportadas?
* ¿Indicas a los usuarios el procedimiento de regeneración tras cambiar la configuración y el reinicio de los workers?

## Páginas relacionadas

* [Desarrollo de paquetes](https://kawax.biz/es/advanced/package-development.md)
* [Diseño del ciclo de vida de los servicios de paquetes](https://kawax.biz/es/advanced/package-service-lifetimes.md)
* [Fusión y caché de la configuración de paquetes](https://kawax.biz/es/advanced/package-config-merging.md)
* [Publicación y actualización de migraciones de paquetes](https://kawax.biz/es/advanced/package-migrations.md)

## Fuentes primarias consultadas

* [Documentación oficial de Laravel: base de datos y múltiples conexiones](https://github.com/laravel/docs/blob/13.x/database.md#using-multiple-database-connections)
* [Documentación oficial de Laravel: service providers de paquetes](https://github.com/laravel/docs/blob/13.x/packages.md#service-providers)
* [DatabaseManager: orden de creación, registro de extensiones, retención de conexiones y reconexión](https://github.com/laravel/framework/blob/v13.40.0/src/Illuminate/Database/DatabaseManager.php)
* [ConnectionFactory: creación diferida del PDO y resolver de Connection](https://github.com/laravel/framework/blob/v13.40.0/src/Illuminate/Database/Connectors/ConnectionFactory.php)
* [Connection: resolverFor y getResolver](https://github.com/laravel/framework/blob/v13.40.0/src/Illuminate/Database/Connection.php)
* [DatabaseServiceProvider: registro de db y db.factory](https://github.com/laravel/framework/blob/v13.40.0/src/Illuminate/Database/DatabaseServiceProvider.php)


## Related topics

- [Migraciones de base de datos](https://kawax.biz/es/migrations.md)
- [Configuración de la base de datos](https://kawax.biz/es/database.md)
- [Extensiones de Blade en paquetes y caché de compilación](https://kawax.biz/es/advanced/package-blade-extensions.md)
- [Extensión y nueva resolución de canales de log en paquetes](https://kawax.biz/es/advanced/package-logging.md)
- [Concurrencia](https://kawax.biz/es/concurrency.md)


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