tivins / database
一个流畅、轻量级且高效的PDO包装器。
Requires
- php: ^8.1
- ext-pdo: *
Requires (Dev)
- php-coveralls/php-coveralls: ^2.5
- phpunit/phpunit: ^9.5
This package is auto-updated.
Last update: 2024-09-25 20:56:53 UTC
README
一个安全、流畅、轻量级且高效的PDO包装器。
帮助防止SQL注入。
安装
要求
使用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();
连接
也可以使用 innerJoin、leftJoin。
$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
嵌套条件
为 SelectQuery、UpdateQuery 和 DeleteQuery 提供了条件。
$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... } }
完整示例
错误处理
数据库会抛出三种主要的异常。
- ConnectionException 异常,由数据库构造函数抛出,如果无法建立连接。
- DatabaseException 异常,在查询执行过程中抛出 PDO 异常时抛出。
- ConditionException 异常,在给定操作符不被允许时抛出。
所有这些异常都有显式的消息(主要来自 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 文件中)。