数据库驱动
merql 将连接传输层与数据库方言分离。DatabaseConnection 适配器通过 PDO 或 mysqli 执行 SQL,而 Driver 则处理特定数据库引擎的元数据 SQL 和标识符引号处理。内置驱动支持 MySQL 和 SQLite。
use Merql\Driver\DriverFactory;
$driver = DriverFactory::create($connection);
// Auto-detects MySQL or SQLite from the connection driver identity.
Driver 接口
每个驱动都实现五个方法:
use Merql\Driver\Driver;
interface Driver
{
public function quoteIdentifier(string $name): string;
public function listTables(DatabaseConnection $connection): array;
public function readSchema(DatabaseConnection $connection, string $table): TableSchema;
public function readForeignKeys(DatabaseConnection $connection): array;
public function selectAll(string $table): string;
}
| 方法 | 用途 |
|---|---|
quoteIdentifier |
为 SQL 中使用的表名或列名加引号。 |
listTables |
返回当前数据库中的所有用户表。 |
readSchema |
读取表的列名、类型、主键和唯一键。 |
readForeignKeys |
返回子表到父表依赖关系的映射。 |
selectAll |
为表构建 SELECT * 查询。 |
MysqlDriver
使用 INFORMATION_SCHEMA 查询读取元数据,并使用反引号为标识符加引号。
use Merql\Driver\MysqlDriver;
$driver = new MysqlDriver();
$driver->quoteIdentifier('posts');
// `posts`
$driver->quoteIdentifier('my`table');
// `my``table`
$tables = $driver->listTables($connection);
// ['comments', 'posts', 'users']
$schema = $driver->readSchema($connection, 'posts');
// TableSchema with columns, primaryKey, uniqueKeys
$fks = $driver->readForeignKeys($connection);
// ['comments' => ['posts', 'users']]
表列表查询使用 INFORMATION_SCHEMA.TABLES,并按当前数据库和 BASE TABLE 类型筛选。架构读取查询使用 INFORMATION_SCHEMA.COLUMNS 获取列信息,使用 INFORMATION_SCHEMA.KEY_COLUMN_USAGE 获取主键和唯一键信息。
SqliteDriver
使用 PRAGMA 查询读取元数据,并使用双引号为标识符加引号。
use Merql\Driver\SqliteDriver;
$driver = new SqliteDriver();
$driver->quoteIdentifier('posts');
// "posts"
$driver->quoteIdentifier('my"table');
// "my""table"
$tables = $driver->listTables($connection);
// Queries sqlite_master, excludes sqlite_* internal tables.
$schema = $driver->readSchema($connection, 'posts');
// Uses PRAGMA table_info() and PRAGMA index_list().
$fks = $driver->readForeignKeys($connection);
// Uses PRAGMA foreign_key_list() for each table.
DriverFactory
DriverFactory 会根据 DatabaseConnection::driverName() 自动检测正确的驱动。
use Merql\Connection;
use Merql\Driver\DriverFactory;
$connection = Connection::mysql('127.0.0.1', 'mydb', 'root', '');
$driver = DriverFactory::create($connection);
// Returns MysqlDriver
$connection = Connection::sqlite(':memory:');
$driver = DriverFactory::create($connection);
// Returns SqliteDriver
如果无法识别连接的驱动标识,则会抛出消息为 Unsupported database driver: <name> 的 RuntimeException。merql 未提供 PostgreSQL 驱动。
注册自定义驱动
对于 MySQL 和 SQLite 以外的数据库,请编写一个实现 Driver 的类并注册它。下方的 PostgresDriver 代表你自己的类:
use Merql\Driver\DriverFactory;
DriverFactory::register('pgsql', PostgresDriver::class);
$connection = Connection::fromDsn('pgsql:host=localhost;dbname=mydb', 'user', 'pass');
$driver = DriverFactory::create($connection);
// Returns your PostgresDriver instance.
驱动类必须实现 Driver 接口。register() 的第一个参数是连接的驱动标识。
重置
测试时,将工厂重置为默认状态:
DriverFactory::reset();
// Reverts to only MySQL and SQLite drivers.
连接辅助类
Connection 类使用合理的默认设置来构建适配器。PDO 适配器在出错时会抛出异常,使用关联数组提取模式、真正的预处理语句和字符串化提取结果。mysqli 适配器会设置 utf8mb4,使用 mysqli 异常模式和预处理语句,并将提取的值规范化为相同的字符串或 null 格式。
use Merql\Connection;
// MySQL.
$connection = Connection::mysql('127.0.0.1', 'my_database', 'root', 'password');
$connection = Connection::mysql('db.example.com', 'mydb', 'user', 'pass', port: 3307);
// MySQLi.
$connection = Connection::mysqli('127.0.0.1', 'my_database', 'root', 'password');
// SQLite.
$connection = Connection::sqlite(':memory:');
$connection = Connection::sqlite('/var/data/app.sqlite');
// Any MySQL or SQLite PDO DSN.
$connection = Connection::fromDsn('mysql:host=127.0.0.1;dbname=mydb;charset=utf8mb4', 'user', 'pass');
fromDsn() 接受 PDO 可打开的任意 DSN,但 merql 只能通过已注册的驱动创建快照并应用变更。pgsql: 连接可以成功打开(前提是已安装 pdo_pgsql),但 Merql::init()、Snapshotter、Applier 和 GuardedApplier 构造函数都会调用 DriverFactory::create();除非你为它注册了驱动或传入一个驱动,否则会抛出 RuntimeException: Unsupported database driver: pgsql。
Connection::sqlite() 还会自动启用 PRAGMA foreign_keys = ON,SQLite 需要它来实施外键约束。
驱动的使用位置
驱动在 merql 中用途广泛:
- Snapshotter 使用
listTables、readSchema和selectAll捕获数据库状态。 - Applier 使用
readForeignKeys确定操作顺序,并将驱动传递给SqlGenerator以处理标识符引号。 - SqlGenerator 使用
quoteIdentifier构建安全的 SQL。
如果将连接传递给 Snapshotter 或 Applier 时未指定驱动,则会自动调用 DriverFactory::create()。