跳转到正文

数据库驱动

merql 将连接传输层与数据库方言分离。DatabaseConnection 适配器通过 PDO 或 mysqli 执行 SQL,而 Driver 则处理特定数据库引擎的元数据 SQL 和标识符引号处理。内置驱动支持 MySQL 和 SQLite。

PHP
use Merql\Driver\DriverFactory;

$driver = DriverFactory::create($connection);
// Auto-detects MySQL or SQLite from the connection driver identity.

Driver 接口

每个驱动都实现五个方法:

PHP
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 查询读取元数据,并使用反引号为标识符加引号。

PHP
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 查询读取元数据,并使用双引号为标识符加引号。

PHP
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() 自动检测正确的驱动。

PHP
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 代表你自己的类:

PHP
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() 的第一个参数是连接的驱动标识。

重置

测试时,将工厂重置为默认状态:

PHP
DriverFactory::reset();
// Reverts to only MySQL and SQLite drivers.

连接辅助类

Connection 类使用合理的默认设置来构建适配器。PDO 适配器在出错时会抛出异常,使用关联数组提取模式、真正的预处理语句和字符串化提取结果。mysqli 适配器会设置 utf8mb4,使用 mysqli 异常模式和预处理语句,并将提取的值规范化为相同的字符串或 null 格式。

PHP
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()。