latinosoft/pixie

一个轻量级、易于表达、不依赖于框架的PHP查询构建器。个人分支。

2.0.10-rc3 2018-05-26 20:40 UTC

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')也会选择ab。这在您想要在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。