v13.40.0 官方源码的确认。连接配置的基础请参阅数据库配置,服务提供者的加载请参阅包的自动发现。
选择扩展点
数据库的创建处理中,存在适用范围不同的扩展点。
传给
DB::extend() 的回调的返回值不是 PDO 或 Query Builder,而是 Illuminate\Database\Connection。DatabaseManager::configure() 要求这个类。
连接名称的扩展优先于驱动名称
DatabaseManager::makeConnection() 在获取配置后,按以下顺序选择创建方式。
DB::extend('reporting', ...)适用于database.connections.reporting。DB::extend('mysql', ...)适用于连接名称一侧没有扩展、且driver为mysql的连接。- 两者使用同一个扩展注册表。考虑到连接名称与驱动名称的冲突,应使用包专属的名称。
extend()不会创建配置数组。仅注册扩展而不存在指定名称的连接配置时,会抛出异常。
显式选择自定义驱动名称
下面是将自定义名称acme-sqlite 桥接到标准 SQLite 连接创建的最小示例。它并非实现新的数据库协议或 SQL 方言的示例。它展示的是分发自定义适配器时,注册与选择之间的边界。
假设 Acme\DatabaseBridge\ 通过 Composer 的 PSR-4 映射到 src/,放置在 src/DatabaseBridgeServiceProvider.php 中。
db.factory 是 Laravel 共享的 ConnectionFactory 绑定。由于直接调用标准 Factory,不会回到 DatabaseManager 的扩展选择,从而可以复用 SQLite 的延迟 PDO 创建与 Connection 创建。
此回调在首次解析连接时执行。服务提供者的 boot() 只负责注册创建方式,不执行连接测试或迁移。应作为常规的非延迟服务提供者,在目标连接首次被解析之前加载。
使用方应用在 config/database.php 现有的 connections 数组中添加连接。不要修改默认的 default。
acme_reports,而不是驱动名称 acme-sqlite。示例中在传给 Factory 之前将 driver 转换为 sqlite,因此创建后的 getDriverName() 也是 sqlite。
替换为 Connection 子类属于另一个范围
如果要替换现有驱动的 Connection 类本身,可以使用Connection::resolverFor()。传给回调的有时并非立即连接的 PDO,而是延迟创建 PDO 的 Closure。
不过,此注册保存在 Connection 类的 static 属性中。如果为 sqlite 注册,会影响通过标准 Factory 新创建的所有 SQLite 连接,上面的 acme-sqlite 示例也在其中。它不能被视为只针对某一个连接名称的替换。
- 回调中应返回目标数据库专用的 Connection 子类。仅使用基础 Connection 无法保留数据库专有的 Grammar 和 Processor 等。
- Factory 将
createConnector()与createConnection()分开。仅注册resolverFor()并不会为未知驱动提供 PDO 连接器。 - 如果
DB::extend()的自定义 resolver 不使用标准 Factory,也不会经过 Factory 内对resolverFor()的检查。 - 对同一驱动重复注册会覆盖原有注册。请确认与其他包的冲突,以及在同一 PHP 进程内的测试之间的残留。
DB::extend()。
区分 reconnect 与 purge
DatabaseManager 按连接名称保留已创建的 Connection。即使修改配置或 resolver,也不会自动反映到已经创建的连接。
特别是
reconnect(),它并不是整体替换已保留 Connection 的类或配置数组的 API。如果要反映扩展类或连接配置的变更,请在没有进行中事务的安全时机执行 purge(),然后获取新的连接。
测试包的注册与创建
在 Orchestra Testbench 中加载服务提供者,并实际选择自定义驱动。下面是tests/DatabaseBridgeTest.php 的示例。运行环境需要 PDO 和 SQLite 驱动。
:memory: 在连接断开时会丢失内容,因此此测试不期望保留表或数据。在实际的包中,还应添加以下测试。
- 连接名称的 resolver 优先于驱动名称的 resolver。
- 同名重复注册和
forgetExtension()不会替换已创建的 Connection。 - 缺少必需配置时的异常,以及连接失败时的行为。
- 在支持的数据库上的迁移、事务、读写分离等承诺提供的功能。
- 在长生命周期 worker 或与多个包并用时,注册和连接状态不会混杂。
维护时的检查项目
- 是否通过自定义名称选择启用,而没有悄悄覆盖默认连接或内置驱动。
- 是否没有将 Closure 或连接对象放入配置缓存,而是将创建处理放在服务提供者中。
- 是否将连接目标和认证信息作为可信的应用配置进行管理。
- 是否在受支持的 Laravel 版本中重新确认了
extend()的参数、Factory 的创建顺序以及reconnect()的迁移对象。 - 是否向使用者说明了配置变更后的重新创建步骤与 worker 重启。