webfiori/database

WebFiori框架的数据库抽象层。


README

WebFiori框架的数据库抽象层。

PHP 8 Build Status CodeCov Quality Checks Version Downloads

内容

支持的PHP版本

支持的数据库

  • MySQL
  • MSSQL

功能

  • 在PHP中构建你的数据库结构。
  • 快速且易于使用的查询构建器。
  • 数据库抽象,使将系统迁移到不同的DBMS变得容易。

安装

要使用composer安装库,请将以下依赖项添加到composer.json"webfiori/database":"*"

用法

连接到数据库

连接到数据库很简单。第一步是使用类ConnectionInfo定义数据库连接信息。之后,可以使用类Database使用该实例建立到数据库的连接。

use webfiori\database\ConnectionInfo;
use webfiori\database\Database;

//This assumes that MySQL is installed on locahost
//and root password is set to '123456' 
//and there is a schema with name 'testing_db'
$connection = new ConnectionInfo('mysql', 'root', '123456', 'testing_db');
$database = new Database($connection);

运行基本的SQL查询

在任何关系型数据库管理系统中都会执行的最常见的SQL查询包括插入、选择、更新和删除。以下示例展示了如何构建这4种类型。

对于每个查询,必须指定将要执行查询的表。要指定表,请使用方法Database::table(string $tblName)。该方法将返回一个AbstractQuery类的实例。该类有许多方法,用于进一步构建查询。常用的方法包括以下:

  • AbstractQuery::insert(array $cols):构建插入查询。
  • AbstractQuery::select(array $cols):构建选择查询。
  • AbstractQuery::update(array $cols):构建更新查询。
  • AbstractQuery::delete():构建删除查询。
  • AbstractQuery::where($col, $val):向查询添加条件。

构建查询后,可以调用方法AbstractQuery::execute()来执行查询。如果查询是select查询,则该方法将返回一个ResultSet类的实例。该实例可以用来遍历由DBMS返回的记录。

插入查询

插入查询用于向数据库添加记录。要执行插入查询,请使用方法AbstractQuery::insert(array $cols)。该方法接受一个参数。该参数是一个关联数组。数组的索引是列名,索引的值是要插入的值。

$connection = new ConnectionInfo('mysql', 'root', '123456', 'testing_db');
$database = new Database($connection);

$database->table('posts')->insert([
    'title' => 'Super New Post',
    'author' => 'Me'
])->execute();

选择查询

选择查询用于检索数据库记录并在应用程序逻辑中使用它们。要执行选择查询,请使用方法AbstractQuery::select(array $cols)。该方法接受一个可选参数。该参数是一个数组,包含要选择的列名。在这种情况下,方法AbstractQuery::execute()将返回一个类型为ResultSet的对象。结果集将包含一个包含实际记录的大数组。每个记录都存储为一个关联数组。

$connection = new ConnectionInfo('mysql', 'root', '123456', 'testing_db');
$database = new Database($connection);

//This assumes that we have a table called 'posts' in the database.
$resultSet = $database->table('posts')->select()->execute();

foreach ($resultSet as $record) {
    echo $record['title'];
}

可以使用方法AbstractQuery::where()向选择查询添加条件。

$connection = new ConnectionInfo('mysql', 'root', '123456', 'testing_db');
$database = new Database($connection);

//This assumes that we have a table called 'posts' in the database.
$resultSet = $database->table('posts')
                      ->select()
                      ->where('author', 'Ibrahim')
                      ->execute();

foreach ($resultSet as $record) {
    echo $record['title'];
}

更新查询

更新查询用于更新单个记录或多个记录。要执行更新查询,可以使用方法 AbstractQuery::update(array $cols)。此方法接受一个参数,该参数是一个关联数组。数组的索引是列名,索引的值是更新的值。通常,对于任何更新查询,都会包含一个 where 条件。要包含一个 where 条件,可以使用方法 AbstractQuery::where()

$connection = new ConnectionInfo('mysql', 'root', '123456', 'testing_db');
$database = new Database($connection);

$database->table('posts')->update([
    'title' => 'Super New Post By ibrahim',
])->where('author', 'Ibrahim')
->andWhere('created-on', '2023-03-24')->execute();

删除查询

此查询用于从数据库中删除特定记录。要执行删除查询,可以使用方法 AbstractQuery::delete()。删除特定记录时应该包含一个 where 条件。要包含一个 where 条件,可以使用方法 AbstractQuery::where()

$connection = new ConnectionInfo('mysql', 'root', '123456', 'testing_db');
$database = new Database($connection);

$database->table('posts')->delete()->where('author', 'Ibrahim');

构建数据库结构

该库的一个特性是能够在源代码中定义数据库结构,并在之后将创建的结构种子到数据库中,以创建数据库表。表的蓝图由类 Table 表示。蓝图的主要目的是确保数据库中的数据类型在源代码中得到正确表示。

注意:如何构建结构的示例源代码可以在这里找到。

创建表蓝图

每个蓝图都必须定义以下属性

  • 蓝图名称(数据库表名称)。
  • 列及其属性,例如数据类型。
  • 与其他表的关系。

方法 Database::createBlueprint() 用于根据连接的 DBMS 创建表。该方法将返回一个 Table 类的实例,该实例可以用于进一步自定义蓝图。

$database->createBlueprint('users_information')->addColumns([
    'id' => [
        ColOption::TYPE => DataType::INT,
        ColOption::SIZE => 5,
        ColOption::PRIMARY => true,
        ColOption::AUTO_INCREMENT => true
    ],
    'first-name' => [
        ColOption::TYPE => DataType::VARCHAR,
        ColOption::SIZE => 15
    ],
    'last-name' => [
        ColOption::TYPE => DataType::VARCHAR,
        ColOption::SIZE => 15
    ],
    'email' => [
        ColOption::TYPE => DataType::VARCHAR,
        ColOption::SIZE => 128
    ]
]);

注意:可以使用类表示蓝图。一个作为类的示例蓝图可以在这里找到。

将结构种入数据库

创建所有蓝图后,必须构建并执行查询以创建数据库表。可以使用方法 Database::createTables() 来构建查询。调用此方法后,必须调用方法 Database::execute() 以创建所有数据库表。

//Build the query
$database->createTables();

//Just to display created query
echo '<pre>'.$database->getLastQuery().'</pre>';

//Execute
$database->execute();

创建实体类及其使用

实体类是基于蓝图(或表)的类。它们可以用于将表的记录映射到对象。每个蓝图将有一个 EntityMapper 类的实例,该实例可以用于创建实体类。

使用 EntityMapper 类生成的实体类是特殊的。它们将有一个名为 map() 的静态方法,可以自动将记录映射到实体实例。

创建实体类

创建实体的第一步是拥有实体将基于的蓝图。从蓝图生成 EntityMapper 类的实例。获得实例后,设置实体的属性,例如其名称、命名空间以及它将在哪里创建。最后,可以调用 EntityMapper::create() 方法来编写类的源代码。

$blueprint = $database->getTable('users_information');

//Get entity mapper
$entityMapper = $blueprint->getEntityMapper();

//Set properties of the entity
$entityMapper->setEntityName('UserInformation');
$entityMapper->setNamespace('');
$entityMapper->setPath(__DIR__);

//Create the entity. The output will be the class 'UserInformation'.
$entityMapper->create();

使用实体类

实体类可以用于将记录映射到对象。每个实体都有一个特殊的方法,称为 map()。该方法接受一个参数,该参数是一个表示获取的记录的关联数组。

结果集实例有一个名为 map($callback) 的数组方法。此方法的作用与函数 array_map($callback, $array) 相同。该方法返回的值是另一个具有映射记录的结果集。

$resultSet = $database->table('users_information')
        ->select()
        ->execute();

$mappedSet = $resultSet->map(function (array $record) {
    return UserInformation::map($record);
});

echo '<ul>';

foreach ($mappedSet as $record) {
    //$record is an object of type UserInformation
    echo '<li>'.$record->getFirstName().' '.$record->getLastName().'</li>';
}
echo '</ul>';

事务

假设数据库中有3个表,分别是 user_infouser_loginuser_contact。为了拥有完整的用户资料,必须在这3个表中同时存在用户信息。假设第一张和第二张表中的记录创建成功。但由于某些错误,最后一张表中的记录没有创建。这将导致数据完整性错误。为了解决这个问题,必须回滚插入过程。在这种情况下,数据库事务可以大有帮助。

数据库事务是工作单元,由多个必须一起执行的操作组成。如果其中一个操作失败,则必须回滚所有操作。可以使用方法 Database::transaction() 启动事务。该方法有两个参数,第一个是事务的逻辑作为闭包,第二个是传递给闭包的可选参数数组。闭包的第一个参数始终是 Database 的一个实例。

如果闭包返回 false 或抛出 DatabaseException,则事务将回滚。

$this->transaction(function (Database $db, User $toAdd) {
    $db->table('users')->insert([
        'full-name' => $toAdd->getFullName(),
        'email' => $toAdd->getEmail(),
        'created-by' => $toAdd->getCreatedBy(),
        'is-refresh' => 0
    ])->execute();

//Assuming such methods exist on calling class
    $addedUserId = $db->getLastUserID();
    $toAdd->getLoginInformation()->setUserId($addedUserId);
    $db->addUserLoginInfo($toAdd->getLoginInformation());
}, [$entity]);