vukbgit / pixie
一个轻量级、表达性、无框架约束的PHP查询构建器。
Requires
- php: >=5.4.0
- usmanhalalit/viocon: 1.0.*@dev
Requires (Dev)
- mockery/mockery: 0.9.4
- phpunit/phpunit: ^4.8
This package is auto-updated.
Last update: 2024-09-05 10:44:30 UTC
README
这是一个原始usmanhalalit/pixie的分支,我只是通过进行一些小的调整添加了对PHP 8.2的支持。全部功劳归功于原始的开发者。以下是原始的README内容
该项目未积极维护,但大多数功能都完全正常工作,并且没有重大安全问题,我只是没有投入太多时间。
Pixie查询构建器
一个轻量级、表达性、无框架约束的PHP查询构建器,也可以被称为数据库抽象层。Pixie支持MySQL、SQLite和PostgreSQL,并使用统一的API处理查询清理、表前缀以及许多其他功能。
它具有一些高级功能,如
- 查询事件
- 嵌套标准
- 子查询
- 嵌套查询
- 多个数据库连接。
语法与Laravel的查询构建器非常相似。
示例
// Make sure you have Composer's autoload file included require 'vendor/autoload.php'; // Create a connection, once only. $config = [ 'driver' => 'mysql', // Db driver 'host' => 'localhost', 'database' => 'your-database', 'username' => 'root', 'password' => 'your-password', 'charset' => 'utf8', // Optional 'collation' => 'utf8_unicode_ci', // Optional 'prefix' => 'cb_', // Table prefix, optional 'options' => [ // PDO constructor options, optional PDO::ATTR_TIMEOUT => 5, PDO::ATTR_EMULATE_PREPARES => false, ], ]; new \Pixie\Connection('mysql', $config, 'QB');
简单查询
以下查询返回id = 3的行,如果没有行则返回null。
$row = QB::table('my_table')->find(3);
完整查询
$query = QB::table('my_table')->where('name', '=', 'Sana'); // Get result $query->get();
查询事件
以下代码之后,每次在users
表上发生选择查询时,都会添加此where条件,以便禁止被禁止的用户访问。
QB::registerEvent('before-select', 'users', function($qb) { $qb->where('status', '!=', 'banned'); });
下面有许多高级选项,具体请参阅文档。心动了吗?让我们来安装。
安装
Pixie使用Composer来简化操作。
学习如何使用Composer,并将其添加到require部分(在您的composer.json中)
"usmanhalalit/pixie": "2.*@dev"
然后运行
composer update
在Packagist上的库。
完整使用API
目录
- 连接
- 查询
- 选择
- Where
- Group By和Order By
- Having
- Limit和Offset
- 连接
- 原始查询
- 插入
- 更新
- 删除
- 事务
- 获取构建的查询
- 子查询和嵌套查询
- 获取PDO实例
- 以指定类的对象形式获取结果
- 查询事件
连接
Pixie支持三种数据库驱动程序,MySQL、SQLite和PostgreSQL。您可以在创建新连接时指定驱动程序及其相关配置。您还可以创建多个连接,但一次只能使用一个别名。
// Make sure you have Composer's autoload file included require 'vendor/autoload.php'; $config = array( 'driver' => 'mysql', // Db driver 'host' => 'localhost', 'database' => 'your-database', 'username' => 'root', 'password' => 'your-password', 'charset' => 'utf8', // Optional 'collation' => 'utf8_unicode_ci', // Optional 'prefix' => 'cb_', // Table prefix, optional ); new \Pixie\Connection('mysql', $config, 'QB'); // Run query $query = QB::table('my_table')->where('name', '=', 'Sana');
别名
创建连接时
new \Pixie\Connection('mysql', $config, 'MyAlias');
MyAlias
是您想要使用的类别名的名称(例如MyAlias::table(...)
),您可以使用您喜欢的任何名称(包括命名空间,例如MyNamespace\\MyClass
),或者如果您不需要别名,则可以省略它。别名让您能够轻松地在整个应用程序中访问QueryBuilder类。
如果不使用别名,您可以单独实例化QueryBuilder处理器,这对于依赖注入和测试非常有帮助。
$connection = new \Pixie\Connection('mysql', $config); $qb = new \Pixie\QueryBuilder\QueryBuilderHandler($connection); $query = $qb->table('my_table')->where('name', '=', 'Sana'); var_dump($query->get());
这里的$connection
是可选的,如果没有提供,它将始终关联到第一个连接,但如果有多个数据库连接,它可能很有用。
SQLite和PostgreSQL配置示例
new \Pixie\Connection('sqlite', array( 'driver' => 'sqlite', 'database' => 'your-file.sqlite', 'prefix' => 'cb_', ), 'QB');
new \Pixie\Connection('pgsql', array( 'driver' => 'pgsql', 'host' => 'localhost', 'database' => 'your-database', 'username' => 'postgres', 'password' => 'your-password', 'charset' => 'utf8', 'prefix' => 'cb_', 'schema' => 'public', ), 'QB');
查询
您必须在使用每个查询之前使用 table()
方法,除了原始的 query()
。要从一个或多个表中选择,只需传递一个数组。
QB::table(array('mytable1', 'mytable2'));
轻松获取
以下查询返回 id = 3 的(第一)行,如果没有行则返回 null。
$row = QB::table('my_table')->find(3);
像这样访问您的行,echo $row->name
。如果您的字段名称不是 id
,那么将字段名称作为第二个参数传递,QB::table('my_table')->find(3, 'person_id');
。
以下查询返回所有名称为 'Sana' 的行,如果没有行则返回 null。
$result = QB::table('my_table')->findAll('name', 'Sana');
选择
$query = QB::table('my_table')->select('*');
多选
->select(array('mytable.myfield1', 'mytable.myfield2', 'another_table.myfield3'));
使用选择方法多次 select('a')->select('b')
也会选择 a
和 b
。如果您想要在 PHP 的 if
中进行条件选择,这可能很有用。
选择唯一
->selectDistinct(array('mytable.myfield1', 'mytable.myfield2'));
获取所有
返回一个数组。
$query = QB::table('my_table')->where('name', '=', 'Sana'); $result = $query->get();
您可以像这样遍历它
foreach ($result as $row) { echo $row->name; }
获取第一行
$query = QB::table('my_table')->where('name', '=', 'Sana'); $row = $query->first();
返回第一行,或者如果没有记录则返回 null。使用此方法,您还可以确保记录是否存在。像这样访问它们,echo $row->name
。
获取行数
$query = QB::table('my_table')->where('name', '=', 'Sana'); $query->count();
Where
基本语法是 (字段名,运算符,值)
,如果您提供两个参数,则假定 =
运算符。因此,where('name', 'usman')
和 where('name', '=', 'usman')
是相同的。
QB::table('my_table') ->where('name', '=', 'usman') ->whereNot('age', '>', 25) ->orWhere('type', '=', 'admin') ->orWhereNot('description', 'LIKE', '%query%') ;
Where In
QB::table('my_table') ->whereIn('name', array('usman', 'sana')) ->orWhereIn('name', array('heera', 'dalim')) ; QB::table('my_table') ->whereNotIn('name', array('heera', 'dalim')) ->orWhereNotIn('name', array('usman', 'sana')) ;
Where Between
QB::table('my_table') ->whereBetween('id', 10, 100) ->orWhereBetween('status', 5, 8);
Where Null
QB::table('my_table') ->whereNull('modified') ->orWhereNull('field2') ->whereNotNull('field3') ->orWhereNotNull('field4');
分组Where
有时查询会变得复杂,需要分组条件,例如 WHERE age = 10 and (name like '%usman%' or description LIKE '%usman%')
。
Pixie 允许您这样做,您可以根据需要嵌套任意数量的闭包,如下所示。
QB::table('my_table') ->where('my_table.age', 10) ->where(function($q) { $q->where('name', 'LIKE', '%usman%'); // You can provide a closure on these wheres too, to nest further. $q->orWhere('description', 'LIKE', '%usman%'); });
Group By和Order By
$query = QB::table('my_table')->groupBy('age')->orderBy('created_at', 'ASC');
多组 Group By
->groupBy(array('mytable.myfield1', 'mytable.myfield2', 'another_table.myfield3')); ->orderBy(array('mytable.myfield1', 'mytable.myfield2', 'another_table.myfield3'));
使用 groupBy()
或 orderBy()
方法多次 groupBy('a')->groupBy('b')
也会首先按 a
分组,然后按 b
分组。如果您想要在 PHP 的 if
中进行条件分组,这可能很有用。同样适用于 orderBy()
。
Having
->having('total_count', '>', 2) ->orHaving('type', '=', 'admin');
Limit和Offset
->limit(30); ->offset(10);
连接
QB::table('my_table') ->join('another_table', 'another_table.person_id', '=', 'my_table.id')
可用方法
- join() 或 innerJoin
- leftJoin()
- rightJoin()
如果您需要 FULL OUTER
连接或任何其他连接,只需将其作为 join
方法的第五个参数传递。
->join('another_table', 'another_table.person_id', '=', 'my_table.id', 'FULL OUTER')
多个连接条件
如果您需要多个条件来连接一个表,则将闭包作为第二个参数传递。
->join('another_table', function($table) { $table->on('another_table.person_id', '=', 'my_table.id'); $table->on('another_table.person_id2', '=', 'my_table.id2'); $table->orOn('another_table.age', '>', QB::raw(1)); })
原始查询
如果您需要,始终可以使用原始查询
$query = QB::query('select * from cb_my_table where age = 12'); var_dump($query->get());
您也可以传递您的绑定
QB::query('select * from cb_my_table where age = ? and name = ?', array(10, 'usman'));
原始表达式
当您用 raw()
方法包装表达式时,Pixie 不会尝试清理这些。
QB::table('my_table') ->select(QB::raw('count(cb_my_table.id) as tot')) ->where('value', '=', 'Ifrah') ->where(QB::raw('DATE(?)', 'now'))
注意:通过 query()
方法运行的查询在您通过绑定传递所有值之前都不会清理。通过 raw()
方法运行的查询也不会清理,您必须自己进行清理。当然,这些也不添加表前缀,但您可以使用 addTablePrefix()
方法。
插入
$data = array( 'name' => 'Sana', 'description' => 'Blah' ); $insertId = QB::table('my_table')->insert($data);
insert()
方法返回插入 id。
批量插入
$data = array( array( 'name' => 'Sana', 'description' => 'Blah' ), array( 'name' => 'Usman', 'description' => 'Blah' ), ); $insertIds = QB::table('my_table')->insert($data);
在批量插入的情况下,它将返回插入 id 的数组。
使用ON DUPLICATE KEY语句插入
$data = array( 'name' => 'Sana', 'counter' => 1 ); $dataUpdate = array( 'name' => 'Sana', 'counter' => 2 ); $insertId = QB::table('my_table')->onDuplicateKeyUpdate($dataUpdate)->insert($data);
更新
$data = array( 'name' => 'Sana', 'description' => 'Blah' ); QB::table('my_table')->where('id', 5)->update($data);
将名称字段更新为 Sana,并将描述字段更新为 Blah,其中 id = 5。
删除
QB::table('my_table')->where('id', '>', 5)->delete();
将删除所有 id 大于 5 的行。
事务
Pixie 具有运行数据库 "事务" 的能力,在这种情况下,所有数据库更改都不会保存,直到提交。这样,如果出现问题或与您预期的不同,则不会保存数据库更改,也不会进行任何更改。
以下是一个基本的事务示例
QB::transaction(function ($qb) { $qb->table('my_table')->insert(array( 'name' => 'Test', 'url' => 'example.com' )); $qb->table('my_table')->insert(array( 'name' => 'Test2', 'url' => 'example.com' )); });
如果这会导致任何错误(例如重复的名称或其他错误),则不会在数据库中显示任何数据集。如果没有,则更改将成功保存。
如果您希望手动提交或回滚更改,则可以使用相应的 commit()
和 rollback()
方法
QB::transaction(function ($qb) { $qb->table('my_table')->insert(array(/* data... */)); $qb->commit(); // to commit the changes (data would be saved) $qb->rollback(); // to rollback the changes (data would be rejected) });
获取构建的查询
有时您可能需要获取查询字符串,这是可能的。
$query = QB::table('my_table')->where('id', '=', 3); $queryObj = $query->getQuery();
getQuery()
将返回一个查询对象,您可以从中获取 SQL、绑定或原始 SQL。
$queryObj->getSql(); // Returns: SELECT * FROM my_table where `id` = ?
$queryObj->getBindings(); // Returns: array(3)
$queryObj->getRawSql(); // Returns: SELECT * FROM my_table where `id` = 3
子查询和嵌套查询
很少,但您可能需要执行子查询或嵌套查询。Pixie 足够强大,可以为您完成这项工作。您可以创建不同的查询对象并使用 QB::subQuery()
方法。
$subQuery = QB::table('person_details')->select('details')->where('person_id', '=', 3); $query = QB::table('my_table') ->select('my_table.*') ->select(QB::subQuery($subQuery, 'table_alias1')); $nestedQuery = QB::table(QB::subQuery($query, 'table_alias2'))->select('*'); $nestedQuery->get();
这将生成如下查询
SELECT * FROM (SELECT `cb_my_table`.*, (SELECT `details` FROM `cb_person_details` WHERE `person_id` = 3) as table_alias1 FROM `cb_my_table`) as table_alias2
注意:Pixie 不为子查询和嵌套查询使用绑定。它使用 PDO 的 quote()
方法引用值。
获取PDO实例
如果您需要获取 PDO 实例,可以这样做。
QB::pdo();
以指定类的对象形式获取结果
只需调用查询的 asObject
方法即可。
QB::table('my_table')->asObject('SomeClass', array('ctor', 'args'))->first();
此外,您可以通过调用setFetchMode
方法来微调获取模式。
QB::table('my_table')->setFetchMode(PDO::FETCH_COLUMN|PDO::FETCH_UNIQUE)->get();
查询事件
Pixie自带强大的查询事件,可增强您的应用程序。这些事件类似于数据库触发器,您可以在事件发生时执行某些操作,例如,您可以挂钩表的after-delete
事件,并从另一张表中删除相关数据。
可用事件
- before-select
- after-select
- before-insert
- after-insert
- before-update
- after-update
- before-delete
- after-delete
注册事件
QB::registerEvent('before-select', 'users', function($qb) { $qb->where('status', '!=', 'banned'); });
现在每次在users
表上执行选择查询时,都会添加此WHERE条件,因此被禁止的用户无法访问。
语法是registerEvent('事件类型', '表名', 闭包中的操作)
。
如果您想在查询任何表时执行事件,请将表名提供为':any'
。
其他示例
在向my_table
插入数据后,详细信息将插入到另一张表中
QB::registerEvent('after-insert', 'my_table', function($queryBuilder, $insertId) { $data = array('person_id' => $insertId, 'details' => 'Meh', 'age' => 5); $queryBuilder->table('person_details')->insert($data); });
每当向person_details
表插入数据时,设置时间戳字段created_at
,这样我们就不必在每个地方都指定它
QB::registerEvent('after-insert', 'person_details', function($queryBuilder, $insertId) { $queryBuilder->table('person_details')->where('id', $insertId)->update(array('created_at' => date('Y-m-d H:i:s'))); });
在从my_table
中删除后删除关系
QB::registerEvent('after-delete', 'my_table', function($queryBuilder, $queryObject) { $bindings = $queryObject->getBindings(); $queryBuilder->table('person_details')->where('person_id', $binding[0])->delete(); });
Pixie将当前查询构建器实例作为闭包的第一个参数传递,因此您可以使用此对象构建查询,您可以像通常的查询构建器(QB
)一样进行任何操作。
如果从before-*
查询处理器返回的不是null
,则值将是执行的结果,并且数据库实际上不会被查询(因此,相应的after-*
处理器也不会被调用)。
只有在after-*
事件中,您才会得到三个参数:**第一个**是查询构建器,**第三个**是执行时间作为浮点数,**第二个**则不同。
- 在
after-select
中,您将获得从select
获取的results
。 - 在
after-insert
中,您将获得插入ID(或者批量插入的情况下的ID数组)。 - 在
after-delete
中,您将获得查询对象(与您从getQuery()
获得的内容相同),您可以从中获取SQL和绑定。 - 在
after-update
中,您将获得与after-delete
相同的查询对象。
删除事件
QB::removeEvent('event-name', 'table-name');
一些用例
以下是一些查询事件可能非常有用的场景
- 限制被禁止的用户。
- 仅获取
deleted = 0
的记录。 - 实现所有查询的缓存。
- 在每次条目后触发用户通知。
- 在删除查询后删除关系数据。
- 在插入查询后插入关系数据。
- 在每次更新查询后保留修改记录。
- 在每次条目后添加/编辑
created_at
和updated_at
数据。
注意
- 查询事件按连接设置,因此多个数据库连接不会引起任何问题,创建新的查询构建器实例将保留您的事件。
- 查询事件是递归的,例如,在向
table_a
插入后,您的事件插入到table_b
,现在您可以为table_b
注册另一个事件,该事件将插入到table_c
。 - 当然,查询事件不与原始查询一起工作。
如果您发现任何错别字,请编辑并发送拉取请求。
© 2020 Muhammad Usman。许可协议为MIT许可。