latinosoft / pixie
一个轻量级、易于表达、不依赖于框架的PHP查询构建器。个人分支。
Requires
- php: >=5.6.0
Requires (Dev)
- mockery/mockery: 0.9.4
- phpunit/phpunit: ^5.7
This package is auto-updated.
Last update: 2024-09-25 07:46:05 UTC
README
一个轻量级、易于表达、不依赖于框架的PHP查询构建器,也可以称之为数据库抽象层。Pixie支持MySQL、SQLite和PostgreSQL,并使用统一的API处理查询清理、表前缀等问题。至少需要PHP 5.6。
注意
这个分支版本旨在更快地部署pull请求和修复bug。
它具有一些高级功能,例如
- 查询事件
- 嵌套标准
- 子查询
- 嵌套查询
- 多个数据库连接。
语法与Laravel的查询构建器非常相似。
示例
// Make sure you have Composer's autoload file included require 'vendor/autoload.php'; // Create a connection, once only. $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 'options' => array( // PDO constructor options, optional PDO::ATTR_TIMEOUT => 5, PDO::ATTR_EMULATE_PREPARES => false, ), ); $connection = new \Pixie\Connection($config);
简单查询
下面的查询返回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'); });
以下有许多高级选项,具体请参见文档。心动了吗?让我们来安装。
安装
尚未
完整使用API
目录
连接
Pixie支持三个数据库驱动程序,MySQL、SQLite和PostgreSQL。您可以在连接时指定驱动程序,并在创建新连接时指定相关配置。您还可以创建多个连接,但一次只能使用一个连接的别名。
// Make sure you have Composer's autoload file included require 'vendor/autoload.php'; $config = array( 'driver' => 'mysql', // Db driver/adapter '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($config); //$connection here is optional, if not given it will always associate itself to the first connection, but it can be useful when you have multiple database connections. $qb = new \Pixie\QueryBuilder\QueryBuilderHandler($connection); // Run query $query = $qb->table('my_table')->where('name', '=', 'Sana');
SQLite和PostgreSQL配置示例
$connection = new \Pixie\Connection(array( 'driver' => 'sqlite', 'database' => 'your-file.sqlite', 'prefix' => 'cb_', ));
$connection = new \Pixie\Connection(array( 'driver' => 'pgsql', 'host' => 'localhost', 'database' => 'your-database', 'username' => 'postgres', 'password' => 'your-password', 'charset' => 'utf8', 'prefix' => 'cb_', 'schema' => 'public', ));
查询
除了原始query()之外,您必须在每次查询之前使用table()方法。要从多个表中选择,只需传递一个数组。
$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');。
以下查询返回所有name = '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方法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();
带有子查询的选择
$subQuery1 = $this->builder->table('mail')->select($this->builder->raw('COUNT(*)')); $subQuery2 = $this->builder->table('event_message')->select($this->builder->raw('COUNT(*)')); $count = $this->builder->select($this->builder->subQuery($subQuery1, 'row1'), $this->builder->subQuery($subQuery2, 'row2'))->first();
将生成以下查询
SELECT (SELECT COUNT(*) FROM `cb_mail`) as row1, (SELECT COUNT(*) FROM `cb_event_message`) as row2 LIMIT 1
where
基本语法是 (字段名,运算符,值),如果您给出两个参数,则假定运算符为=。因此,where('name', 'usman')和where('name', '=', 'usman')是相同的。
$qb->table('my_table') ->where('name', '=', 'usman') ->whereNot('age', '>', 25) ->orWhere('type', '=', 'admin') ->orWhereNot('description', 'LIKE', '%query%') ;
使用别名
$qb->table(['my_table' => 'm']) ->where('m.name', '=', 'usman') ->whereNot('m.age', '>', 25) ->orWhere('m.type', '=', 'admin') ->orWhereNot('m.description', 'LIKE', '%query%') ->select(['m.age' => 'my_age', 'm.type' => 'my_type']) ;
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%'); });
分组by和排序by
$query = $qb->table('my_table')->groupBy('age')->orderBy('created_at', 'ASC');
多重分组
->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(30); ->offset(10);
连接
$qb->table('my_table') ->join('another_table', 'another_table.person_id', '=', 'my_table.id')
可用方法:
- join() 或 innerJoin
- leftJoin()
- rightJoin()
如果您需要 FULL OUTER 连接或其他连接,只需将连接类型作为 join 方法的第 5 个参数传递。
->join('another_table', 'another_table.person_id', '=', 'my_table.id', 'FULL OUTER')
对于别名,使用与 select() 和 table() 相同的语法。
$qb->table(['my_table' => 'm']) ->join(['another_table' => 'a'], 'a.person_id', '=', 'm.id')
数组中只使用第一个元素。
多个连接标准
如果您需要多个条件来连接一个表,则将闭包作为第二个参数传递。
->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);
将 id = 5 的行的名称字段更新为 Sana,描述字段更新为 Blah。
删除
$qb->table('my_table')->where('id', '>', 5)->delete();
将删除所有 id 大于 5 的行。
事务
Pixie 有运行数据库 "事务" 的能力,在这种情况下,所有数据库更改在提交之前都不会保存。这样,如果出现问题或与您预期不同,数据库更改将不会被保存,也不会进行任何更改。
这是一个基本的事务示例
$qb->transaction(function ($qb2) { $qb2->table('my_table')->insert(array( 'name' => 'Test', 'url' => 'example.com' )); $qb2->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 事件,并从另一个表中删除相关数据。
可用事件
- after-*
- before-*
- 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。 - 当然,查询事件不与原始查询一起使用。
如果您发现任何错误,请编辑并发送 pull request。
© 2016 Juan Noriega。许可协议:MIT。