BeMart

G-24 — SQL境界はRay.MediaQuery interface + SQLファイルにする

G-24 — SQL境界はRay.MediaQuery interface + SQLファイルにする

Context

BeMartのEC-CUBE移植では、既存のPhase 2 SQL実装に Sql*Query / Sql*Command のPHP実クラスとPDO prepared statementが存在する。これは既存分として後でまとめて移行する。

今後の新規SQL境界では、PHP実クラスにPDOクエリを書かず、Ray.MediaQueryでQuery/Command interfaceを実装する。

Problem

新規Query/CommandごとにPHP実クラスでPDOを直接書くと、以下が起きる。

Solution

新規SQL境界は Ray.MediaQuery を使う。

Code example

use Ray\MediaQuery\Annotation\DbQuery;
use Ray\MediaQuery\Annotation\Pager;
use Ray\MediaQuery\Pages;

interface AdminProductMediaQuery
{
    #[DbQuery('admin_product_find', type: 'row')]
    public function find(string $productCode): ?AdminProductRow;

    /** @return array<AdminProductRow> */
    #[DbQuery('admin_product_list')]
    public function list(int $limit, int $offset): array;

    #[DbQuery('admin_product_update')]
    public function update(
        string $productCode,
        string $productName,
        int $price02,
    ): void;
}

sql/admin_product_find.sql:

SELECT
  p.product_code,
  p.name,
  pc.price02
FROM dtb_product p
JOIN dtb_product_class pc ON pc.product_id = p.id
WHERE p.product_code = :productCode

Module wiringは MediaQuerySqlModule(interfaceDir: ..., sqlDir: ...) と AuraSqlModule(...) をinstallする。

Anti-pattern

final class SqlProductQuery implements ProductQueryInterface
{
    public function __construct(private PDO $pdo) {}

    public function find(string $productCode): ?ProductEntity
    {
        $stmt = $this->pdo->prepare('SELECT ... WHERE product_code = :productCode');
        $stmt->execute(['productCode' => $productCode]);
        // hydration...
    }
}

これは既存分には残っているが、新規追加は禁止する。既存 Sql* 実装は別フェーズでまとめてRay.MediaQueryへ移行する。

Where this matters

これらの新規SQLは Fake → EC-CUBEスキーマ照合 → Ray.MediaQuery SQL → Resource/Form → Twig/Browser の順で作る。