ray / query-module
An external media access framework
Installs: 195 835
Dependents: 2
Suggesters: 0
Security: 0
Stars: 6
Watchers: 4
Forks: 4
Open Issues: 4
Requires
- php: ^7.3 || ^8.0
- ext-json: *
- ext-pdo: *
- aura/sql: ^3.0 | ^4.0 | ^5.0
- bear/resource: ^1.15
- doctrine/annotations: ^1.12
- guzzlehttp/guzzle: ^6.3 || ^7.0
- koriym/query-locator: ^1.4
- nikic/php-parser: ^v4.13
- ray/aop: ^2.10.3
- ray/aura-sql-module: ^1.10.0
- ray/di: ^2.11
Requires (Dev)
- bamarni/composer-bin-plugin: ^1.4
- phpunit/phpunit: ^9.5
This package is auto-updated.
Last update: 2024-10-13 02:59:02 UTC
README
Overview
Ray.QueryModule
makes a query to an external media such as a database or Web API with a function object to be injected.
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
INSERT INTO todo (id, title) VALUES (:id, :title)
$sqlDir/todo_item_by_id.sql
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.
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.
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
.
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.
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.
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
.
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 makes POST
request to https://httpbin.org/todo
is injected as $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)); } }
The usage code is the same as for 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]);
The usage code of @Query
does not change either.
Bind to PHP class
If other dependencies are needed, we bind to PHP class and use dependency as a service.
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
.
$this->bind('')->annotatedWith('cretate_todo')->to(CreateTodo::class); // callableはインターフェイスなし
The usage codes are the same. The usage code of @Query
does not change either.
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
.
$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.
use Ray\Query\SqlFileName; use Ray\Query\SqlQueryModule; $this->install(new SqlQueryModule(__DIR__ . '/Fake/sql', null, new SqlFileName()));
Execute SQL
/* todo_item_by_id.sql */ SELECT * FROM todo WHERE id = :id
Demo
php demo/run.php