popphp/pop-db

Pop PHP 框架的 Pop Db 组件


README

Build Status Coverage Status

Join the chat at https://popphp.slack.com Join the chat at https://discord.gg/TZjgT74U7E

概述

pop-db 是一个强大的数据库 ORM 风格组件,它提供了一系列功能和功能,以轻松地与数据库接口。这些功能包括

  • 数据库适配器
    • MySQL
    • PostgreSQL
    • Sqlite
    • PDO
    • SQL Server
  • ORM 风格概念
    • 活动记录
    • 表网关
  • 关系
  • SQL 查询构建器
  • SQL 模式构建器
  • 迁移器
  • 分析器

pop-dbPop PHP 框架 的一个组件。

顶部

安装

使用 Composer 安装 pop-db

composer require popphp/pop-db

或者,在您的 composer.json 文件中引入它

"require": {
    "popphp/pop-db" : "^6.0.0"
}

顶部

快速入门

连接到数据库

您可以使用 Pop\Db\Db::connect() 方法连接到数据库

use Pop\Db\Db;

$db = Db::connect('mysql', [
    'database' => 'DATABASE',
    'username' => 'DB_USER',
    'password' => 'DB_PASS',
    'host'     => 'localhost'
]);

或者,另外,有针对每种数据库连接类型的简写方法

use Pop\Db\Db;

$db = Db::mysqlConnect([
    'database' => 'DATABASE',
    'username' => 'DB_USER',
    'password' => 'DB_PASS',
    'host'     => 'localhost'
]);
  • mysqlConnect()
  • pgsqlConnect()
  • sqliteConnect()
  • pdoConnect()
  • sqlsrvConnect()

如果没有提供 host 值,它将默认为 localhost

顶部

查询数据库

一旦您有了表示数据库连接的数据库对象,您就可以使用它来查询数据库。有一个 API 支持执行查询并返回结果

  • $db->select($sql, array $params = [])
  • $db->insert($sql, array $params = [])
  • $db->update($sql, array $params = [])
  • $db->delete($sql, array $params = [])

上述方法支持 SQL 查询以及带有参数的预处理语句。

use Pop\Db\Db;

$db = Db::mysqlConnect([
    'database' => 'DATABASE',
    'username' => 'DB_USER',
    'password' => 'DB_PASS'
]);

$users = $db->select('SELECT * FROM `users`');
print_r($users);

如果 users 表中存在任何用户记录,则结果将是

Array
(
    [0] => Array
        (
            [id] => 1
            [username] => testuser
            [password] => password
            [email] => test@test.com
        )

)

一个插入示例

use Pop\Db\Db;

$db = Db::mysqlConnect([
    'database' => 'DATABASE',
    'username' => 'DB_USER',
    'password' => 'DB_PASS'
]);

$db->insert(
    'INSERT INTO `users` (`username`, `password`, `email`) VALUES (?, ?, ?)',
    ['testuser1', 'password1', 'testuser1@test.com']
);

进行查询的更详细的方法是

use Pop\Db\Db;

$db = Db::mysqlConnect([
    'database' => 'DATABASE',
    'username' => 'DB_USER',
    'password' => 'DB_PASS'
]);

$db->query('SELECT * FROM `users`');
$users = $db->fetchAll();
print_r($users);

顶部

表类

使用像 pop-db 这样的 ORM 风格数据库库的好处之一是抽象出所需的 SQL 层,这样您就只需关注与 PHP 对象的交互,而无需编写 SQL。ORM 会为您完成这些操作。下面将更深入地探讨的一个示例是使用表示活动记录模式的表类。

use Pop\Db\Db;
use Pop\Db\Record;

$db = Db::mysqlConnect([
    'database' => 'DATABASE',
    'username' => 'DB_USER',
    'password' => 'DB_PASS'
]);

class Users extends Record {}

Record::setDb($db);

在上面的示例中,创建了一个数据库对象并将其传递给 Pop\Db\Record 类。这是为了让任何扩展 Pop\Db\Record 的类都能意识到并访问数据库对象。

然后,表示数据库中 users 表的表类扩展了 Pop\Db\Record 类,并继承其所有内置功能。从那里,可以调用方法从 users 表中检索数据或将新数据保存到 users 表中。

检索用户

$users = Users::findAll();
print_r($users->toArray());
Array
(
    [0] => Array
        (
            [id] => 1
            [username] => testuser
            [password] => 12345678
            [email] => test@test.com
        )

)

检索用户 ID 1

$user = Users::findById(1);
print_r($user->toArray());
Array
(
    [id] => 1
    [username] => testuser
    [password] => 12345678
    [email] => test@test.com
)

编辑用户 ID 1

$user = Users::findById(1);
$user->username = 'testuser2';
$user->email    = 'test2@test.com'; 
$user->save();
print_r($user->toArray());
Array
(
    [id] => 1
    [username] => testuser2
    [password] => 12345678
    [email] => test2@test.com
)

创建新用户

$user = new Users([
    'username' => 'newuser',
    'password' => 'somepassword',
    'email'    => 'newuser@test.com'
]);
$user->save();
print_r($user->toArray());
Array
(
    [username] => newuser
    [password] => somepassword
    [email] => newuser@test.com
    [id] => 2
)

删除用户 ID 1

$user = Users::findById(1);
$user->delete();

顶部

适配器

使用适配器连接数据库的基本方法已在快速入门部分进行了概述。在本节中,我们将介绍每个数据库适配器的基本方法。它们的连接参数略有不同,但一旦创建了不同的适配器对象,它们都具有与数据库交互的通用接口。

  • connect(array $options = [])
  • beginTransaction()
  • commit()
  • rollback()
  • isTransaction()
  • getTransactionDepth()
  • transaction($callable, array $params = [])
  • isSuccess()
  • select(string|Sql $sql, array $params = [])
  • insert(string|Sql $sql, array $params = [])
  • update(string|Sql $sql, array $params = [])
  • delete(string|Sql $sql, array $params = [])
  • executeSql(string|Sql $sql, array $params = [])
  • query(mixed $sql)
  • prepare(mixed $sql)
  • bindParams(array $params)
  • execute()
  • fetch()
  • fetchAll()
  • disconnect()
  • escape(?string $value = null)
  • getLastId()
  • getNumberOfRows()
  • getNumberOfAffectedRows()
  • getVersion()
  • getTables()

顶部

MySQL

创建MySQL数据库适配器并与MySQL数据库连接的支持选项有

  • database(必需)
  • username(必需)
  • password(必需)
  • host(可选,默认为localhost
  • port
  • socket
$db = Db::mysqlConnect([
    'database' => 'DATABASE',
    'username' => 'DB_USER',
    'password' => 'DB_PASS'
]);

返回的Pop\Db\Adapter\Mysql对象使用了与mysqli PHP扩展一起提供的mysqli类。

顶部

PostgreSQL

创建PostgreSQL数据库适配器并与PostgreSQL数据库连接的支持选项有

  • database(必需)
  • username(必需)
  • password(必需)
  • host(可选,默认为localhost
  • hostaddr
  • port
  • connect_timeout
  • options
  • sslmode
  • persist
$db = Db::pgsqlConnect([
    'database' => 'DATABASE',
    'username' => 'DB_USER',
    'password' => 'DB_PASS'
]);

返回的Pop\Db\Adapter\Pgsql对象使用了与pgsql PHP扩展一起提供的pg_*函数。

顶部

SQLite

创建SQLite数据库适配器并与SQLite数据库连接的支持选项有

  • database(必需 - 磁盘上数据库文件的路径)
  • flags
  • key
$db = Db::mysqlConnect([
    'database' => '/path/to/my_database.sqlite',
]);

返回的Pop\Db\Adapter\Sqlite对象使用了与sqlite3 PHP扩展一起提供的Sqlite3类。

注意:确保数据库文件具有适当的权限,以便数据库适配器能够访问和修改它是很重要的。

顶部

SQL Server

创建SQL Server数据库适配器并与SQL Server数据库连接的支持选项有

  • database(必需)
  • username(必需)
  • password(必需)
  • host(可选,默认为localhost
  • info
  • ReturnDatesAsStrings
$db = Db::sqlsrvConnect([
    'database' => 'DATABASE',
    'username' => 'DB_USER',
    'password' => 'DB_PASS'
]);

返回的Pop\Db\Adapter\Sqlsrv对象使用了与sqlsrv PHP扩展一起提供的sqlsrv_*函数。

顶部

PDO

PDO适配器与PHP中流行的PDO扩展一起工作。这包括PDO支持的多个数据库驱动程序。它们为其他原生驱动程序提供了一种替代方案。

创建PDO数据库适配器并与PDO支持的数据库连接的支持选项有

  • type(必需 - 驱动程序类型:mysqlpgsqlsqlitesqlsrv等)
  • database(必需)
  • username(对于需要凭证的数据库驱动程序是必需的)
  • password(对于需要凭证的数据库驱动程序是必需的)
  • host(可选,默认为localhost
$db = Db::pdoConnect([
    'type'     => 'mysql'
    'database' => 'DATABASE',
    'username' => 'DB_USER',
    'password' => 'DB_PASS'
]);

返回的Pop\Db\Adapter\Pdo对象使用了PDO扩展及其各种可用驱动程序提供的类和函数。

顶部

对象关系映射(ORM)

pop-db组件的主要概念是ORM(对象关系映射)。这意味着使数据库工作的所有复杂事物(连接、SQL查询等)都被抽象出来,这样开发者只需关心与PHP中的对象交互。其余的将由您在幕后以安全高效的方式处理。

当然,如果您更喜欢直接与那些抽象出来的概念工作,您仍然可以使用pop-db组件这样做。它提供了两种数据库交互方式的灵活性。

顶部

活动记录

pop-db 的 ORM 风格核心是其使用活动记录模式,该模式内置在 Pop\Db\Record 类中。正如快速入门部分所暗示的,主要概念是编写代表数据库中表的“表”类,并扩展 Pop\Db\Record 类。

use Pop\Db\Db;
use Pop\Db\Record;

$db = Db::mysqlConnect([
    'database' => 'DATABASE',
    'username' => 'DB_USER',
    'password' => 'DB_PASS'
]);

class Users extends Record {}

Record::setDb($db);

注册数据库

在上面的示例中,创建了一个 users 表类,它继承了 Pop\Db\Record 的所有功能。数据库适配器已与 Pop\Db\Record 类注册,这意味着扩展它的任何表类都将有权访问该数据库适配器。

如果您需要将特定的数据库适配器添加到特定的表类中,也可以这样做

use Pop\Db\Db;
use Pop\Db\Record;

$db = Db::mysqlConnect([
    'database' => 'DATABASE',
    'username' => 'DB_USER',
    'password' => 'DB_PASS'
]);

$dbUsers = Db::mysqlConnect([
    'database' => 'DATABASE_FOR_USERS',
    'username' => 'DB_USER',
    'password' => 'DB_PASS'
]);

class Users extends Record {};

Users::setDb($dbUsers); // Only the users table class uses the $dbUsers connection
Record::setDb($db);     // All other table classes will use the $db connection

表配置

默认情况下配置了一些内容

  • 表名自动从类名解析
    • Users 变为 users
    • UserLogins 变为 user_logins
  • 主键设置为 id
  • 没有表前缀

但是,您可以通过表属性来覆盖它

class Users extends Record
{
    protected ?string $table       = 'users_table';
    protected ?string $prefix      = 'my_app_';
    protected array   $primaryKeys = ['user_id'];
}

一旦配置了表类,就有一组基本的静态方法来获取数据库适配器或其他对象或信息

  • Users::getDb() - 获取数据库适配器对象
  • Users::db() - getDb() 的别名
  • Users::getSql() - 获取 SQL 构建器对象
  • Users::sql() - getSql() 的别名
  • Users::table() - 获取完整表名,例如 my_app_users_table
  • Users::getTableInfo() - 获取有关表的信息,如列等。

获取记录

使用表类的基本方式是从数据库中获取单个记录对象。所有以下示例都返回 Users 的实例。

// Fetch a single user record by ID
$user = Users::findById(1);
// Search for a single user record
$user = Users::findOne(['username' => 'testuser']);
// Search for a single user record, or create one if it doesn't exist
$user = Users::findOneOrCreate(['username' => 'testuser']);
// Search for the latest single user record
$user = Users::findLatest();

默认情况下,findLatest() 将使用主键,如 id。但是,您可以传递另一个字段来按该字段排序

// Search for the latest single user record by 'last_login'
$user = Users::findLatest('last_login');

查找 API

这些是用于在数据库表中选择记录或记录的静态方法

  • findById($id, array $options = null, bool $asArray = false)
  • findOne(array $columns = null, array $options = null, bool $asArray = false)
  • findOneOrCreate(array $columns = null, array $options = null, bool $asArray = false)
  • findLatest($by = null, array $columns = null, array $options = null, bool $asArray = false)
  • findBy(array $columns = null, array $options = null, bool $asArray = false)
  • findByOrCreate(array $columns = null, array $options = null, bool $asArray = false)
  • findIn($key, array $values, array $columns = null, array $options = null, bool $asArray = false)
  • findAll(array $options = null, bool $asArray = false)

这些是根据某些条件在数据库表中查找记录或记录的静态魔法帮助方法

  • findWhereEquals($column, $value, array $options = null, bool $asArray = false)
  • findWhereNotEquals($column, $value, array $options = null, bool $asArray = false)
  • findWhereGreaterThan($column, $value, array $options = null, bool $asArray = false)
  • findWhereGreaterThanOrEqual($column, $value, array $options = null, bool $asArray = false)
  • findWhereLessThan($column, $value, array $options = null, bool $asArray = false)
  • findWhereLessThanOrEqual($column, $value, array $options = null, bool $asArray = false)
  • findWhereLike($column, $value, array $options = null, bool $asArray = false)
  • findWhereNotLike($column, $value, array $options = null, bool $asArray = false)
  • findWhereIn($column, $values, array $options = null, bool $asArray = false)
  • findWhereNotIn($column, $values, array $options = null, bool $asArray = false)
  • findWhereBetween($column, $values, array $options = null, bool $asArray = false)
  • findWhereNotBetween($column, $values, array $options = null, bool $asArray = false)
  • findWhereNull($column, array $options = null, bool $asArray = false)
  • findWhereNotNull($column, array $options = null, bool $asArray = false)

修改记录

一旦获取了记录,就可以修改它并保存它

$user->username = 'newusername';
$user->save();

甚至可以删除它

$user->delete();

有其他方法可以修改现有记录

$user->increment('attempts'); // Increment column by one and save
$user->decrement('capacity', 5); // Decrement column by 5 and save
// Make a new copy of the user record in the database
// The $replace parameter can be an array of new, overriding column values
$newUser = $user->copy($replace);

脏记录

如果记录已被修改,则更改将存储,您可以像这样获取它们

$user->username = 'newusername';
$user->email    = 'newemail@test.com';

if ($user->isDirty()) {
    print_r($user->getDirty);
}
Array
(
    [old] => Array
        (
            [username] => testuser
            [email] => test@test.com
        )
    [new] => Array
        (
            [username] => newusername
            [email] => newemail@test.com
        )
)

这对于跟踪和记录数据库中数据更改的应用程序组件很有用。

顶部

编码记录

Pop\Db\Record\Encoded 类扩展了 Pop\Db\Record 类,并提供管理数据库记录中需要编码、序列化、加密或哈希的字段的功能。支持的类型包括:

  • JSON
  • PHP 序列化
  • Base64
  • 单向哈希
  • 双向加密

此类的优点是它为您处理编码和解码。要使用它,您需要像下面这样配置您的类,定义需要编码/解码的字段

use Pop\Db\Record\Encoded

class Users extends Encoded
{
    protected array $jsonFields   = ['metadata'];
    protected array $phpFields    = ['user_info'];
    protected array $base64Fields = ['user_image'];
} 

上面的例子意味着每次您保存到这些字段时,都会进行适当的字段数据编码,并将正确的编码数据存储在数据库中。然后,当您检索记录并检索这些字段时,将进行适当的解码,给您原始的解码数据。

单向哈希

使用密码哈希字段将是一个更高级的例子,需要更多的配置

use Pop\Db\Record\Encoded

class Users extends Encoded
{

    protected array  $hashFields    = ['password'];
    protected string $hashAlgorithm = PASSWORD_BCRYPT;
    protected array  $hashOptions   = ['cost' => 10];
}

此配置将使用定义的算法和选项在数据库中安全地创建和存储单向哈希值。然后,当需要时,您可以使用 verify() 方法并检查尝试的密码与存储的哈希值进行比较。

$user = Users::findOne(['username' => 'testuser']);
if ($user->verify('password', $attemptedPassword)) {
    // The user submitted the correct password.
}

双向加密

更高级的例子将是使用双向加密字段,它使用 Open SSL 库扩展。它需要配置更多表属性

  • $cipherMethod
  • $key
  • $iv

您必须创建一个 IV 值并将其 base64 编码以将其设置为 $iv 属性。

use Pop\Db\Record\Encoded

class Users extends Encoded
{
    protected array   $encryptedFields = ['sensitive_data'];
    protected ?string $cipherMethod    = 'aes-256-cbc';
    protected ?string $key             = 'YOUR_KEY';
    protected ?string $iv              = 'BASE64_ENCODED_IV_STRING';
}

此配置允许您在数据库中存储加密值,并在检索记录时安全地提取它。

顶部

表网关

Pop\Db\Record 类实际上具有允许您一次获取多个记录或行的功能,就像表数据网关一样。大多数这些调用返回的默认值是 Pop\Db\Record\Collection,它提供了对行或数据进行类似数组操作的功能。默认情况下,集合中的每个对象都是扩展 Pop\Db\Record 的表类的实例,这允许您直接与这些对象一起工作,修改或删除它们。

查找记录

// Find all users who have never logged in.
$users = Users::findBy(['logins' => 0]);
// Find a group of users
$users = Users::findIn('username', ['testuser', 'someotheruser', 'anotheruser']);
// Find all users
$users = Users::findAll();

您可以使用 toArray() 方法将集合对象转换为普通数组

// Returns an array
$users = Users::findBy(['logins' => 0])->toArray();

或者,在大多数方法中,都有一个 $asArray 参数可以完成相同的功能

// 3rd parameter $asArray set to true; Returns an array
$users = Users::findBy(['logins' => 0], null, true);

获取记录数

如果您只需要获取记录数,您可以这样做

// Get count of all users
$count = Users::getTotal();
// Get count of all users who have never logged in.
$count = Users::getTotal(['logins' => 0]);

顶部

选项

在上述大多数方法中,都有一个可用的 $options 数组,允许您真正定制查询。这些是支持选项

  • 选择
  • 偏移
  • 限制
  • 排序
  • 分组
  • 连接
选择列

使用 select 键传递您想要查询的字段数组。这可以帮助减少返回的不必要数据,或者帮助定义跨多个连接表选择的数据。如果没有使用此选项,则默认为 table_name.*

偏移

返回数据集的起始 offset。通常与分页一起使用

限制

限制结果值的值

排序

按字段或字段排序结果

分组

按字段或字段分组结果

连接

join 选项允许您在 JOIN 查询中定义多个要使用的表。

基本选项示例

$users = Users::findBy(['logins' => 0], [
    'select' => ['id', 'username'],
    'order'  => ['id DESC'],
    'offset' => 10
    'limit'  => 25
]);

使用连接的选项示例

假设存在另一个名为 Roles 的表,并且用户表包含一个 role_id 外键。

$users = Users::findBy(['logins' => 0], [
    'select' => [
        Users::table() . '.*',
        Roles::table() . '.role',
    ],
    'join' => [
        'table'   => Roles::table(),
        'columns' => [
            Roles::table() . '.id' => Users::table() . '.role_id',
        ],
    ],
]);

join 选项定义了要连接的表以及要连接的列。请注意,使用了 select 选项来构建所需的字段 - 在这种情况下,所有用户字段以及来自角色表的 role 字段。

默认的连接类型是 LEFT JOIN,但可以通过添加 type 键来定义其他连接类型。您还可以在嵌套数组中一次定义多个连接。

顶部

简写语法

Pop\Db\Record 类支持简化的 SQL 语法,可以帮助您对查询有更细粒度的控制,而无需编写自己的代码或使用查询构建器。以下是支持的内容及其翻译:

基本运算符

$users = Users::findBy(['id' => 1]);   // WHERE id = 1
$users = Users::findBy(['id!=' => 1]); // WHERE id != 1
$users = Users::findBy(['id>' => 1]);  // WHERE id > 1
$users = Users::findBy(['id>=' => 1]); // WHERE id >= 1
$users = Users::findBy(['id<' => 1]);  // WHERE id < 1
$users = Users::findBy(['id<=' => 1]); // WHERE id <= 1

LIKE 和 NOT LIKE

$users = Users::findBy(['%username%'   => 'test']); // WHERE username LIKE '%test%'
$users = Users::findBy(['username%'    => 'test']); // WHERE username LIKE 'test%'
$users = Users::findBy(['%username'    => 'test']); // WHERE username LIKE '%test'
$users = Users::findBy(['-%username'   => 'test']); // WHERE username NOT LIKE '%test'
$users = Users::findBy(['username%-'   => 'test']); // WHERE username NOT LIKE 'test%'
$users = Users::findBy(['-%username%-' => 'test']); // WHERE username NOT LIKE '%test%'

NULL 和 NOT NULL

$users = Users::findBy(['username' => null]);  // WHERE username IS NULL
$users = Users::findBy(['username-' => null]); // WHERE username IS NOT NULL

IN 和 NOT IN

$users = Users::findBy(['id' => [2, 3]]);  // WHERE id IN (2, 3)
$users = Users::findBy(['id-' => [2, 3]]); // WHERE id NOT IN (2, 3)

BETWEEN 和 NOT BETWEEN

$users = Users::findBy(['id' => '(1, 5)']);  // WHERE id BETWEEN (1, 5)
$users = Users::findBy(['id-' => '(1, 5)']); // WHERE id NOT BETWEEN (1, 5)

如果您需要在查询中使用多个条件,可以使用它们,并且它们将用 AND 连接

$users = Users::findBy([
    'id>'       => 1,
    '%username' => 'user1'
]);

这将被翻译为

WHERE (id > 1) AND (username LIKE '%test')

如果您需要使用 OR,可以像这样指定

$users = Users::findBy([
    'id>'       => 1,
    '%username' => 'user1 OR'
]);

请注意,第二个条件的值后面添加了 OR。这将像这样应用 OR 到谓词的这一部分

WHERE (id > 1) OR (username LIKE '%test')

顶部

执行查询

如果上述任何方法都不能满足需求,您可以通过表类直接执行查询。

查询(无参数)

这将返回一个 Pop\Db\Record\Collection 对象

$users = Users::query('SELECT * FROM ' . Users::table());

预处理语句(带参数)

这将返回一个 Pop\Db\Record\Collection 对象

$sql   = 'SELECT * FROM ' . Users::table() . ' WHERE last_login >= :last_login';
$users = Users::execute($sql, ['last_login' => '2023-11-01 08:00:00']);

顶部

Active Record Transactions

事务通过 ORM 活跃记录类提供。有几种方法可以通过主要记录类执行事务。在下面的示例中,通过调用 startTransaction() 方法开始事务。一旦调用该方法,后续的 save() 将在成功保存时自动调用 commitTransaction(),或者在抛出异常时调用 rollback 方法。

$user = new Users([
    'username' => 'testuser',
    'password' => 'password',
    'email'    => 'test@test.com'
]);
$user->startTransaction();
$user->save();

执行相同操作的一种简短方法是调用静态 start() 方法,该方法结合了构造函数和 startTransaction 调用。

$user = Users::start([
    'username' => 'testuser',
    'password' => 'password',
    'email'    => 'test@test.com'
]);
$user->save();

如果您需要执行包含多个查询的事务,这些查询涉及多个活跃记录对象,您也可以这样做。

try {
    Record::start();

    $user = new Users([
        'username' => 'testuser',
        'password' => 'password',
        'email'    => 'test@test.com'
    ]);
    $user->save();

    $role = new Roles([
        'role' => 'Admin'
    ]);
    $role->save();

    Record::commit();
} catch (\Exception $e) {
    Record::rollback();
    echo $e->getMessage();
}

要达到相同的效果,可以使用带有可调用的 transaction 方法。

try {
    Record::transaction(function() {
        $user = new Users([
            'username' => 'testuser',
            'password' => 'password',
            'email'    => 'test@test.com'
        ]);
        $user->save();
    
        $role = new Roles([
            'role' => 'Admin'
        ]);
        $role->save();
    });
} catch (\Exception $e) {
    echo $e->getMessage();
}

还支持嵌套事务。

try {
    Record::transaction(function() {
        $user = new Users([
            'username' => 'testuser',
            'password' => 'password',
            'email'    => 'test@test.com'
        ]);
        $user->save();
        
        Record::transaction(function(){
            $role = new Roles([
                'role' => 'Admin'
            ]);
            $role->save();
        });
    });
} catch (\Exception $e) {
    echo $e->getMessage();
}

顶部

关系

关系允许以简单的方式在数据库中选择相关数据。这些关系可以是 1:1 或 1:多,您可以在表类中将它们定义为方法。在 Pop\Db\Record 类中利用的主要方法包括

  • hasOneOf()
    • 1:1 关系,其中兄弟对象的 外键是不同兄弟对象的主键
  • hasOne()
    • 1:1 关系,其中子对象的外键是父对象的主键
  • hasMany()
    • 1:1 关系,其中多个子对象的外键是父对象的主键
  • belongsTo()
    • 1:1 关系,其中子对象的外键是父对象的主键(逆向 "hasOne")

让我们考虑以下表示数据库表的表类

class Users extends Pop\Db\Record
{

    /**
     * Mock Schema
     *    - id
     *    - role_id (FK to roles.id)
     *    - username
     *    - password
     *    - email
     */

    // Define the 1:1 relationship of the user's role
    public function role()
    {
        return $this->hasOneOf('Roles', 'role_id');
    }

    // Define the 1:1 relationship of the info record this user owns
    public function info()
    {
        return $this->hasOne('Info', 'user_id')
    }

    // Define the 1:many relationship to all the orders this user owns
    public function orders()
    {
        return $this->hasMany('Orders', 'user_id');
    }

}
class Roles extends Pop\Db\Record
{
    /**
     * Mock Schema
     *    - id (FK to users.role_id)
     *    - role
     */
}
class Info extends Pop\Db\Record
{
    /**
     * Mock Schema
     *    - user_id (FK to users.id)
     *    - address
     *    - phone
     */
    // Define the parent relationship up to the user that owns this info record
    public function user()
    {
        return $this->belongsTo('Users', 'user_id');
    }

}
class Orders extends Pop\Db\Record
{
    /**
     * Mock Schema
     *    - id
     *    - user_id (FK to users.id)
     *    - order_date
     *    - order_total
     *    - products
     */

    // Define the parent relationship up to the user that owns this order record
    public function user()
    {
        return $this->belongsTo('Users', 'user_id');
    }

}

定义了这些关系之后,现在可以像这样调用相关数据

// The two 1:1 relationships
$user = Users::findById(1);
print_r($user->role()->toArray());
print_r($user->info()->toArray());
Array
(
    [id] => 1
    [role] => Admin
)
Array
(
    [user_id] => 1
    [address] => 123 Main St
    [phone] => 504-555-5555
)
// The 1:many relationship
$user   = Users::findById(1);
$orders = $users->orders();

foreach ($orders as $order) {
    echo 'Order Total: $' . $order->order_total . PHP_EOL;
}
// The inverse 1:1 relationship
$userInfo = UserInfo::findOne(['user_id' => 1]);
print_r($userInfo->user()->toArray());
Array
(
    [id] => 1
    [role_id] => 1
    [username] => testuser
    [password] => 12345678
    [email] => test@test.com
)

预加载

在上面的示例中,相关数据是“延迟加载”的,这意味着相关数据在调用这些关系方法之前不可用。但是,您可以在调用主记录的同时调用这些关系方法,使用静态 with() 方法。

$users = Users::with('orders')->getById(1);
foreach ($user->orders as $order) {
    echo 'Order Total: $' . $order->order_total . PHP_EOL;
}

也可以传递多个关系。

$users = Users::with(['role', 'info', 'orders'])->getById(1);

还支持嵌套关系。假设有一个Posts类和一个Comments类。此外,假设用户对象拥有帖子,帖子对象拥有评论,并且每个表类中都设置了正确的关联关系。那么这个调用就是有效的

$user = Users::with('posts.comments')->getById(1);

这将返回一个包含用户所有posts的用户对象,并且每个帖子对象也将包含它们自己的comments

顶部

查询

您可以直接从数据库适配器查询数据库,而不是使用ORM组件。API可以帮助您进行特定查询或执行预处理语句,并返回结果。

  • $db->select(string|Sql $sql, array $params = []): array
  • $db->insert(string|Sql $sql, array $params = []): int
  • $db->update(string|Sql $sql, array $params = []): int
  • $db->delete(string|Sql $sql, array $params = []): int

select()的情况下,它将返回找到的结果数组。在其他情况下,它将返回受影响的行数。

使用查询

use Pop\Db\Db;

$db = Db::mysqlConnect([
    'database' => 'DATABASE',
    'username' => 'DB_USER',
    'password' => 'DB_PASS'
]);

$users = $db->select('SELECT * FROM `users`');
print_r($users);

使用带有参数的预处理语句

use Pop\Db\Db;

$db = Db::mysqlConnect([
    'database' => 'DATABASE',
    'username' => 'DB_USER',
    'password' => 'DB_PASS'
]);

$users = $db->select('SELECT * FROM `users` WHERE `id` < ?', [10]);
print_r($users);

查询数据库的更详细的方法

use Pop\Db\Db;

$db = Db::mysqlConnect([
    'database' => 'DATABASE',
    'username' => 'DB_USER',
    'password' => 'DB_PASS'
]);

$db->query('SELECT * FROM `users`');
$users = $db->fetchAll();
print_r($users);

顶部

预处理语句

更进一步,您还可以执行预处理语句

use Pop\Db\Db;

$db = Db::mysqlConnect([
    'database' => 'DATABASE',
    'username' => 'DB_USER',
    'password' => 'DB_PASS'
]);

$db->prepare('SELECT * FROM `users` WHERE `id` = ?');
$db->bindParams(['id' => 1]);
$db->execute();

$users = $db->fetchAll();
print_r($users);
Array
(
    [0] => Array
        (
            [id] => 1
            [role_id] => 1
            [username] => testuser
            [password] => 12test34
            [email] => test@test.com
        )

)

顶部

查询事务

当直接使用数据库适配器时,您可以像以下示例那样利用它的事务功能

try {
    $db->beginTransaction();
    $db->query("INSERT INTO `users` (`username`, `email`) VALUES ('testuser', 'test@test.com')");
    $db->commit();
} catch (\Exception $e) {
    $db->rollback();
}
try {
    $db->beginTransaction();
    $db->prepare("INSERT INTO `users` (`username`, `email`) VALUES (?, ?)")
        ->bindParam([
            'username' => 'testuser',
            'email'    => 'test@test.com'
        ]);
    $db->execute();
    $db->commit();
} catch (\Exception $e) {
    $db->rollback();
}

您也可以在单个事务中调用一系列查询,如下所示

try {
    $db->transaction(function() use ($db) {
        $db->query(
            "INSERT INTO `users` (`username`, `email`) VALUES ('testuser', 'test@test.com')"
        );
    });
} catch (\Exception $e) {
    echo $e->getMessage();
}

还支持嵌套事务。

try {
    $db->transaction(function() use ($db) {
        $db->query(
            "INSERT INTO `users` (`username`, `email`) VALUES ('testuser1', 'test1@test.com')"
        );
        $db->transaction(function() use ($db) {
            $db->query(
                "INSERT INTO `users` (`username`, `email`) VALUES ('testuser2', 'test2@test.com')"
            );
        });
    });
} catch (\Exception $e) {
    echo $e->getMessage();
}

顶部

查询构建器

查询构建器可用于构建适用于不同数据库适配器的有效SQL查询。如果构建的应用程序可能部署到具有不同数据库服务器的不同环境,这将非常有用。当预处理查询语句需要用于绑定参数的占位符时,请使用命名参数格式(例如,'id = :id')。它将被转换为数据库适配器正确的占位符值。

选择

use Pop\Db\Db;

$db = Db::mysqlConnect([
    'database' => 'DATABASE',
    'username' => 'DB_USER',
    'password' => 'DB_PASS'
]);

$sql = $db->createSql();
$sql->select(['id', 'username'])
    ->from('users')
    ->where('id = :id');

echo $sql;

以下是针对MySQL适配器生成的SQL查询

-- MySQL
SELECT `id`, `username` FROM `users` WHERE (`id` = ?)

切换到PostgeSQL适配器,相同的代码将生成

-- PostgreSQL
SELECT "id", "username" FROM "users" WHERE ("id" = $1)

切换到SQLite适配器,相同的代码将生成

-- SQLite
SELECT "id", "username" FROM "users" WHERE ("id" = :id)

当然,可以将$sql构建器对象直接传递给数据库适配器以直接执行已创建的SQL查询

use Pop\Db\Db;

$db = Db::mysqlConnect([
    'database' => 'DATABASE',
    'username' => 'DB_USER',
    'password' => 'DB_PASS'
]);

$sql = $db->createSql();
$sql->select(['id', 'username'])
    ->from('users')
    ->where('id = :id');

$db->prepare($sql);
$db->bindParams(['id' => 1]);
$db->execute();

$users = $db->fetchAll();
print_r($users);

顶部

插入

$sql->insert('users')->values([
    'username' => ':username',
    'password' => ':password'
]);

echo $sql;
-- MySQL
INSERT INTO `users` (`username`, `password`) VALUES (?, ?)
-- PostgreSQL
INSERT INTO "users" ("username", "password") VALUES ($1, $2)
-- SQLite
INSERT INTO "users" ("username", "password") VALUES (:username, :password)

顶部

更新

$sql->update('users')->values([
    'username' => ':username',
    'password' => ':password'
])->where('id = :id');

echo $sql;
-- MySQL
UPDATE `users` SET `username` = ?, `password` = ? WHERE (`id` = ?)
-- PostgreSQL
UPDATE "users" SET "username" = $1, "password" = $2 WHERE ("id" = $3)
-- SQLite
UPDATE "users" SET "username" = :username, "password" = :password WHERE ("id" = :id)

顶部

删除

$sql->delete('users')
    ->where('id = :id');

echo $sql;
-- MySQL
DELETE FROM `users` WHERE (`id` = ?)
-- PostgreSQL
DELETE FROM "users" WHERE ("id" = $1)
-- SQLite
DELETE FROM "users" WHERE ("id" = :id)

顶部

连接

SQL构建器有一个API,可以帮助您构造复杂的SQL语句,这些语句可以使用连接。通常,连接方法需要两个参数:外键表和一个数组,其中包含两个表之间相关列的'key => value'。以下是一个使用LEFT JOIN的SQL构建器示例

$sql->select(['id', 'username', 'email'])->from('users')
    ->leftJoin('user_info', ['users.id' => 'user_info.user_id'])
    ->where('id < :id')
    ->orderBy('id', 'DESC');

echo $sql;
-- MySQL
SELECT `id`, `username`, `email` FROM `users`
    LEFT JOIN `user_info` ON (`users`.`id` = `user_info`.`user_id`)
    WHERE (`id` < ?) ORDER BY `id` DESC
-- PostgreSQL
SELECT "id", "username", "email" FROM "users"
    LEFT JOIN "user_info" ON ("users"."id" = "user_info"."user_id")
    WHERE ("id" < $1) ORDER BY "id" DESC
-- SQLite
SELECT "id", "username", "email" FROM "users"
    LEFT JOIN "user_info" ON ("users"."id" = "user_info"."user_id")
    WHERE ("id" < :id) ORDER BY "id" DESC

以下是可用的连接API

  • $sql->join($foreignTable, array $columns, $join = 'JOIN'); - 基本连接
  • $sql->leftJoin($foreignTable, array $columns); - 左连接
  • $sql->rightJoin($foreignTable, array $columns); - 右连接
  • $sql->fullJoin($foreignTable, array $columns); - 全连接
  • $sql->outerJoin($foreignTable, array $columns); - 外连接
  • $sql->leftOuterJoin($foreignTable, array $columns); - 左外连接
  • $sql->rightOuterJoin($foreignTable, array $columns); - 右外连接
  • $sql->fullOuterJoin($foreignTable, array $columns); - 全外连接
  • $sql->innerJoin($foreignTable, array $columns); - 内连接
  • $sql->leftInnerJoin($foreignTable, array $columns); - 左内连接
  • $sql->rightInnerJoin($foreignTable, array $columns); - 右内连接
  • $sql->fullInnerJoin($foreignTable, array $columns); - 全内连接

顶部

谓词

SQL构建器还有一个广泛的API,可以帮助您构造用于过滤SQL语句的谓词。以下是一些可用的方法,以帮助您构造谓词子句

  • $sql->where($where); - 添加WHERE谓词
  • $sql->andWhere($where); - 使用AND连接符添加另一个WHERE谓词
  • $sql->orWhere($where); - 使用OR连接符添加另一个WHERE谓词
  • $sql->having($having); - 添加HAVING谓词
  • $sql->andHaving($having); - 使用AND连接符添加另一个HAVING谓词
  • $sql->orHaving($having); - 使用OR连接符添加另一个HAVING谓词

AND WHERE

$sql->select()
    ->from('users')
    ->where('id > :id')->andWhere('email LIKE :email');

echo $sql;
-- MySQL
SELECT * FROM `users` WHERE ((`id` > ?) AND (`email` LIKE ?))

OR WHERE

$sql->select()
    ->from('users')
    ->where('id > :id')->orWhere('email LIKE :email');

echo $sql;
-- MySQL
SELECT * FROM `users` WHERE ((`id` > ?) OR (`email` LIKE ?))

甚至还有一个更详细、更细粒度的API,它包含了谓词对象。

$sql->select()
    ->from('users')
    ->where->greaterThan('id', ':id')->and()->equalTo('email', ':email');

echo $sql;
-- MySQL
SELECT * FROM `users` WHERE ((`id` > ?) AND (`email` = ?))

顶部

嵌套谓词

如果您需要嵌套谓词,也有API方法允许您这样做

  • $sql->nest($conjunction = 'AND'); - 创建嵌套谓词集
  • $sql->andNest(); - 使用AND连接符创建嵌套谓词集
  • $sql->orNest(); - 使用OR连接符创建嵌套谓词集
$sql->select()
    ->from('users')
    ->where->greaterThan('id', ':id')
        ->nest()->greaterThan('logins', ':logins')
            ->or()->lessThanOrEqualTo('failed', ':failed');

echo $sql;

下面的输出显示了loginsfailed谓词被嵌套在一起

-- MySQL
SELECT * FROM `users` WHERE ((`id` > ?) AND ((`logins` > ?) OR (`failed` <= ?)))

顶部

排序、排序、限制

SQL Builder 还提供了方法来进一步控制SQL语句的结果集

  • $sql->groupBy($by); - 添加 GROUP BY
  • $sql->orderBy($by, $order = 'ASC'); - 添加 ORDER BY
  • $sql->limit($limit); - 添加 LIMIT
  • $sql->offset($offset); - 添加 OFFSET

顶部

模式构建器

除了查询构建器之外,还有一个模式构建器来帮助管理数据库表结构和它们的管理。与查询构建器类似,模式构建器有一个API,它反映了用于在数据库中创建、更改和删除表的SQL。它也旨在可移植,可以在可能具有不同数据库适配器的不同环境中工作。并且像查询构建器一样,为了正确运行,您需要传递当前应用程序正在使用的数据库适配器,以便它可以正确构建SQL。完成此操作的最简单方法是直接从数据库适配器调用createSchema()方法。它将自动注入到正在创建的模式构建器对象中。

下面的示例显示了单独的模式语句,但单个模式构建器对象在其生命周期内可以包含多个模式语句。

顶部

创建表

$db = Pop\Db\Db::mysqlConnect($options);

$schema = $db->createSchema();
$schema->create('users')
    ->int('id', 16)->increment()
    ->varchar('username', 255)
    ->varchar('password', 255)
    ->primary('id');

echo $schema;

上面的代码将生成以下SQL

-- MySQL
CREATE TABLE `users` (
  `id` INT(16) NOT NULL AUTO_INCREMENT,
  `username` VARCHAR(255),
  `password` VARCHAR(255),
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;

外键示例

以下是一个创建附加的user_info表示例,该表通过外键引用上面的users

$schema->create('user_info')
    ->int('user_id', 16)
    ->varchar('email', 255)
    ->varchar('phone', 255)
    ->foreignKey('user_id')->references('users')->on('id')->onDelete('CASCADE');

上面的代码将生成以下SQL

-- MySQL
CREATE TABLE `user_info` (
  `user_id` INT(16),
  `email` VARCHAR(255),
  `phone` VARCHAR(255)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

ALTER TABLE `user_info` ADD CONSTRAINT `fk_user_id` FOREIGN KEY (`user_id`)
  REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;

顶部

修改表

$schema->alter('users')
    ->varchar('email', 255)
    ->after('password');

echo $schema;

这相当于

$schema->alter('users')
    ->addColumn('email', 'VARCHAR', 255)
    ->after('password');

echo $schema;

将生成以下SQL

-- MySQL
ALTER TABLE `users` ADD `email` VARCHAR(255) AFTER `password`;

顶部

删除表

$schema->drop('users');

echo $schema;

上面的代码将生成以下SQL

-- MySQL
DROP TABLE `users`;

顶部

执行模式

您可以通过在模式构建器对象中使用execute()方法来执行模式

$schema->execute();

顶部

模式构建器 API

在上面的代码示例中,如果您想直接访问表对象,可以这样操作

$createTable   = $schema->create('users');
$alterTable    = $schema->alter('users');
$truncateTable = $schema->truncate('users');
$renameTable   = $schema->rename('users');
$dropTable     = $schema->drop('users');

以下是一个常见方法列表,可用于构建您的模式

  • $createTable->ifNotExists(); - 添加 IF NOT EXISTS标志
  • $createTable->addColumn($name, $type, $size = null, $precision = null, array $attributes = []); - 添加一个列
  • $createTable->increment($start = 1); - 设置增量值
  • $createTable->defaultIs($value); - 设置当前列的默认值
  • $createTable->nullable(); - 使当前列可空
  • $createTable->notNullable(); - 使当前列不可空
  • $createTable->index($column, $name = null, $type = 'index'); - 在列上创建索引
  • $createTable->unique($column, $name = null); - 在列上创建唯一索引
  • $createTable->primary($column, $name = null); - 在列上创建主索引

以下方法是为添加各种常见类型列的简写方法。请注意,如果所选列类型不受当前数据库适配器支持,则列类型将转换为最接近的类型。

  • $createTable->integer($name, $size = null, array $attributes = []);
  • $createTable->int($name, $size = null, array $attributes = []);
  • $createTable->bigInt($name, $size = null, array $attributes = []);
  • $createTable->mediumInt($name, $size = null, array $attributes = []);
  • $createTable->smallInt($name, $size = null, array $attributes = []);
  • $createTable->tinyInt($name, $size = null, array $attributes = []);
  • $createTable->float($name, $size = null, $precision = null, array $attributes = []);
  • $createTable->real($name, $size = null, $precision = null, array $attributes = [])
  • $createTable->double($name, $size = null, $precision = null, array $attributes = []);
  • $createTable->decimal($name, $size = null, $precision = null, array $attributes = []);
  • 创建表格方法:$createTable->numeric($name, $size = null, $precision = null, array $attributes = []);
  • 创建表格方法:$createTable->date($name, array $attributes = []);
  • 创建表格方法:$createTable->time($name, array $attributes = []);
  • 创建表格方法:$createTable->datetime($name, array $attributes = []);
  • 创建表格方法:$createTable->timestamp($name, array $attributes = []);
  • 创建表格方法:$createTable->year($name, $size = null, array $attributes = []);
  • 创建表格方法:$createTable->text($name, array $attributes = []);
  • 创建表格方法:$createTable->tinyText($name, array $attributes = []);
  • 创建表格方法:$createTable->mediumText($name, array $attributes = []);
  • 创建表格方法:$createTable->longText($name, array $attributes = []);
  • 创建表格方法:$createTable->blob($name, array $attributes = []);
  • 创建表格方法:$createTable->mediumBlob($name, array $attributes = []);
  • 创建表格方法:$createTable->longBlob($name, array $attributes = []);
  • 创建表格方法:$createTable->char($name, $size = null, array $attributes = []);
  • 创建表格方法:$createTable->varchar($name, $size = null, array $attributes = []);

以下方法都与创建外键约束及其关系有关

  • $createTable->foreignKey(string $column, ?string $name = null) - 在列上创建外键
  • $createTable->references($foreignTable); - 为当前外键约束创建对表的引用
  • $createTable->on($foreignColumn); - 与 references() 结合使用,指定外键列
  • $createTable->onDelete($action = null) - 设置外键约束的 ON DELETE 参数

顶部

迁移器

数据库迁移是辅助实施数据库新更改以及撤销任何更改到之前状态的脚本。它通过存储迁移类文件目录并跟踪当前状态或最后处理的状态来工作。从那里,您可以编写脚本以运行下一个迁移状态或回滚到上一个状态。状态可以存储在迁移文件夹中,也可以存储在数据库中其自己的表中。pop-kettle 组件内置了此功能,以协助管理应用程序的数据库迁移。

您可以创建一个空白模板迁移类,如下所示

use Pop\Db\Sql\Migrator;

Migrator::create('MyNewMigration', __DIR__ . 'migrations');

上面的代码将创建一个类似 migrations/20170225100742_my_new_migration.php 的文件,并包含一个空白类模板

<?php

use Pop\Db\Sql\Migration\AbstractMigration;

class MyNewMigration extends AbstractMigration
{

    public function up()
    {

    }

    public function down()
    {

    }

}

从那里,您可以在 up() 方法中编写您的向前迁移步骤,或者在 down() 方法中编写您的回滚步骤。以下是一个示例,在向前时创建一个表,在回滚时删除该表

<?php

use Pop\Db\Sql\Migration\AbstractMigration;

class MyNewMigration extends AbstractMigration
{

    public function up()
    {
        $schema = $this->db->createSchema();
        $schema->create('users')
            ->int('id', 16)->increment()
            ->varchar('username', 255)
            ->varchar('password', 255)
            ->primary('id');

        $schema->execute();
    }

    public function down()
    {
        $schema = $this->db->createSchema();
        $schema->drop('users');
        $schema->execute();
    }

}

要向前迁移,您会调用迁移器如下

use Pop\Db\Db;
use Pop\Db\Sql\Migrator;

$db = Pop\Db\Db::connect('mysql', [
    'database' => 'my_database',
    'username' => 'my_db_user',
    'password' => 'my_db_password',
    'host'     => 'mydb.server.com'
]);

$migrator = new Migrator($db, 'migrations');
$migrator->run();

上面的代码会创建具有定义列的表 users。要回滚迁移,您会调用迁移器如下

use Pop\Db\Db;
use Pop\Db\Sql\Migrator;

$db = Pop\Db\Db::connect('mysql', [
    'database' => 'my_database',
    'username' => 'my_db_user',
    'password' => 'my_db_password',
    'host'     => 'mydb.server.com'
]);

$migrator = new Migrator($db, 'migrations');
$migrator->rollback();

上面的代码会从数据库中删除表 users

顶部

播种器

类似于迁移,您还可以创建数据库种子类,以协助将一些初始数据填充到您的数据库中。此功能也内置在 pop-kettle 组件中。

use Pop\Db\Sql\Seeder;

Seeder::create('MyFirstSeeder', __DIR__ . '/seeds');

上面的代码将创建一个类似 seeds/20231105215257_my_first_seeder.php 的文件,并包含一个空白类模板

<?php

use Pop\Db\Adapter\AbstractAdapter;
use Pop\Db\Sql\Seeder\AbstractSeeder;

class MyFirstSeeder extends AbstractSeeder
{

    public function run(AbstractAdapter $db): void
    {

    }

}

从那里,您可以在 run() 方法中编写您的种子查询步骤。您可以与模式构建器和查询构建器交互。

<?php

use Pop\Db\Adapter\AbstractAdapter;
use Pop\Db\Sql\Seeder\AbstractSeeder;

class MyFirstSeeder extends AbstractSeeder
{

    public function run(AbstractAdapter $db): void
    {
        $schema = $db->createSchema();
        $schema->create('users')
            ->int('id', 16)->notNullable()->increment()
            ->varchar('username', 255)->notNullable()
            ->varchar('password', 255)->notNullable()
            ->varchar('email', 255)->nullable()
            ->primary('id');

        $db->query($schema);

        $sql = $db->createSql();
        $sql->insert('users')->values([
            'username' => 'testuser1',
            'password' => '12345678',
            'email'    => 'testuser1@test.com'
        ]);
        $db->query($sql);

        $sql->insert('users')->values([
            'username' => 'testuser2',
            'password' => '87654321',
            'email'    => 'testuser2@test.com'
        ]);
        $db->query($sql);

        $sql->insert('users')->values([
            'username' => 'testuser3',
            'password' => '74185296',
            'email'    => 'testuser3@test.com'
        ]);
        $db->query($sql);
    }

}

或者,您也可以使用纯 SQL 文件,种子器将解析它并执行其中的查询

CREATE TABLE `users` (
  `id` INT(16) NOT NULL AUTO_INCREMENT,
  `username` VARCHAR(255) NOT NULL,
  `password` VARCHAR(255) NOT NULL,
  `email` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;

INSERT INTO `users` (`id`, `username`, `password`, `email`) VALUES
(1, 'testuser1', '12345678', 'test1@test.com'),
(2, 'testuser2', '87654321', 'test2@test.com'),
(3, 'testuser3', '74185296', 'test3@test.com');

无论哪种方式,当您在种子器类上调用 run() 方法时,它将扫描文件夹以查找种子类或 SQL 文件并执行它们

$db = Db::mysqlConnect([
    'database' => 'DATABASE',
    'username' => 'DB_USER',
    'password' => 'DB_PASS'
]);

Seeder::run($db, __DIR__ . '/seeds');

顶部

SQL 数据

您可以使用 SQL 数据类将大量数据输出到有效的 SQL 文件中。

use Pop\Db\Db;
use Pop\Db\Sql\Data;

$db = Db::mysqlConnect([
    'database' => 'DATABASE',
    'username' => 'DB_USER',
    'password' => 'DB_PASS',
]);

$users = [
    [
        'id'       => 1,
        'username' => 'testuser1',
        'password' => 'mypassword1',
        'email'    => 'testuser1@test.com'
    ],
    [
        'id'       => 2,
        'username' => 'testuser2',
        'password' => 'mypassword2',
        'email'    => 'testuser2@test.com'
    ],
    [
        'id'       => 3,
        'username' => 'testuser3',
        'password' => 'mypassword3',
        'email'    => 'testuser3@test.com'
    ]
];

$data = new Data($db, 'users');
$data->streamToFile($users, __DIR__ . '/seeds/users.sql');

上面的示例代码将生成一个包含以下内容的 users.sql 文件

INSERT INTO `users` (`id`, `username`, `password`, `email`) VALUES
(1, 'testuser1', 'mypassword1', 'testuser1@test.com');
INSERT INTO `users` (`id`, `username`, `password`, `email`) VALUES
(2, 'testuser2', 'mypassword2', 'testuser2@test.com');
INSERT INTO `users` (`id`, `username`, `password`, `email`) VALUES
(3, 'testuser3', 'mypassword3', 'testuser3@test.com');

如果您有一个较大的集合,希望将其分成更少的 INSERT 查询,您可以将 divide 参数设置为

use Pop\Db\Db;
use Pop\Db\Sql\Data;

$db = Db::mysqlConnect([
    'database' => 'DATABASE',
    'username' => 'DB_USER',
    'password' => 'DB_PASS',
]);

$users = [
    [
        'id'       => 1,
        'username' => 'testuser1',
        'password' => 'mypassword1',
        'email'    => 'testuser1@test.com'
    ],
    // ... large array of data ...
    [
        'id'       => 18,
        'username' => 'testuser3',
        'password' => 'mypassword3',
        'email'    => 'testuser3@test.com'
    ]
];

$data = new Data($db, 'users', 10); // Set the divide to 10
$data->streamToFile($users, __DIR__ . '/seeds/users.sql');

这将生成

INSERT INTO `users` (`id`, `username`, `password`, `email`) VALUES
(1, 'testuser1', 'mypassword1', 'testuser1@test.com'),
(2, 'testuser2', 'mypassword2', 'testuser2@test.com'),
(3, 'testuser3', 'mypassword3', 'testuser3@test.com'),
(4, 'testuser4', 'mypassword4', 'testuser4@test.com'),
(5, 'testuser5', 'mypassword5', 'testuser5@test.com'),
(6, 'testuser6', 'mypassword6', 'testuser6@test.com'),
(7, 'testuser7', 'mypassword7', 'testuser7@test.com'),
(8, 'testuser8', 'mypassword8', 'testuser8@test.com'),
(9, 'testuser9', 'mypassword9', 'testuser9@test.com'),
(10, 'testuser10', 'mypassword10', 'testuser10@test.com');
INSERT INTO `users` (`id`, `username`, `password`, `email`) VALUES
(11, 'testuser11', 'mypassword11', 'testuser11@test.com'),
(12, 'testuser12', 'mypassword12', 'testuser12@test.com'),
(13, 'testuser13', 'mypassword13', 'testuser13@test.com'),
(14, 'testuser14', 'mypassword14', 'testuser14@test.com'),
(15, 'testuser15', 'mypassword15', 'testuser15@test.com'),
(16, 'testuser16', 'mypassword16', 'testuser16@test.com'),
(17, 'testuser17', 'mypassword17', 'testuser17@test.com'),
(18, 'testuser18', 'mypassword18', 'testuser18@test.com');

顶部

分析器

分析器对象与 pop-debug 组件协同工作,以设置查询监听器以监控性能并记录任何潜在问题。

use Pop\Db\Db;
use Pop\Db\Record;
use Pop\Debug\Debugger;
use Pop\Debug\Storage\File;
use Pop\Db\Adapter\Profiler\Profiler;

$db = Db::mysqlConnect([
    'database' => 'DATABASE',
    'username' => 'DB_USER',
    'password' => 'DB_PASS',
]);

class Users extends Record {}

Record::setDb($db);

// Register the debugger and query handler with the DB adapter
$debugger = new Debugger(new File(__DIR__ . '/log'));
$db->listen('Pop\Debug\Handler\QueryHandler', null, new Profiler($debugger));

// Save a user to the database
$user = new Users([
    'username' => 'admin',
    'password' => 'password',
    'email'    => 'admin@test.com'
]);

$user->save();

通过在数据库分析器中注册调试器和查询处理器,任何执行的查询都会自动与调试器一起记录。上述示例的调试器日志输出可能看起来像这样

Start:			1699246221.25475
Finish:			0.00000
Elapsed:		0.00997 seconds

Queries:
--------
INSERT INTO `users` (`username`, `password`, `email`) VALUES (?, ?, ?) [0.00674]
Start:			1699246221.25796
Finish:			1699246221.26470
Params:
	username => admin
	password => password
	email => admin@test.com

如果您想更精细地控制调试器何时触发,您也可以手动保存它

// Register the query handler with the DB adapter
$queryHandler = $db->listen('Pop\Debug\Handler\QueryHandler');

$debugger = new Debugger();
$debugger->addHandler($queryHandler);
$debugger->setStorage(new File(__DIR__ . '/log'));

// Save a user to the database
$user = new Users([
    'username' => 'admin',
    'password' => 'password',
    'email'    => 'admin@test.com'
]);

$user->save();
$debugger->save();

在上面的示例中,查询处理器从 listen() 方法调用返回,这可以进一步与独立的调试器注册。一旦最终查询在用户 save() 方法上运行,您就可以触发调试器的 save() 方法。

顶部