ray/query-module

外部媒体访问框架

0.9.0 2022-04-26 01:39 UTC

README

codecov Type Coverage Continuous Integration

日语

概述

Ray.QueryModule 使用一个注入的函数对象对数据库或 Web API 等外部媒体进行查询。

  • SqlQueryModule 用于数据库。将 SQL 文件转换为执行该 SQL 的简单函数对象。
  • WebQueryModule 用于 Web API。将 URI 和方法集转换为执行 Web 请求的简单函数对象。
  • PhpQueryModule 是一个通用模块。它提供存储访问,这是 PHP 函数对象无法通过静态转换提供的。

动机

  • 在代码中,您可以清楚地在领域层(使用代码)和基础设施层(注入的函数)之间划分界限。
  • 执行对象会自动生成,因此您不需要编写执行过程代码。
  • 由于使用代码对外部媒体的实际状态不关心,因此以后可以更改存储。便于并行开发和并行攻击。

安装

Composer 安装

$ composer require ray/query-module

模块安装

use Ray\Di\AbstractModule;
use Ray\Query\SqlQueryModule;

class AppModule extends AbstractModule
{
    protected function configure()
    {
        // SqlQueryModule install
        $this->install(new SqlQueryModule($sqlDir));

        // WebQueryModule install
        $webQueryConfig = [
            'post_todo' => ['POST', 'https://httpbin.org/todo'], // bind-name => [method, uri]
            'get_todo' => ['GET', 'https://httpbin.org/todo']
        ];
        $guzzleConfig = []; // @see http://docs.guzzlephp.org/en/stable/request-options.html
        $this->install(new WebQueryModule($webQueryConfig, $guzzleConfig));
    }
}

SQL 文件

$sqlDir/todo_insert.sql

INSERT INTO todo (id, title) VALUES (:id, :title)

$sqlDir/todo_item_by_id.sql

SELECT * FROM todo WHERE id = :id

将 SQL 转换为 SQL 调用对象

注入到构造函数中的可调用对象。这些对象是在指定的 SQL 中使用 @Named 绑定创建的。

class Todo
{
    /**
     * @var callable
     */
    private $createTodo;
    
    /**
     * @var callable
     */
    private $todo;
    
    /**
     * @Named("createTodo=todo_insert, todo=todo_item_by_id")
     */
    public function __construct(
        callable $createTodo,
        callable $todo
    ){
        $this->createTodo = $createTodo;
        $this->todo = $todo;
    }
    
    public function get(string $uuid)
    {
        return ($this->todo)(['id' => $uuid]);
    }

    public function create(string $uuid, string $title)
    {
        ($this->createTodo)([
            'id' => $uuid,
            'title' => $title
        ]);
    }
}

行或行列表

您可以使用 RowInterfaceRowListInterface 指定期望的返回值类型为 RowRowListRowInterface 用于指定返回单行的 SQL。

use Ray\Query\RowInterface;

class Todo
{
    /**
     * @Named("todo_item_by_id")
     */
    public function __construct(RowInterface $todo)
    {
        $this->todo = $todo;
    }
    
    public function get(string $uuid)
    {
        $todo = ($this->todo)(['id' => $uuid]); // single row data
    }
}
use Ray\Query\RowListInterface;

class Todos
{
    /**
     * @Named("todos")
     */
    public function __construct(RowListInterface $todos)
    {
        $this->todos = $todos;
    }
    
    public function get(string $uuid)
    {
        $todos = ($this->todos)(); // multiple row data
    }
}

使用可调用对象覆盖方法

可以使用指定了 @Query 的可调用对象覆盖整个方法调用。

class Foo
{
    /**
     * @Query(id="todo_item_by_id")
     */
    public function get(string $id)
    {
    }
}

当参数名与方法和查询对象参数不同时,可以使用 uri_template 样式表达式解决这个问题。

class FooTempalted
{
    /**
     * @Query(id="todo_item_by_id?id={a}", templated=true)
     */
    public function get(string $a)
    {
    }
}

当期望返回单行结果时,指定 type='row'

class FooRow
{
    /**
     * @Query(id="ticket_item_by_id", type="row")
     */
    public function onGet(string $id) : ResourceObject
    {
    }
}

如果没有 SELECT 结果,则返回 404 Not Found

将 URI 转换为 Web 请求对象

使用 WebQueryModule,它将配置中绑定的 URI 转换为 Web 访问的调用对象并注入它。在以下示例中,将向 https://httpbin.org/todo 发送 POST 请求的 $createTodo 调用对象注入为 $createTodo

use Ray\Di\AbstractModule;
use Ray\Query\SqlQueryModule;

class AppModule extends AbstractModule
{
    protected function configure()
    {
        // WebQueryModuleインストール
        $webQueryConfig = [
            'todo_post' => ['POST', 'https://httpbin.org/todo'],
            'todo_get' => ['GET', 'https://httpbin.org/todo']
        ];
        $guzzleConfig = [];
        $this->install(new WebQueryModule($webQueryConfig, $guzzleConfig));
    }
}

使用代码与 SqlQueryModule 相同。

/**
 * @Named("createTodo=todo_post, todo=todo_get")
 */
public function __construct(
    callable $createTodo,
    callable $todo
){
    $this->createTodo = $createTodo;
    $this->todo = $todo;
}
// POST
($this->createTodo)([
    'id' => $uuid,
    'title' => $title
]);

// GET
($this->todo)(['id' => $uuid]);

@Query 的使用代码也没有改变。

绑定到 PHP 类

如果需要其他依赖项,我们将其绑定到 PHP 类,并使用依赖项作为服务。

class CreateTodo implements QueryInterface
{
    private $pdo;
    private $builder;

    public function __construct(PdoInterface $pdo, QueryBuilderInferface $builder)
    {
        $this->pdo = $pdo;
        $this->builder = $builder;
    }

    public function __invoke(array $query)
    {
        // Query execution using $pdo and $builder
        return $result;
    }
}

绑定到 callable

$this->bind('')->annotatedWith('cretate_todo')->to(CreateTodo::class); // callableはインターフェイスなし

使用代码相同。使用 @Query 的使用代码也没有改变。

ISO8601 日期时间模块

将指定的列名值转换为 ISO8601 格式。在 PHP 中,这是由 DateTime::ATOM 常量定义的格式。将日期列名作为数组安装,并将其作为参数传递给 Iso8601FormatModule

$this->install(new Iso8601FormatModule(['created_at', 'updated_at']));

SQL 文件名日志

可以将 SQL 文件名附加到 SQL 语句作为注释。这对于查询日志非常有用。

use Ray\Query\SqlFileName;
use Ray\Query\SqlQueryModule;

$this->install(new SqlQueryModule(__DIR__ . '/Fake/sql', null, new SqlFileName()));

执行 SQL

/* todo_item_by_id.sql */ SELECT * FROM todo WHERE id = :id

演示

php demo/run.php

BEAR.Sunday 示例