v13.40.0. For connection configuration basics, see Database configuration. For provider loading, see Package discovery.
Choose an extension point
Database connection creation offers extension points with different scopes.
The callback passed to
DB::extend() returns an Illuminate\Database\Connection, not a PDO instance or a Query Builder. DatabaseManager::configure() requires this class.
Connection name extensions take precedence over driver names
DatabaseManager::makeConnection() retrieves the configuration and then chooses how to create the connection in the following order.
DB::extend('reporting', ...)applies todatabase.connections.reporting.DB::extend('mysql', ...)applies to connections whosedriverismysqland that have no connection name extension.- Both use the same extension registry. Use package-specific names, keeping collisions between connection names and driver names in mind.
extend()does not create a configuration array. If you only register an extension and no connection configuration exists for the given name, an exception is thrown.
Explicitly select a custom driver name
The following is a minimal example that bridges the custom nameacme-sqlite to standard SQLite connection creation. It is not an example of implementing a new database protocol or SQL dialect. It shows the boundary between registration and selection when distributing a custom adapter.
It assumes Acme\DatabaseBridge\ maps to src/ via Composer PSR-4, and the class is placed in src/DatabaseBridgeServiceProvider.php.
db.factory is the binding for the ConnectionFactory shared by Laravel. Because it calls the standard factory directly, it does not go back through the DatabaseManager’s extension selection and can reuse SQLite’s lazy PDO creation and Connection creation.
This callback runs when the connection is resolved for the first time. The provider’s boot() only registers the creation logic; it does not test the connection or run migrations. Load it as a normal, non-deferred provider so it is registered before the target connection is first resolved.
The consuming application adds the connection to the existing connections array in config/database.php. It does not change the default.
acme_reports, not the driver name acme-sqlite. Because the example converts driver to sqlite before passing it to the factory, getDriverName() on the created connection also returns sqlite.
Replacing with a Connection subclass is a different scope
To replace the Connection class itself for an existing driver, useConnection::resolverFor(). The callback may receive a Closure that lazily creates the PDO rather than an already connected PDO.
However, this registration is stored in a static property of the Connection class. If you register it for sqlite, it affects every SQLite connection newly created by the standard factory, including the acme-sqlite example above. It cannot be treated as a replacement for a single connection name.
- Return a Connection subclass for the target database from the callback. The base Connection alone cannot preserve database-specific Grammar, Processor, and so on.
- The factory separates
createConnector()andcreateConnection(). Registering withresolverFor()alone does not provide a PDO connector for an unknown driver. - If your custom
DB::extend()resolver does not use the standard factory, theresolverFor()check inside the factory is never reached. - Registering again for the same driver overwrites the previous registration. Check for conflicts with other packages and for state carried over between tests in the same PHP process.
DB::extend() with a package-specific connection name or driver name.
Distinguish reconnect from purge
The DatabaseManager retains created Connections per connection name. Changing the configuration or resolver is not automatically reflected in connections that have already been created.
In particular,
reconnect() is not an API that wholesale replaces the class or configuration array of a retained Connection. To apply changes to an extension class or connection configuration, purge() at a safe time when no transaction is in progress, then get a new connection.
Test package registration and creation
With Orchestra Testbench, load the provider and actually select the custom driver. The following is an example oftests/DatabaseBridgeTest.php. The test environment requires PDO and the SQLite driver.
:memory: loses its contents when the connection is closed, this test does not expect tables or data to persist. In a real package, also add tests for the following:
- The connection name resolver takes precedence over the driver name resolver.
- Re-registering the same name and
forgetExtension()do not replace created Connections. - Exceptions when required configuration is missing, and behavior when the connection fails.
- The features you promise to provide on supported databases, such as migrations, transactions, and read/write splitting.
- Registrations and connection state do not get mixed up in long-lived workers or when used alongside multiple packages.
Maintenance checklist
- Does the package opt in with a custom name, without silently overwriting the default connection or built-in drivers?
- Do you keep Closures and connection objects out of the config cache and place the creation logic in the provider?
- Are connection targets and credentials managed as trusted application configuration?
- Have you rechecked the
extend()arguments, the factory’s creation order, and whatreconnect()transfers on the supported Laravel versions? - Do you guide users through the recreation steps and worker restarts after configuration changes?
Related pages
- Package development
- Designing package service lifetimes
- Merging and caching package configuration
- Publishing and updating package migrations
Primary sources
- Laravel official: Database and multiple connections
- Laravel official: Package service providers
- DatabaseManager: Creation order, extension registration, connection retention and reconnection
- ConnectionFactory: Lazy PDO creation and the Connection resolver
- Connection: resolverFor and getResolver
- DatabaseServiceProvider: Registering db and db.factory