rougin/windstorm

一个可链式、可表达和互操作的 SQL 查询构建器。

dev-master 2020-08-20 22:51 UTC

This package is auto-updated.

Last update: 2024-09-25 08:18:43 UTC


README

Latest Version on Packagist Software License Build Status Coverage Status Quality Score Total Downloads

Windstorm 是一个基于 Doctrine 的 数据库抽象层 (DBAL) 的可表达 SQL 查询构建器。它具有与 DBAL 查询构建器相同的功能,但不同之处在于它不需要 Doctrine\DBAL\Connection 实例。它的目标是成为处理 SQL 查询构建器和 对象关系映射器 (ORM) 的单一接口。Windstorm 目前为 Doctrine(通过 DBAL)和 Eloquent 提供查询实现。

原因

我试图将 DoctrineEloquent 统一成一个单一接口,以便它们可以互换。不幸的是,由于不同的核心设计模式(Doctrine 使用 数据映射模式,而 Eloquent 使用 活动记录模式),实现是不可能的。后来我意识到,两者共同的一点是它们的查询构建器,这也在所有现有的 ORM 包和 SQL 查询构建器中很常见。

安装

通过 Composer 安装 Windstorm

$ composer require rougin/windstorm:dev-master

基本用法

配置

由于查询构建器默认不需要 Doctrine\DBAL\Connection,因此需要指定一个平台

use Doctrine\DBAL\Platforms\AbstractPlatform;
use Rougin\Windstorm\Doctrine\Builder;
use Rougin\Windstorm\Doctrine\Query;

// $platform instanceof AbstractPlatform

$query = new Query(new Builder($platform));

支持的平台列表(Doctrine DBAL):https://www.doctrine-project.org/projects/doctrine-dbal/en/2.8/reference/platforms.html

使用 Connection 实例

如果平台需要来自数据库连接,请使用 Connection::createQueryBuilder 方法

use Doctrine\DBAL\Connection;
use Rougin\Windstorm\Doctrine\Query;

// $connection instanceof Connection

$query = new Query($connection->createQueryBuilder());

有关获取连接的文档: https://www.doctrine-project.org/projects/doctrine-dbal/en/2.8/reference/configuration.html

查询构建器

查询构建器语法与编写 SQL 查询时的语法类似

// $query instanceof Rougin\Windstorm\QueryInterface

$query = $query
    ->select(array('u.id', 'u.name'))
    ->from('users', 'u')
    ->where('name')->like('%winds%')
    ->orderBy('created_at')->descending();

// SELECT u.id, u.name FROM users u WHERE u.name LIKE :u_name ORDER BY u.created_at DESC
$sql = $query->sql();

// array(':u_name' => '%winds%')
$bindings = $query->bindings();

返回结果

要从定义的查询中返回结果,必须在 ResultInterface 中实现一个实例。

// $connection instanceof Doctrine\DBAL\Connection
// $query instanceof Rougin\Windstorm\QueryInterface

use Rougin\Windstorm\Doctrine\Result;

$result = new Result($connection);

$query = $query->select(array('u.*'));

$query = $query->from('users');

$result = $result->execute($query);

var_dump((array) $result->items());
array(3) {
  [0] =>
  array(4) {
    'id' =>
    string(1) "1"
    'name' =>
    string(9) "Windstorm"
    'created_at' =>
    string(19) "2018-10-15 23:06:28"
    'updated_at' =>
    NULL
  }
  [1] =>
  array(4) {
    'id' =>
    string(1) "2"
    'name' =>
    string(11) "SQL Builder"
    'created_at' =>
    string(19) "2018-10-15 23:09:47"
    'updated_at' =>
    NULL
  }
  [2] =>
  array(4) {
    'id' =>
    string(1) "3"
    'name' =>
    string(12) "Rougin Gutib"
    'created_at' =>
    string(19) "2018-10-15 23:14:45"
    'updated_at' =>
    NULL
  }
}

QueryRepository 和突变

QueryRepository 实例是一个特殊类,它将通过使用突变(在 MutatorInterface 中实现)来通过突变修改 QueryInterface。使用这种方法将条件分离到类中,而不是在仓库中定义方法。

namespace Acme\Mutators;

use Rougin\Windstorm\Mutators\ReturnEntity;

class ReturnUser extends ReturnEntity
{
    protected $table = 'users';
}

可扩展的可用突变

  • CreateEntity(array $data) - 生成 INSERT INTO 查询
  • DeleteEntity(integer $id) - 生成 DELETE FROM 查询
  • ReturnEntities($limit, $offset) - 生成带有限制和偏移的 SELECT 查询
  • ReturnEntity(integer $id) - 生成 SELECT 查询(在 ResultInterface 中使用 first
  • UpdateEntity($id, array $data) - 生成 UPDATE 查询
// $query instanceof Rougin\Windstorm\QueryInterface;
// $result instanceof Rougin\Windstorm\ResultInterface;

use Acme\Mutators\ReturnUser;

$query = $query->select(['*'])->from('users');

$query = new QueryRepository($query, $result);

$query = $query->mutate(new ReturnUser(1));

var_dump($query->first());
array(4) {
  'id' =>
  string(1) "1"
  'name' =>
  string(9) "Windstorm"
  'created_at' =>
  string(19) "2018-10-15 23:06:28"
  'updated_at' =>
  NULL
}

要将结果映射到类中,请实现一个 MapperInterface 中的映射器

namespace Acme\Mappers;

use Acme\Models\User;

class UserMapper implements MapperInterface
{
    public function map($data)
    {
        return new User($data['id'], $data['name']);
    }
}
// $query instanceof Rougin\Windstorm\QueryRepository;

use Acme\Mappers\UserMapper;

$query->mapper(new UserMapper);

var_dump($query->first());
class Acme\Models\User#11 (2) {
  protected $id =>
  string(1) "1"
  protected $name =>
  string(6) "Windstorm"
}

如果没有实现从 MapperInterface 继承的类,则将从 ResultInterface 返回数据

混合查询

在执行SQL查询时,在ResultInterface中只允许执行一个QueryInterface。但存在需要先执行一个查询实例,然后使用前一个查询的结果执行另一个查询实例的场景。为了解决这个问题,可以实现一个MixedInterface,它仍然是一个QueryInterface,但能够添加子查询(在ChildInterface中实现)。

// $users instanceof \Rougin\Windstorm\QueryInterface
// $posts instanceof \Rougin\Windstorm\QueryInterface

use Rougin\Windstorm\Relation\Mixed;
use Rougin\Windstorm\Relation\Child;

$mixed = new Mixed($users, 'id');

$child = new Child($child, 'id', 'user_id');

$mixed->add($child, 'posts');

// SELECT u.id, u.name FROM users u
echo $mixed->sql();

$child = current($mixed->all());

// SELECT p.id, p.title, p.body, p.user_id FROM posts p
echo $child->sql();

致谢

许可证

MIT许可证(MIT)。更多信息请参阅LICENSE