activecollab / databaseconnection
封装了MySQLi连接的一些简单辅助方法
Requires
- php: >=8.0
- ext-json: *
- ext-mbstring: *
- ext-mysqli: *
- activecollab/containeraccess: ^2.0
- activecollab/datevalue: ^2.0
- psr/log: ^1.0.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^2.0
- phpunit/phpunit: ~9.0
- pimple/pimple: ~3.5.0
README
该库的目的不是抽象数据库,而是使使用MySQLi连接变得更容易。功能
- 可轻松迭代的查询结果
- 结果可以是行数组、对象,或通过已知的类名或从行字段中读取的类名加载的对象
- 根据字段名自动转换值
为什么还需要另一个数据库抽象层?关注点和历史。这个库已经作为Active Collab的一部分存在多年,因此工作得非常好。另一方面,它很简单 - 只与MySQL一起使用,可以在一个小时之内阅读和理解,同时还能为您节省大量时间。
获取数据
此库使查询执行快速且简单。您可以获取所有记录、仅第一条记录、仅第一列或仅第一个单元格(第一条记录的第一列)。以下是一些示例
<?php use ActiveCollab\DatabaseConnection\Connection\MysqliConnection; use MySQLi; use RuntimeException; $database_link = new MySQLi('localhost', 'root', '', 'activecollab_database_connection_test'); if ($database_link->connect_error) { throw new RuntimeException('Failed to connect to database. MySQL said: ' . $database_link->connect_error); } $connection = new MysqliConnection($database_link); // List all writers foreach ($connection->execute('SELECT `id`, `name` FROM `writers` WHERE `name` = ? ORDER BY `id`', 'Leo Tolstoy') as $row) { print '#' . $row['id'] . ' ' . $row['name'] . "\n"; } // Get the first cell of the first row (so we can print Tolstoy's birthday) print $connection->executeFirstCell('SELECT `birthday` FROM `writers` WHERE `name` = ? LIMIT 0, 1', 'Leo Tolstoy'); // Get everything that we have on Leo Tolstoy print_r($connection->executeFirstRow('SELECT * FROM `writers` WHERE `name` = ?', 'Leo Tolstoy')); // Show names of all authors print_r($connection->executeFirstColumn('SELECT `name` FROM `writers` ORDER BY `name`'));
选择记录
要从参数运行SELECT查询,而不是编写自己的SELECT查询,请使用以下方法
select()
- 映射到execute()
selectFirstRow()
- 映射到executeFirstRow()
selectFirstColumn()
- 映射到executeFirstColumn()
selectFirstCell()
- 映射到executeFirstCell()
所有方法都接受以下参数
$table_name
- 表的名称。这是唯一必需的参数$fields
- 需要获取的字段列表。提供字符串(单个字段)、字段数组或NULL
(所有字段)$conditions
- 查询条件。提供字符串、数组(模式 + 参数)或NULL
$order_by_fields
- 我们想要按其排序的记录的字段列表。提供字符串(单个字段)、字段数组或NULL
(所有字段)
$writers = $connection->select('writers', ['id', 'name'], ['birthday >= ?', '1821-11-11'], 'name');
记录计数
DatabaseConnection允许您轻松地从表中计数记录
$num = $connection->count('writers');
默认情况下,它返回表中所有记录的数量。要过滤,您可以提供 $conditions
参数
$num = $connection->count('writers', "`name` = 'Leo Tolstoy'"); $num = $this->connection->count('writers', ['name = ?', 'Leo Tolstoy']);
count()
方法假设表中存在 id
主键,因此它准备查询为 COUNT(id)
。我们计数的列名也可以更改(甚至可以是 *
)
$num = $this->connection->count('writers', null, '*'); $num = $this->connection->count('writers', null, 'name')
从文件运行查询
要从文件运行所有查询,请使用 executeFromFile()
方法
$connection->executeFromFile('/path/to/file');
注意: 此方法尚未实现以处理大型数据转储。请使用 mysqldump
或其他专门的备份工具代替。
连接工厂
最快的连接方式是使用 ConnectionFactory
类
<?php use ActiveCollab\DatabaseConnection\Connection\MysqliConnection; use ActiveCollab\DatabaseConnection\ConnectionFactory; $connection = (new ConnectionFactory())->mysqli('localhost', 'root', '', 'activecollab_database_connection_test', 'utf8mb4'); if ($connection instanceof MysqliConnection) { print_r($connection->getTableNames()); }
前三个参数是必需的(MySQL主机名、用户名和密码)。此外,此函数还接受
- 需要选择的数据库的名称。如果没有指定,则不会选择数据库
- 我们想要强制执行的连接编码。如果没有指定,则使用默认的连接编码
批量插入
批量插入实用工具帮助您准备和插入大量相同类型的行。示例
// Insert 3 rows per INSERT query in `writers` table $batch_insert = $connection->batchInsert('writers', ['name', 'birthday'], 3); $batch_insert->insert('Leo Tolstoy', new DateTime('1828-09-09')); // No insert $batch_insert->insert('Alexander Pushkin', new DateTime('1799-06-06')); // No insert $batch_insert->insert('Fyodor Dostoyevsky', new DateTime('1821-11-11')); // Insert $batch_insert->insert('Anton Chekhov', new DateTime('1860-01-29')); // No insert $batch_insert->done(); // Insert remaining rows and close the batch insert
注意: 批量完成后调用 insert
、insertEscaped
、flush
或 done
方法将抛出 RuntimeException
。
批量插入还可以用于替换记录(使用 REPLACE INTO
而不是 INSERT INTO 查询)
$batch_replace = $this->connection->batchInsert('writers', ['name', 'birthday'], 3, ConnectionInterface::REPLACE);
转换
除非指定不同,以下约定适用
id
和row_count
字段始终转换为整数- 以
_id
结尾的名称的字段将被转换为整数, - 以
is_
、has_
、had_
、was_
、were_
和have_
开头的名称的字段将被转换为布尔值, - 以
_at
或_on
结尾的名称的字段将被转换为DateValue。
空间数据
库支持OpenGIS定义的空间数据类型。支持的类型
- 点
- 多点
- 线字符串
- 多线字符串
- 线性环
- 多边形
- 多重多边形
- 几何集合
每个支持的类型都定义为一个单独的值对象,在\\ActiveCollab\DatabaseConnection\Spatial
命名空间下。要指定哪些字段应被视为空间字段,请指导值转换器使用\\ActiveCollab\DatabaseConnection\Record\ValueCasterInterface::CAST_SPATIAL
。示例
$rows = $this->connection->execute('SELECT ST_AsText(`geometry`) AS "geometry" FROM `example`'); $rows->setValueCaster( new ValueCaster( [ 'geometry' => ValueCasterInterface::CAST_SPATIAL, ] ) );
对象初始化
此库使对象初始化变得快速简单。要初始化对象,您需要一个实现\ActiveCollab\DatabaseConnection\Record\LoadFromRow
接口的类,例如
<?php use ActiveCollab\DatabaseConnection\Record\LoadFromRow; use DateTime; class Writer implements LoadFromRow { /** * @var array */ private $row; /** * @param array $row */ public function loadFromRow(array $row) { $this->row = $row; } /** * @return integer */ public function getId() { return $this->row['id']; } /** * @return string */ public function getName() { return $this->row['name']; } /** * @return DateTime */ public function getBirthday() { return new DateTime($this->row['birthday']); } }
您可以通过传递类的名称到advancedExecute()
方法来获取初始化对象的列表
<?php use ActiveCollab\DatabaseConnection\Connection\MysqliConnection; use DateTime; use MySQLi; use RuntimeException; $database_link = new MySQLi('localhost', 'root', '', 'activecollab_database_connection_test'); if ($database_link->connect_error) { throw new RuntimeException('Failed to connect to database. MySQL said: ' . $database_link->connect_error); } $connection = new MysqliConnection($database_link); foreach ($this->connection->advancedExecute('SELECT * FROM `writers` ORDER BY `id`', null, ConnectionInterface::LOAD_ALL_ROWS, ConnectionInterface::RETURN_OBJECT_BY_CLASS, '\ActiveCollab\DatabaseConnection\Test\Fixture\Writer') as $writer) { print '#' . $writer->getId() . ' ' . $writer->getName() . ' (' . $writer->getBirthday()->format('Y-m-d') . ')'; }
如果同一表中存储了多种类型的对象,您可以告诉advancedExecute()
方法在何处查找类名。此示例假定我们将完整的类名存储在type
字段中
<?php use ActiveCollab\DatabaseConnection\Connection\MysqliConnection; use DateTime; use MySQLi; use RuntimeException; $database_link = new MySQLi('localhost', 'root', '', 'activecollab_database_connection_test'); if ($database_link->connect_error) { throw new RuntimeException('Failed to connect to database. MySQL said: ' . $database_link->connect_error); } $connection = new MysqliConnection($database_link); foreach ($this->connection->advancedExecute('SELECT * FROM `writers` ORDER BY `id`', null, ConnectionInterface::LOAD_ALL_ROWS, ConnectionInterface::RETURN_OBJECT_BY_FIELD, 'type') as $writer) { print '#' . $writer->getId() . ' ' . $writer->getName() . ' (' . $writer->getBirthday()->format('Y-m-d') . ')'; }
对象初始化 + DIC
如果您初始化的对象实现了ActiveCollab\ContainerAccess\ContainerAccessInterface
接口,并且您将一个Interop\Container\ContainerInterface
实例传递给advancedExecute()
方法,则容器将提供给所有初始化的对象
use ActiveCollab\DatabaseConnection\Test\Fixture\Container; use ActiveCollab\DatabaseConnection\Test\Fixture\WriterWithContainer; use Psr\Container\ContainerInterface; $container = new Container([ 'dependency' => 'it works!' ]); $result = $this->connection->advancedExecute('SELECT * FROM `writers` ORDER BY `id`', null, ConnectionInterface::LOAD_ALL_ROWS, ConnectionInterface::RETURN_OBJECT_BY_CLASS, WriterWithContainer::class, null, $container); foreach ($result as $writer) { print $writer->dependency . "\n"; // Prints 'it works!' }
辅助方法
数据库连接实例提供了一些有助于日常开发的方法
databaseExists($database_name)
- 如果具有给定名称的数据库存在,则返回TRUE
,userExists($user_name)
- 如果具有给定名称的用户存在,则返回TRUE
。getTableNames()
- 返回我们连接到的数据库中所有表的名字,tableExists($table_name)
- 如果我们连接到的数据库中存在具有给定名称的表,则返回TRUE
,dropTable($table_name)
- 删除一个表。
测试
要测试库,您需要手动创建一个数据库
CREATE DATABASE activecollab_database_connection_test DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_general_ci;
然后从项目根目录执行以下命令
phpunit
待办事项
- 对于所有具有额外参数的查询使用预定义语句,
- 启用库使用两个数据库连接,一个用于写入,另一个用于读取,
- 正确处理MySQL已离线错误和死锁(模拟的)。
- 考虑支持MySQL Native Driver及其所启用的功能。