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

# Estendere le connessioni al database nei pacchetti

> Analizzare DB::extend e Connection::resolverFor di Laravel 13 per comprendere la priorità tra nome della connessione e nome del driver, la conservazione delle connessioni, la ricreazione e i test dei pacchetti.

Per estendere le connessioni al database da un pacchetto, separa **la registrazione del metodo di creazione** dalla **selezione della connessione da usare**. Progetta il pacchetto in modo che la semplice installazione non sostituisca la connessione predefinita dell'applicazione né tutte le connessioni MySQL.

Questa pagina esamina i sorgenti ufficiali di Laravel Framework `v13.40.0`. Per le basi della configurazione delle connessioni consulta [Configurazione del database](https://kawax.biz/it/database.md), per il caricamento dei provider consulta [Package auto-discovery](https://kawax.biz/it/advanced/package-discovery.md).

## Scegliere il punto di estensione

Il processo di creazione del database offre punti di estensione con ambiti diversi.

| Punto di estensione | Ambito | Argomenti del callback |
| - | - | - |
| `DB::extend('nome connessione', $resolver)` | La connessione con il nome indicato | Array di configurazione, nome della connessione |
| `DB::extend('nome driver', $resolver)` | Le connessioni che usano quel driver. L'estensione per nome della connessione ha però la precedenza | Array di configurazione, nome della connessione |
| `Connection::resolverFor('sqlite', $resolver)` | Le Connection di quel driver create dalla ConnectionFactory standard | PDO o Closure di creazione, nome del DB, prefisso delle tabelle, array di configurazione |

Il valore restituito dal callback passato a `DB::extend()` non è un PDO né un Query Builder, ma una `Illuminate\Database\Connection`. È `DatabaseManager::configure()` a richiedere questa classe.

> [!WARNING]
>
> L'`extend()` del database non segue la stessa convenzione di chiamata del comune `Illuminate\Support\Manager`. Il primo argomento del callback non è il container dell'applicazione, ma **l'array di configurazione della connessione**. Non riutilizzare così come sono gli esempi di estensione di altri driver.

## L'estensione per nome della connessione ha la precedenza sul nome del driver

`DatabaseManager::makeConnection()`, dopo aver ottenuto la configurazione, sceglie il metodo di creazione nel seguente ordine.

```mermaid theme={null}
flowchart TD
    A["Indicare il nome della connessione con DB::connection"] --> B{"Esiste già una connessione creata?"}
    B -->|Sì| C["Restituire la Connection conservata"]
    B -->|No| D["Ottenere la configurazione e analizzare l'URL"]
    D --> E{"Esiste un'estensione per il nome della connessione?"}
    E -->|Sì| F["Eseguire il resolver del nome della connessione"]
    E -->|No| G{"Esiste un'estensione per il driver?"}
    G -->|Sì| H["Eseguire il resolver del driver"]
    G -->|No| I["Creare con la ConnectionFactory standard"]
    F --> J["Configurare eventi, riconnessione ecc. e conservare"]
    H --> J
    I --> J
```

* `DB::extend('reporting', ...)` si applica a `database.connections.reporting`.
* `DB::extend('mysql', ...)` si applica alle connessioni con `driver` uguale a `mysql` che non hanno un'estensione per nome della connessione.
* Entrambi usano lo stesso registro delle estensioni. Considerando anche le collisioni tra nomi di connessione e nomi di driver, usa nomi specifici del pacchetto.
* `extend()` non crea l'array di configurazione. Registrando solo l'estensione, se non esiste la configurazione di una connessione con il nome indicato si verifica un'eccezione.

## Selezionare esplicitamente un nome di driver personalizzato

Di seguito un esempio minimo che collega il nome personalizzato `acme-sqlite` alla creazione di una connessione SQLite standard. **Non è un esempio di implementazione di un nuovo protocollo di DB o di un dialetto SQL**. Mostra il confine tra registrazione e selezione quando si distribuisce un adattatore personalizzato.

Si presuppone una struttura in cui `Acme\DatabaseBridge\` corrisponde a `src/` tramite PSR-4 di Composer, e il file si trova in `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` è il binding della `ConnectionFactory` condivisa da Laravel. Poiché chiama direttamente la Factory standard, non torna alla selezione delle estensioni del DatabaseManager e può riutilizzare la creazione lazy del PDO e la creazione della Connection di SQLite.

Questo callback viene eseguito alla prima risoluzione della connessione. Il `boot()` del provider si limita a registrare il metodo di creazione e non esegue test di connessione né migrazioni. Come normale provider non differito, fallo caricare prima che la connessione interessata venga risolta per la prima volta.

L'applicazione utilizzatrice aggiunge la connessione all'array `connections` esistente in `config/database.php`. Il valore predefinito `default` non viene modificato.

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

Prepara in anticipo il file SQLite e verifica i permessi di scrittura prima dell'uso. Non progettare il pacchetto in modo che crei il file al semplice caricamento.

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

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

Ciò che l'utente passa è il nome della connessione `acme_reports`, non il nome del driver `acme-sqlite`. Nell'esempio, `driver` viene convertito in `sqlite` prima di essere passato alla Factory, quindi anche `getDriverName()` dopo la creazione restituisce `sqlite`.

> [!TIP]
>
> Se dall'interno del resolver richiami `DB::connection($name)`, torni alla creazione della stessa connessione non ancora conservata. Per evitare la ricorsione, crea la Connection con la Factory o con un adattatore personalizzato.

## La sostituzione con una sottoclasse di Connection ha un ambito diverso

Per sostituire la classe Connection stessa di un driver esistente, esiste `Connection::resolverFor()`. Al callback **può essere passata una Closure che crea il PDO in modo lazy**, anziché un PDO già connesso.

Tuttavia, questa registrazione viene conservata in una proprietà statica della classe Connection. Registrandola per `sqlite`, influisce su tutte le nuove connessioni SQLite create dalla Factory standard, incluso l'esempio `acme-sqlite` qui sopra. Non può essere trattata come la sostituzione di un singolo nome di connessione.

* Nel callback restituisci una sottoclasse di Connection per il DB interessato. Con la sola Connection di base non si conservano Grammar, Processor e altri elementi specifici del DB.
* La Factory separa `createConnector()` e `createConnection()`. La sola registrazione con `resolverFor()` non fornisce anche un connettore PDO per driver sconosciuti.
* Se il resolver personalizzato di `DB::extend()` non usa la Factory standard, non passa nemmeno per il controllo di `resolverFor()` all'interno della Factory.
* Una nuova registrazione per lo stesso driver la sovrascrive. Verifica i conflitti con altri pacchetti e il trascinamento tra test nello stesso processo PHP.

I pacchetti che vogliono limitare l'impatto dovrebbero valutare prima `DB::extend()` con un nome di connessione o di driver specifico.

## Distinguere reconnect e purge

Il DatabaseManager conserva le Connection create per nome della connessione. Anche modificando la configurazione o il resolver, le modifiche non si riflettono automaticamente sulle connessioni già create.

| Operazione | Effetto sulla Connection conservata |
| - | - |
| `DB::extend()` | Registra o sovrascrive il processo di creazione successivo. L'oggetto esistente resta invariato |
| `DB::disconnect($name)` | Disconnette il PDO, ma conserva la Connection |
| `DB::reconnect($name)` | Se conservata, trasferisce il PDO e simili da una connessione appena creata. Restituisce la Connection originale |
| `DB::purge($name)` | Disconnette e rimuove dagli oggetti conservati dal Manager. Viene ricreata al successivo `connection()` |
| `DB::forgetExtension($key)` | Elimina la registrazione del resolver. Non elimina la configurazione né le connessioni create |

In particolare, `reconnect()` non è un'API che sostituisce integralmente la classe o l'array di configurazione della Connection conservata. Per riflettere modifiche alla classe di estensione o alla configurazione della connessione, esegui `purge()` in un momento sicuro senza transazioni in corso, quindi ottieni una nuova connessione.

> [!WARNING]
>
> `purge()` non sostituisce con quella nuova i riferimenti alla Connection conservati da altri servizi o variabili. Inoltre, `::read`, `::write` e `::direct` nel nome della connessione sono chiavi di conservazione separate: non dare per scontato che eseguire il purge del nome base le elimini tutte insieme.
>
> Non riscrivere con leggerezza la configurazione di una connessione condivisa in esecuzione, ad esempio per cambiare tenant. Nei worker di lunga durata, verifica non solo l'aggiornamento della cache di configurazione, ma anche il riavvio dei processi e l'impatto sui servizi conservati.

## Testare la registrazione e la creazione nel pacchetto

Con [Orchestra Testbench](https://kawax.biz/it/advanced/package-testing.md) carica il provider e seleziona effettivamente il driver personalizzato. Di seguito un esempio di `tests/DatabaseBridgeTest.php`. L'ambiente di esecuzione richiede PDO e il 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);
    }
}
```

Con `:memory:` il contenuto va perso alla disconnessione, quindi questo test non si aspetta che tabelle o dati vengano conservati. In un pacchetto reale, aggiungi anche:

* Che il resolver del nome della connessione abbia la precedenza su quello del nome del driver.
* Che una nuova registrazione con lo stesso nome e `forgetExtension()` non sostituiscano le Connection già create.
* L'eccezione quando manca una configurazione obbligatoria e il comportamento in caso di connessione fallita.
* Le funzionalità che ti impegni a fornire, come migrazioni, transazioni e separazione read/write sui DB supportati.
* Che registrazioni e stati delle connessioni non si mescolino con worker di lunga durata o con l'uso insieme ad altri pacchetti.

## Punti da verificare durante la manutenzione

* Si attiva tramite un nome personalizzato (opt-in) senza sovrascrivere silenziosamente la connessione predefinita o i driver integrati?
* Evita di inserire Closure o oggetti di connessione nella cache della configurazione, collocando il processo di creazione nel provider?
* Destinazioni di connessione e credenziali sono gestite come configurazione affidabile dell'applicazione?
* Hai ricontrollato, sulle versioni di Laravel supportate, gli argomenti di `extend()`, l'ordine di creazione della Factory e gli elementi trasferiti da `reconnect()`?
* Indichi agli utenti la procedura di ricreazione dopo le modifiche alla configurazione e il riavvio dei worker?

## Pagine correlate

* [Sviluppo di pacchetti](https://kawax.biz/it/advanced/package-development.md)
* [Progettare il ciclo di vita dei servizi nei pacchetti](https://kawax.biz/it/advanced/package-service-lifetimes.md)
* [Unione e cache della configurazione dei pacchetti](https://kawax.biz/it/advanced/package-config-merging.md)
* [Pubblicazione e aggiornamento delle migrazioni dei pacchetti](https://kawax.biz/it/advanced/package-migrations.md)

## Fonti primarie consultate

* [Documentazione ufficiale di Laravel: database e connessioni multiple](https://github.com/laravel/docs/blob/13.x/database.md#using-multiple-database-connections)
* [Documentazione ufficiale di Laravel: service provider dei pacchetti](https://github.com/laravel/docs/blob/13.x/packages.md#service-providers)
* [DatabaseManager: ordine di creazione, registrazione delle estensioni, conservazione e riconnessione delle connessioni](https://github.com/laravel/framework/blob/v13.40.0/src/Illuminate/Database/DatabaseManager.php)
* [ConnectionFactory: creazione lazy del PDO e resolver della Connection](https://github.com/laravel/framework/blob/v13.40.0/src/Illuminate/Database/Connectors/ConnectionFactory.php)
* [Connection: resolverFor e getResolver](https://github.com/laravel/framework/blob/v13.40.0/src/Illuminate/Database/Connection.php)
* [DatabaseServiceProvider: registrazione di db e db.factory](https://github.com/laravel/framework/blob/v13.40.0/src/Illuminate/Database/DatabaseServiceProvider.php)


## Related topics

- [Estendere e risolvere di nuovo i channel di log nei pacchetti](https://kawax.biz/it/advanced/package-logging.md)
- [Registrazione e test dei transport di posta nei pacchetti](https://kawax.biz/it/advanced/package-mail-transports.md)
- [Driver di sessione personalizzati nei pacchetti](https://kawax.biz/it/advanced/package-session-drivers.md)
- [Hook di risoluzione dei servizi nei pacchetti](https://kawax.biz/it/advanced/package-service-resolution.md)
- [Client HTTP e test nei pacchetti](https://kawax.biz/it/advanced/package-http-client.md)


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