tivins/database

一个流畅、轻量级且高效的PDO包装器。

v1.0.0 2023-01-18 11:44 UTC

This package is auto-updated.

Last update: 2024-09-25 20:56:53 UTC


README

一个安全、流畅、轻量级且高效的PDO包装器。

帮助防止SQL注入。

Travis CI Github CI Coverage Status

安装

要求

composer.json

使用composer安装

composer require tivins/database

快速示例

use Tivins\Database\Database;
use Tivins\Database\Connectors\MySQLConnector;

require 'vendor/autoload.php';

$db = new Database(new MySQLConnector('dbname', 'user', 'password', 'localhost'));

$posts = $db->select('books', 'b')
    ->leftJoin('users', 'u', 'b.author_id = u.id')
    ->addFields('b')
    ->addField('u', 'name', 'author_name')
    ->condition('b.year', 2010)
    ->execute()
    ->fetchAll();

摘要

用法

连接器

创建一个 Database 实例需要一个有效的 Connector 实例。

# MySQL
$connector = new MySQLConnector('dbname', 'user', 'password');
# SQLite
$connector = new SQLiteConnector('path/to/file');

或者

$db = new Database (new MySQLConnector(
  dbname:   'my_database', 
  user:     'my_user', 
  password: 'my_encrypted_password',
  host:     'localhost',
  port:     3306,
));

然后创建一个带有已创建连接器的 Database 实例

$database = new Database($connector);

new Database() 尝试连接给定的 Connector 时,可能会抛出 ConnectionException

使用查询

以下两种用法都是有效的

// from database object
$query = $db->select('users', 'u');
// from new object
$query = new SelectQuery($db, 'users', 'u');

选择查询

基本

$data = $db->select('books', 'b')
    ->addFields('b')
    ->condition('b.reserved', 0)
    ->execute()
    ->fetchAll();

连接

也可以使用 innerJoinleftJoin

$db->select('books', 'b')
    ->addFields('b', ['id', 'title'])
    ->leftJoin('users', 'u', 'u.id = b.owner')
    ->addField('u', 'name', 'owner_name')
    ->condition('b.reserved', 1)
    ->execute()
    ->fetchAll();

表达式

$db->select('books', 'b')
    ->addField('b', 'title')
    ->addExpression('concat(title, ?)', 'some_field', time())
    ->condition('b.reserved', 0)
    ->execute()
    ->fetchAll();

分组

$tagsQuery = $db->select('tags', 't')
    ->innerJoin('book_tags', 'bt', 'bt.tag_id = t.id')
    ->addFields('t')
    ->addExpression('count(bt.book_id)', 'books_count')
    ->groupBy('t.id')
    ->orderBy('t.name', 'asc');

条件表达式

$db->select('books', 'b')
    ->addFields('b')
    ->conditionExpression('concat(b.id, "-", ?) = b.reference', $someValue)
    ->execute();

范围/限制

$query->limit(10);          # implicit start from 0.
$query->limitFrom(0, 10);   # explicit start from 0.
$query->limitFrom(100, 50); # will fetch 50 rows from 100th row.

排序

orderBy() 在查询中添加一个新的排序语句。它可以多次调用。

$query->orderBy('field', 'desc');

多次调用。在以下示例中,结果将按 post_type 排序,然后按 date 排序

$query->orderBy('post_type', 'desc')
      ->orderBy('date', 'asc');

插入查询

$db->insert('book')
    ->fields([
        'title' => 'Book title',
        'author' => 'John Doe',
    ])
    ->execute();

多行插入

$db->insert('book')
    ->multipleFields([
        ['title' => 'Book title', 'author' => 'John Doe'],
        ['title' => 'Another book title', 'author' => 'John Doe Jr'],
    ])
    ->execute();

或者,

$db->insert('book')
    ->multipleFields([
          ['Book title', 'John Doe'],
          ['Another book title', 'John Doe Jr'],
        ], 
        ['title', 'author'])
    ->execute();

execute() 将在 book 表中插入两行。

查看构建结果
  • 查询
    insert into `book` (`title`,`author`) values (?,?), (?,?);
  • 参数
    ["Book title","John Doe","Another book title","John Doe Jr"]

插入表达式

表达式可以用于传递给 fields() 函数的数组中。

$db->insert('geom')
    ->fields([
        'name'     => $name,
        'position' => new InsertExpression('POINT(?,?)', $x, $y)
    ])
    ->execute();

execute() 将在 book 表中插入两行。

查看构建结果
  • 查询
    insert into `geom` (`name`, `position`) values (?, POINT(?,?))
  • 参数
    [$name, $x, $y]

还可以与 MergeQuery 一起使用 InsertExpression。

更新查询

$db->update('book')
    ->fields(['reserved' => 1])
    ->condition('id', 123)
    ->execute();

合并查询

$db->merge('book')
    ->keys(['ean' => '123456'])
    ->fields(['title' => 'Book title', 'author' => 'John Doe'])
    ->execute();

删除查询

在给定的表上执行 delete 查询。可以在 DeleteQuery 对象上使用 Conditions 的所有方法。

$db->delete('book')
    ->whereIn('id', [3, 4, 5])
    ->execute();

创建查询

在当前数据库上执行 create table 查询。

$query = $db->create('sample')
    ->addAutoIncrement(name: 'id')
    ->addInteger('counter', 0, unsigned: true, nullable: false)
    ->addInteger('null_val', null, nullable: false)
    ->addJSON('json_field')
    ->execute();

字段类型

  • 整数

    $query->addPointer('id_user'); // Shortcut to Not-null Unsigned Integer
  • UnitEnum 或 BackedEnum

    Enum Fruits { case Apple; case Banana; }
    $query->addEnum('fruits', Fruits::cases());
  • 标准 Enum

    $query->addStdEnum('fruits', ['apple','banana'], 'apple');

选择-插入查询

执行选择,如果未找到则插入。

$qry = $db->selectInsert('users')->matching(['name' => 'test', 'state' => 1]);
$qry->fetch()->id; // 1
$qry->getProcessedOperation(); // MergeOperation::INSERT

$qry = $db->selectInsert('users')->matching(['name' => 'test', 'state' => 1]);
$qry->fetch()->id; // 1
$qry->getProcessedOperation(); // MergeOperation::SELECT

默认情况下,使用在 matching() 中给出的数组来插入新记录。

您可以定义插入查询的字段

$matches = ['email' => 'user@example.com'];
$obj = $db->selectInsert('users')
    ->matching($matches)
    ->fields($matches + ['name' =>  'user', 'created' => time()])
    ->fetch();

表达式

您可以使用 SelectQuery::addExpression() 将表达式添加到选择的字段。

签名: ->addExpression(string $expression, string $alias, array $args)

$query = $db->select('books', 'b')
    ->addExpression('concat(title, ?)', 'some_field', time())
    ->execute();

预定义表达式

计数 (addCount())

$total = $db->select('table','t')
    ->addCount('*')
    ->execute()
    ->fetchField();

条件

一些示例

->condition('field', 2);      // eg: where field = 2
->condition('field', 2, '>'); // eg: where field > 2
->condition('field', 2, '<'); // eg: where field < 2
->whereIn('field', [2,6,8]);  // eg: where field in (2,6,8)
->like('field', '%search%');  // eg: where field like '%search%'
->isNull('field');            // eg: where field is null
->isNotNull('field');         // eg: where field is not null

嵌套条件

SelectQueryUpdateQueryDeleteQuery 提供了条件。

$db->select('book', 'b')
    ->fields('b', ['id', 'title', 'author'])
    ->condition(
        $db->or()
        ->condition('id', 3, '>')
        ->like('title', '%php%')
    )
    ->execute();

以下是等价的内容

$db->select('book', 'b')
    ->fields('b', ['id', 'title', 'author'])
    ->condition(
        (new Conditions(Conditions::MODE_OR))
        ->condition('id', 3, '>')
        ->like('title', '%php%')
    )
    ->execute();

Having

$db->select('maps_polygons', 'p')
    // ->...
    ->having($db->and()->isNotNull('geom'))
    ->execute()
    //...
    ;

事务

use Tivins\Database{ Database, DatabaseException, MySQLConnector };
function makeSomething(Database $db)
{
    $db->transaction()
    try {
        // do some stuff
    }
    catch (DatabaseException $exception) {
        $db->rollback();
        // log exception...
    }
}

完整示例

请参阅 /tests/FullTest.php

错误处理

数据库会抛出三种主要的异常。

所有这些异常都有显式的消息(主要来自 PDO)。

使用短示例

try {
    $this->db = new Database($connector);
}
catch (ConnectionException $exception) {
    $this->logErrorInternally($exception->getMessage());
    $this->displayError("Cannot join the database.");
}
try {
    $this->db->insert('users')
        ->fields([
            'name' => 'DuplicateName',
        ])
        ->execute();  
}
catch (DatabaseException $exception) {
    $this->logErrorInternally($exception->getMessage());
    $this->displayError("Cannot create the user.");
}

单元测试

创建一个测试数据库,并在其上授予用户权限。在存储库根目录下添加一个 phpunit.xml 文件。

/* NB: This is a quick-start example. */
create database test_db;
create user test_user@localhost identified by 'test_passwd';
grant all on test_db.* to test_user@localhost;
flush privileges;
<phpunit>
    <php>
        <env name="DB_NAME" value="test_db"/>
        <env name="DB_USER" value="test_user"/>
        <env name="DB_PASS" value="test_password"/>
        <env name="DB_HOST" value="localhost"/>
    </php>
</phpunit>

然后,运行单元测试

vendor/bin/phpunit tests/

要包含覆盖率测试,使用

mkdir -p build/logs
vendor/bin/phpunit tests/ --coverage-clover cover.xml

许可

Database 在 MIT 许可证下发布。有关详细信息,请参阅附带 LICENSE 文件。

此外,如果您使用的是 --dev 模式,项目的一些部分附有其自己的许可证(在源文件中或在其旁边的 LICENSE 文件中)。

统计

Download Status