yuki777 / Ray.MediaQuery

Media query object

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Ray.MediaQuery

Continuous Integration

日本語 (Japanese)

Overview

Ray.QueryModule makes a query to an external media such as a database or Web API with a function object to be injected.

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.

Composer install

$ composer require ray/media-query 1.x-dev

Usage

Define an interface for media access by adding the attribute DbQuery to the method and specifying the SQL ID.

interface TodoAddInterface
{
    #[DbQuery('user_add'), Transactional]
    public function __invoke(string $id, string $title): void;
}
interface TodoItemInterface
{
    /**
     * @return array{id: string, title: string}
     */
    #[DbQuery('user_item')]
    public function __invoke(string $id): array;
}

Specify the query interface and install the module.

protected function configure(): void
{
    $mediaQueries = [
        TodoAddInterface::class,
        TodoItemInterface::class, TodoItemInterface::class,
    ];
    $this->install(new MediaQueryModule('sqlite::memory:', $sqlDir, $mediaQueries));
}

You don't need to provide any implementation classes. It will be generated and injected.

class Todo
{
    public function __construct(
        private TodoAddInterface $todoAdd,
        private TodoItemInterface $todoItem
    ) {}

    public function add(string $id, string $title): void
    {
        ($this->todoAdd)($id, $title);
    }

    public function get(string $id): array
    {
        return ($this->todoItem)($id);
    }
}

SQL execution is mapped to a method, and the SQL specified by ID is bound to the method argument and executed. For example, if ID is specified as todo_item, todo_item.sql SQL statement will be executed with ['id => $id] bound.

  • Prepare each SQL in $sqlDir/ directory, $sqlDir/todo_add.sql if ID is todo_add. If the ID is todo_add, the file is $sqlDir/todo_add.sql. * Add a postfix of item if the SQL execution returns a single row, or list if it returns multiple rows.
  • The SQL file can contain multiple SQL statements. * The SQL file can contain multiple SQL statements, where the last line of the SELECT is the result of the execution.

Pagination

The #[Pager] annotation allows you to paginate a SELECT query.

interface TodoList
{
    #[DbQuery, Pager(perPage: 10, template: '/{?page}')]]
    public function __invoke(): Pages
    {
    }
}

You can get the number of pages with count(), and you can get the page object with array access by page number. Pages is a SQL lazy execution object.

$pages = ($todoList)();
$cnt = count($page); // count SQL is generated and queried when count() is done.
$page = $pages[2]; // When array access is done, DB query for that page is done.

// $page->data // sliced data
// $page->current;
// $page->total
// $page->hasNext
// $page->hasPrevious
// $page->maxPerPage;
// (string) $page // pager html

SqlQuery

SqlQuery executes SQL by specifying the ID of an SQL file. It can be used to prepare implementation classes for detailed implementation.

class TodoItem implements TodoItemInterface
{
    public function __construct(
        private SqlQueryInterface $sqlQuery
    ){}

    public function __invoke(string $id) : array
    {
        return $this->sqlQuery->getRow('todo_item', ['id' => $id]);
    }
}

Get* Method

To get the SELECT result, use get* method depending on the result you want to get.

$sqlQuery->getRow($queryId, $params); // Result is a single row
$sqlQuery->getRowList($queryId, $params); // result is multiple rows
$statement = $sqlQuery->getStatement(); // Retrieve the PDO Statement
$pages = $sqlQuery->getPages(); // Get the pager

Ray.MediaQuery contains the Ray.AuraSqlModule. If you need more lower layer operations, you can use Aura.Sql's Query Builder or Aura.Sql which extends PDO. doctrine/dbal is also available.

Profiler

Media accesses are logged by a logger. By default, a memory logger is bound to be used for testing.

public function testAdd(): void
{
    $this->sqlQuery->exec('todo_add', $todoRun);
    $this->assertStringContainsString('query:todo_add({"id": "1", "title": "run"})', (string) $this->log);
}

Implement your own MediaQueryLoggerInterface and run You can also implement your own MediaQueryLoggerInterface to benchmark each media query and log it with the injected PSR logger.

About

Media query object

License:MIT License


Languages

Language:PHP 100.0%