Help us understand the problem. What is going on with this article?

BEAR.SundayとSQLと

この記事はBEAR.Sunday Advent Calendar 2019の18日目の後出し記事です。
BEAR.SundayとDBの間にあるものと便利機能を調べました。

BEAR.SundayとSQL

BEAR.Sundayのチュートリアル2でも紹介されていますが、以下のように設定することで、生のSQLを記述したファイルを扱えるようになります。

src/Module/AppModule.php
class AppModule extends AbstractAppModule
{
    /**
     * {@inheritdoc}
     */
    protected function configure()
    {
        $appDir = $this->appMeta->appDir;
        require_once $appDir . '/env.php';
        $this->install(
            new AuraSqlModule(
                (string) getenv('DB_DSN'),
                (string) getenv('DB_USER'),
                (string) getenv('DB_PASS')
            )
        );
        $sqlDir = $appDir . '/var/sql';
        $this->install(new SqlQueryModule($sqlDir));
        $this->install(new QueryLocatorModule($sqlDir));

        // 略
    }

SELECT

SELECT文を書いてパラメータを渡してクエリを実行するのもQueryアノテーションを使うことで簡単に実現できます。
一行取得するときは、

src/Resource/App/Hoge.php
namespace MyAPP\Api\Resource\App;

use BEAR\Resource\Annotation\JsonSchema;
use BEAR\Resource\ResourceObject;
use Ray\Query\Annotation\Query;

class Hoge extends ResourceObject
{
    /**
     * @JsonSchema(schema="hoge.json")
     * @Query("hoge_by_id?hoge_id={hogelId}", templated=false, type="row")
     */
    public function onGet(string $hogelId) : ResourceObject
    {
        return $this;
    }
}

var/sql/hoge.sql
SELECT
    h.id,
    hd.name,
    hd.name_latin,
    hd.postal_code,
    hd.address,
    hd.email,
    hd.telephone
FROM hoge AS h
    INNER JOIN hoge_detail AS hd
    ON h.id = hd.hoge_id
WHERE h.id = :hoge_id


のようにかけます。
複数行取得する場合はNamedアノテーションを使ってコンストラクタで設定もできます。

src/Resource/App/HogeList.php
namespace MyAPP\Api\Resource\App;

use BEAR\Resource\Annotation\JsonSchema;
use BEAR\Resource\ResourceObject;
use Koriym\HttpConstants\StatusCode;
use Ray\Di\Di\Named;
use Ray\Query\RowListInterface;

class HogeList extends ResourceObject
{
    /**
     * @var RowListInterface
     */
    private $hogeList;

    /**
     * @Named("hogeList=hoge_list")
     */
    public function __construct(
        RowListInterface $hogeList
    ) {
        $this->hogeList = $hogeList;
    }
    // 略
}

var/sql/hoge_list.sql
SELECT h.id, hd.name, hd.age, hd.nationality
   FROM hoge AS h
   INNER JOIN hoge_detail AS hd
   ON h.id = hd.hoge_id
WHERE h.id = :hoge_id

INSERT/UPDATE/DELETE

複数行のSELECT同様、Namedアノテーションを使ってコンストラクタで設定します。

src/Resource/App/HogeDetail.php
namespace MyApp\Api\Resource\App;

use BEAR\Resource\ResourceObject;
use Ray\Di\Di\Named;

class HogeDetail extends ResourceObject
{
    /**
     * @var callable
     */
    private $deleteHogeDetail;

    /**
     * @Named("deleteHogeDetail=delete_hoge_detail")
     */
    public function __construct(
        callable $deleteHogeDetail
    ) {
        $this->deleteHogeDetail = $deleteHogeDetail;
   }

    public function onPost() : ResourceObject
    {
        // 略
        ($this->deleteHogeDetail)(
            [
                'hoge_ids' => implode(',', $hogeIds)
            ]
        );

        $this->code = 204;

        return $this;
    }
}

var/sql/delete_hoge_detail.sql
DELETE FROM hoge_detail WHERE hoge_detail.hoge_id in (:hoge_ids);

業務要件が込み入ってきた場合に、複数のテーブルへの書き込み/削除をいっぺんにやりたくなることは、往々にしてあることです。
それぞれのテーブルに対する操作を別々のファイルにしても良いのですが、ある程度正規化されている場合には呼び忘れが発生する可能性があります。
PL/SQLとまではいきませんが、BEAR.Sundayでも複数のDML(CUD)を1ファイルで記述できます。

src/Resource/App/HogeDetail.php
namespace MyApp\Api\Resource\App;

use BEAR\Resource\ResourceObject;
use Ray\Di\Di\Named;

class HogeDetail extends ResourceObject
{
    /**
     * @var callable
     */
    private $deleteHogeDetailAndMemo;

    /**
     * @Named("deleteHogeDetailAndMemo=delete_hoge_detail_and_hoge_memo")
     */
    public function __construct(
        callable $deleteHogeDetail,
    ) {
        $this->deleteHogeDetailAndMemo = $deleteHogeDetailAndMemo;
   }

    public function onPost() : ResourceObject
    {
        // 略
        ($this->deleteHogeDetailAndMemo)(
            [
                'hoge_ids' => implode(',', $hogeIds)
            ],
            [
                'hoge_ids' => implode(',', $hogeIds)
            ]
        );

        $this->code = 204;

        return $this;
    }
}

var/sql/delete_hoge_detail_and_hoge_memo.sql
DELETE FROM hoge_detail WHERE hoge_detail.hoge_id in (:hoge_ids);
DELETE FROM hoge_memo WHERE hoge_memo.hoge_id in (:hoge_ids);

BEAR.Sundayとリポジトリパターン

BEAR.Sunday チュートリアル1に紹介されている通り、クエリビルダーも使えます。

と、ここまで書いてふと、リポジトリパターンで言われている利点がリポジトリなしで実現できているのではないかと思ったのです。

リポジトリによって隠蔽されたSQLのオプティマイズや挙動を調べるのに、PHPならvar_dump($queryBuilder->getQuery()->getSQL());、Railsならrecord.to_sqlのような「実際にどんなSQLが発行されているか確認する」ということをしていませんか?(私はしょっちゅうしています
はて、隠蔽されたことで果たして本当に開発が楽になっているのだろうか。何のためのリポジトリという道具なのだろうか。

そんなことを改めて考えさせられるきっかけになったBEAR.SundayとSQLについてでした。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした