Skip to main content
To extend database connections from a package, separate registering how a connection is created from choosing which connection to use. Design your package so that installing it alone does not replace the application’s default connection or every MySQL connection. This page is based on the official source of Laravel Framework 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.
The database extend() does not follow the same calling convention as the shared Illuminate\Support\Manager. The callback’s first argument is the connection configuration array, not the application container. Do not copy examples from other driver extensions as-is.

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 to database.connections.reporting.
  • DB::extend('mysql', ...) applies to connections whose driver is mysql and 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 name acme-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.
Prepare the SQLite file in advance and confirm write permissions before using it. Do not design the package to create the file just by being loaded.
What the user passes is the connection name 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.
Calling DB::connection($name) again from inside the resolver goes back into creating the same connection, which is not yet retained. To avoid recursion, create the Connection with the factory or your own adapter.

Replacing with a Connection subclass is a different scope

To replace the Connection class itself for an existing driver, use Connection::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() and createConnection(). Registering with resolverFor() alone does not provide a PDO connector for an unknown driver.
  • If your custom DB::extend() resolver does not use the standard factory, the resolverFor() 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.
Packages that want to limit their impact should first consider 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.
purge() does not swap Connection references held by other services or variables for the new one. Also, the ::read, ::write, and ::direct connection names are separate retention keys, so do not assume that purging the base name removes them all at once.Do not casually rewrite shared connection configuration at runtime, for example to switch tenants. For long-lived workers, check not only updating the configuration cache but also restarting the process and the impact on retained services.

Test package registration and creation

With Orchestra Testbench, load the provider and actually select the custom driver. The following is an example of tests/DatabaseBridgeTest.php. The test environment requires PDO and the SQLite driver.
Because :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 what reconnect() transfers on the supported Laravel versions?
  • Do you guide users through the recreation steps and worker restarts after configuration changes?

Related pages

Primary sources

Last modified on November 3, 2026