Ray.QueryModule
, (*1)
Japanese, (*2)
Overview
Ray.QueryModule
makes a query to an external media such as a database or Web API with a function object to be injected., (*3)
-
SqlQueryModule
is for DB. Convert the SQL file to a simple function object that executes that SQL.
-
WebQueryModule
is for the Web API. Convert the URI and method set into a simple function object that Web requests to that URI.
-
PhpQueryModule
is a generic module. It provides storage access which can not be provided by static conversion by PHP function object.
Motivation
- You can have a clear boundary between domain layer (usage code) and infrastructure layer (injected function) in code.
- Execution objects are generated automatically so you do not need to write procedural code for execution.
- Since usage codes are indifferent to the actual state of external media, storage can be changed later. Easy parallel development and stabbing.
Installation
Composer install
$ composer require ray/query-module
Module install
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 files
$sqlDir/todo_insert.sql, (*4)
INSERT INTO todo (id, title) VALUES (:id, :title)
$sqlDir/todo_item_by_id.sql, (*5)
SELECT * FROM todo WHERE id = :id
Convert SQL to SQL invocation object
A callable object injected into the constructor. Those object was made in specified sql with @Named
binding., (*6)
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
]);
}
}
Row or RowList
You can specify expected return value type is either Row
or RowList
with RowInterface
or RowListInterface
.
RowInterface
is handy to specify SQL which return single row., (*7)
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
}
}
Override the method with callable object
Entire method invocation can be override with callable object in specified with @Query
., (*8)
class Foo
{
/**
* @Query(id="todo_item_by_id")
*/
public function get(string $id)
{
}
}
When parameter name is different method arguments and Query object arguments, uri_template style expression can solve it., (*9)
class FooTempalted
{
/**
* @Query(id="todo_item_by_id?id={a}", templated=true)
*/
public function get(string $a)
{
}
}
Specify type='row'
when single row result is expected to return., (*10)
class FooRow
{
/**
* @Query(id="ticket_item_by_id", type="row")
*/
public function onGet(string $id) : ResourceObject
{
}
}
If there is no SELECT result, it returns 404 Not Found
., (*11)
Convert URI to Web request object
With WebQueryModule
, it converts the URI bound in the configuration into an invocation object for web access and injects it.
In the following example, an invocation object of $createTodo
which makesPOST
request to https://httpbin.org/todo
is injected as $createTodo
., (*12)
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));
}
}
The usage code is the same as for SqlQueryModule
., (*13)
/**
* @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]);
The usage code of @Query
does not change either., (*14)
Bind to PHP class
If other dependencies are needed, we bind to PHP class and use dependency as a service., (*15)
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;
}
}
Bind to callable
., (*16)
$this->bind('')->annotatedWith('cretate_todo')->to(CreateTodo::class); // callableはインターフェイスなし
The usage codes are the same. The usage code of @Query
does not change either., (*17)
ISO8601 DateTime Module
Convert the specified column name value to the ISO8601 format. In PHP, it is a format defined by constants of DateTime::ATOM.
Install date column names as an array and pass it as an argument to Iso8601FormatModule
., (*18)
$this->install(new Iso8601FormatModule(['created_at', 'updated_at']));
SQL file name log
The SQL file name can be appended to the SQL statement as a comment. This is useful for query logging., (*19)
use Ray\Query\SqlFileName;
use Ray\Query\SqlQueryModule;
$this->install(new SqlQueryModule(__DIR__ . '/Fake/sql', null, new SqlFileName()));
````
Execute SQL
```sql
/* todo_item_by_id.sql */ SELECT * FROM todo WHERE id = :id
````
## Demo
php demo/run.php
```, (*20)
BEAR.Sunday example