activecollab/databaseconnection

封装了MySQLi连接的一些简单辅助方法

5.1.7 2024-05-12 18:13 UTC

This package is auto-updated.

Last update: 2024-09-12 18:55:18 UTC


README

Build Status

该库的目的不是抽象数据库,而是使使用MySQLi连接变得更容易。功能

  1. 可轻松迭代的查询结果
  2. 结果可以是行数组、对象,或通过已知的类名或从行字段中读取的类名加载的对象
  3. 根据字段名自动转换值

为什么还需要另一个数据库抽象层?关注点和历史。这个库已经作为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查询,请使用以下方法

  1. select() - 映射到 execute()
  2. selectFirstRow() - 映射到 executeFirstRow()
  3. selectFirstColumn() - 映射到 executeFirstColumn()
  4. selectFirstCell() - 映射到 executeFirstCell()

所有方法都接受以下参数

  1. $table_name - 表的名称。这是唯一必需的参数
  2. $fields - 需要获取的字段列表。提供字符串(单个字段)、字段数组或 NULL(所有字段)
  3. $conditions - 查询条件。提供字符串、数组(模式 + 参数)或 NULL
  4. $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主机名、用户名和密码)。此外,此函数还接受

  1. 需要选择的数据库的名称。如果没有指定,则不会选择数据库
  2. 我们想要强制执行的连接编码。如果没有指定,则使用默认的连接编码

批量插入

批量插入实用工具帮助您准备和插入大量相同类型的行。示例

// 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

注意: 批量完成后调用 insertinsertEscapedflushdone 方法将抛出 RuntimeException

批量插入还可以用于替换记录(使用 REPLACE INTO 而不是 INSERT INTO 查询)

$batch_replace = $this->connection->batchInsert('writers', ['name', 'birthday'], 3, ConnectionInterface::REPLACE);

转换

除非指定不同,以下约定适用

  1. idrow_count 字段始终转换为整数
  2. _id结尾的名称的字段将被转换为整数,
  3. is_has_had_was_were_have_开头的名称的字段将被转换为布尔值,
  4. _at_on结尾的名称的字段将被转换为DateValue。

空间数据

库支持OpenGIS定义的空间数据类型。支持的类型

  1. 多点
  2. 线字符串
  3. 多线字符串
  4. 线性环
  5. 多边形
  6. 多重多边形
  7. 几何集合

每个支持的类型都定义为一个单独的值对象,在\\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!'
}

辅助方法

数据库连接实例提供了一些有助于日常开发的方法

  1. databaseExists($database_name) - 如果具有给定名称的数据库存在,则返回TRUE
  2. userExists($user_name) - 如果具有给定名称的用户存在,则返回TRUE
  3. getTableNames() - 返回我们连接到的数据库中所有表的名字,
  4. tableExists($table_name) - 如果我们连接到的数据库中存在具有给定名称的表,则返回TRUE
  5. dropTable($table_name) - 删除一个表。

测试

要测试库,您需要手动创建一个数据库

CREATE DATABASE activecollab_database_connection_test DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_general_ci;

然后从项目根目录执行以下命令

phpunit

待办事项

  1. 对于所有具有额外参数的查询使用预定义语句,
  2. 启用库使用两个数据库连接,一个用于写入,另一个用于读取,
  3. 正确处理MySQL已离线错误和死锁(模拟的)。
  4. 考虑支持MySQL Native Driver及其所启用的功能。