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

# Extension des connexions de base de données pour les packages

> Analysez DB::extend et Connection::resolverFor de Laravel 13 pour comprendre la priorité entre nom de connexion et nom de driver, la conservation des connexions, leur recréation et les tests de package.

Pour étendre les connexions de base de données depuis un package, séparez **l'enregistrement de la méthode de création** de **la sélection de la connexion utilisée**. Le simple fait d'installer le package ne doit remplacer ni la connexion par défaut de l'application, ni toutes les connexions MySQL.

Cette page a été vérifiée sur l'implémentation officielle de Laravel Framework `v13.40.0`. Pour les bases de la configuration des connexions, consultez [Configuration de la base de données](https://kawax.biz/fr/database.md), et pour le chargement des providers, consultez [Découverte automatique des packages](https://kawax.biz/fr/advanced/package-discovery.md).

## Choisir le point d'extension

Le processus de création de la base de données propose des points d'extension de portées différentes.

| Point d'extension | Portée | Arguments du callback |
| - | - | - |
| `DB::extend('nom de connexion', $resolver)` | La connexion portant ce nom | Tableau de configuration, nom de connexion |
| `DB::extend('nom de driver', $resolver)` | Les connexions utilisant ce driver. L'extension par nom de connexion reste prioritaire | Tableau de configuration, nom de connexion |
| `Connection::resolverFor('sqlite', $resolver)` | Les Connection de ce driver créées par la ConnectionFactory standard | PDO ou Closure de création, nom de la base, préfixe de table, tableau de configuration |

La valeur de retour du callback passé à `DB::extend()` n'est ni un PDO ni un Query Builder, mais une `Illuminate\Database\Connection`. `DatabaseManager::configure()` exige cette classe.

> [!WARNING]
>
> Le `extend()` de la base de données ne suit pas la même convention d'appel que le `Illuminate\Support\Manager` commun. Le premier argument du callback n'est pas le conteneur de l'application, mais **le tableau de configuration de la connexion**. Ne réutilisez pas tels quels les exemples d'extension d'autres drivers.

## L'extension par nom de connexion prime sur le nom de driver

`DatabaseManager::makeConnection()` récupère la configuration, puis choisit la méthode de création dans l'ordre suivant.

```mermaid theme={null}
flowchart TD
    A["Indiquer le nom de connexion avec DB::connection"] --> B{"Une connexion déjà créée existe ?"}
    B -->|Oui| C["Retourner la Connection conservée"]
    B -->|Non| D["Récupérer la configuration et analyser l'URL"]
    D --> E{"Extension pour le nom de connexion ?"}
    E -->|Oui| F["Exécuter le resolver du nom de connexion"]
    E -->|Non| G{"Extension pour le driver ?"}
    G -->|Oui| H["Exécuter le resolver du driver"]
    G -->|Non| I["Créer avec la ConnectionFactory standard"]
    F --> J["Configurer événements, reconnexion, etc. et conserver"]
    H --> J
    I --> J
```

* `DB::extend('reporting', ...)` s'applique à `database.connections.reporting`.
* `DB::extend('mysql', ...)` s'applique aux connexions dont le `driver` est `mysql` et qui n'ont pas d'extension côté nom de connexion.
* Les deux utilisent le même registre d'extensions. En tenant compte des collisions entre noms de connexion et noms de driver, utilisez des noms propres au package.
* `extend()` ne crée pas de tableau de configuration. Si vous enregistrez seulement l'extension sans configuration de connexion portant ce nom, une exception est levée.

## Sélectionner explicitement un nom de driver propre

Voici un exemple minimal qui relie le nom propre `acme-sqlite` à la création standard d'une connexion SQLite. **Ce n'est pas un exemple d'implémentation d'un nouveau protocole de base de données ou d'un dialecte SQL**. Il montre la frontière entre enregistrement et sélection lors de la distribution d'un adaptateur propre.

On suppose une structure où `Acme\DatabaseBridge\` correspond à `src/` via le PSR-4 de Composer, et le fichier est placé dans `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` est le binding de la `ConnectionFactory` partagée par Laravel. Comme la Factory standard est appelée directement, on ne revient pas à la sélection d'extension du DatabaseManager, et on peut réutiliser la création différée du PDO SQLite ainsi que la création de la Connection.

Ce callback est exécuté lors de la première résolution de la connexion. Le `boot()` du provider se contente d'enregistrer la méthode de création, sans tester la connexion ni exécuter de migrations. Chargez-le comme un provider normal non différé, avant que la connexion cible ne soit résolue pour la première fois.

L'application utilisatrice ajoute la connexion au tableau `connections` existant de `config/database.php`. Le `default` n'est pas modifié.

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

Préparez le fichier SQLite à l'avance et vérifiez les droits d'écriture avant de l'utiliser. Ne concevez pas le package pour qu'il crée le fichier dès son chargement.

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

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

L'utilisateur transmet le nom de connexion `acme_reports`, et non le nom de driver `acme-sqlite`. Dans l'exemple, le `driver` est converti en `sqlite` avant d'être transmis à la Factory, donc `getDriverName()` retourne aussi `sqlite` après la création.

> [!TIP]
>
> Rappeler `DB::connection($name)` depuis le resolver relance la création de la même connexion, qui n'est pas encore conservée. Pour éviter la récursion, créez la Connection avec la Factory ou votre propre adaptateur.

## Le remplacement par une sous-classe de Connection a une autre portée

Pour remplacer la classe Connection elle-même d'un driver existant, il existe `Connection::resolverFor()`. Le callback peut recevoir **une Closure qui crée le PDO de manière différée**, plutôt qu'un PDO déjà connecté.

Cependant, cet enregistrement est conservé dans une propriété statique de la classe Connection. Si vous l'enregistrez pour `sqlite`, il affecte toutes les connexions SQLite nouvellement créées par la Factory standard, y compris l'exemple `acme-sqlite` ci-dessus. Il ne peut pas servir à remplacer une seule connexion nommée.

* Le callback doit retourner une sous-classe de Connection propre à la base cible. La Connection de base seule ne conserve pas la Grammar, le Processor, etc. propres à la base de données.
* La Factory sépare `createConnector()` et `createConnection()`. Enregistrer `resolverFor()` ne fournit pas pour autant un connecteur PDO pour un driver inconnu.
* Si le resolver propre de `DB::extend()` n'utilise pas la Factory standard, la vérification de `resolverFor()` dans la Factory n'est pas non plus effectuée.
* Un nouvel enregistrement pour le même driver écrase le précédent. Vérifiez les conflits avec d'autres packages et la persistance entre tests au sein d'un même processus PHP.

Un package qui souhaite limiter son impact devrait d'abord envisager `DB::extend()` avec un nom de connexion ou de driver propre.

## Distinguer reconnect et purge

Le DatabaseManager conserve les Connection créées par nom de connexion. Modifier la configuration ou le resolver n'est pas répercuté automatiquement sur les connexions déjà créées.

| Opération | Effet sur la Connection conservée |
| - | - |
| `DB::extend()` | Enregistre ou écrase le prochain processus de création. L'objet existant reste inchangé |
| `DB::disconnect($name)` | Déconnecte le PDO, mais conserve la Connection |
| `DB::reconnect($name)` | Si elle est conservée, transfère le PDO, etc. depuis une connexion nouvellement créée. Retourne la Connection d'origine |
| `DB::purge($name)` | Déconnecte et retire la connexion de celles conservées par le Manager. Elle est recréée au prochain `connection()` |
| `DB::forgetExtension($key)` | Supprime l'enregistrement du resolver. Ne supprime ni la configuration ni les connexions créées |

En particulier, `reconnect()` n'est pas une API qui remplace entièrement la classe ou le tableau de configuration de la Connection conservée. Pour appliquer un changement de classe d'extension ou de configuration de connexion, appelez `purge()` à un moment sûr, sans transaction en cours, puis récupérez une nouvelle connexion.

> [!WARNING]
>
> `purge()` ne remplace pas les références à la Connection détenues par d'autres services ou variables. De plus, les noms de connexion `::read`, `::write` et `::direct` sont des clés de conservation distinctes : ne supposez pas qu'un purge du nom de base les supprime toutes en une fois.
>
> Ne réécrivez pas à la légère la configuration d'une connexion partagée en cours d'exécution, par exemple pour changer de tenant. Avec des workers de longue durée, vérifiez non seulement la mise à jour du cache de configuration, mais aussi le redémarrage du processus et l'impact sur les services conservés.

## Tester l'enregistrement et la création du package

Avec [Orchestra Testbench](https://kawax.biz/fr/advanced/package-testing.md), chargez le provider et sélectionnez réellement le driver propre. Voici un exemple de `tests/DatabaseBridgeTest.php`. L'environnement d'exécution nécessite PDO et le driver 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);
    }
}
```

Comme le contenu de `:memory:` est perdu à la déconnexion, ce test ne s'attend pas à ce que les tables ou les données soient conservées. Dans un vrai package, ajoutez également :

* Le resolver du nom de connexion est prioritaire sur celui du nom de driver.
* Un nouvel enregistrement sous le même nom et `forgetExtension()` ne remplacent pas les Connection déjà créées.
* Les exceptions en cas de configuration obligatoire manquante, et le comportement en cas d'échec de connexion.
* Les fonctionnalités que vous vous engagez à fournir sur les bases prises en charge : migrations, transactions, séparation read/write, etc.
* L'enregistrement et l'état des connexions ne se mélangent pas avec des workers de longue durée ou en combinaison avec plusieurs packages.

## Points à vérifier lors de la maintenance

* L'activation se fait-elle via un nom propre, sans écraser silencieusement la connexion par défaut ou les drivers intégrés ?
* Évitez-vous de placer des Closure ou des objets de connexion dans le cache de configuration, en gardant le processus de création dans le provider ?
* Les destinations de connexion et les identifiants sont-ils gérés comme une configuration d'application de confiance ?
* Avez-vous revérifié les arguments de `extend()`, l'ordre de création de la Factory et les éléments transférés par `reconnect()` sur les versions de Laravel prises en charge ?
* Indiquez-vous aux utilisateurs la procédure de recréation après un changement de configuration et le redémarrage des workers ?

## Pages associées

* [Développement de packages](https://kawax.biz/fr/advanced/package-development.md)
* [Conception du cycle de vie des services de package](https://kawax.biz/fr/advanced/package-service-lifetimes.md)
* [Fusion et cache de la configuration des packages](https://kawax.biz/fr/advanced/package-config-merging.md)
* [Publication et mise à jour des migrations de package](https://kawax.biz/fr/advanced/package-migrations.md)

## Sources primaires consultées

* [Documentation officielle Laravel : bases de données et connexions multiples](https://github.com/laravel/docs/blob/13.x/database.md#using-multiple-database-connections)
* [Documentation officielle Laravel : service providers des packages](https://github.com/laravel/docs/blob/13.x/packages.md#service-providers)
* [DatabaseManager : ordre de création, enregistrement des extensions, conservation et reconnexion](https://github.com/laravel/framework/blob/v13.40.0/src/Illuminate/Database/DatabaseManager.php)
* [ConnectionFactory : création différée du PDO et resolver de Connection](https://github.com/laravel/framework/blob/v13.40.0/src/Illuminate/Database/Connectors/ConnectionFactory.php)
* [Connection : resolverFor et getResolver](https://github.com/laravel/framework/blob/v13.40.0/src/Illuminate/Database/Connection.php)
* [DatabaseServiceProvider : enregistrement de db et db.factory](https://github.com/laravel/framework/blob/v13.40.0/src/Illuminate/Database/DatabaseServiceProvider.php)


## Related topics

- [Migrations de base de données](https://kawax.biz/fr/migrations.md)
- [Configuration de la base de données](https://kawax.biz/fr/database.md)
- [Drivers de session personnalisés pour les packages](https://kawax.biz/fr/advanced/package-session-drivers.md)
- [Seeding de base de données](https://kawax.biz/fr/seeding.md)
- [Structure des répertoires](https://kawax.biz/fr/directory-structure.md)


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