13
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

BEAR.SundayAdvent Calendar 2019

Day 18

BEAR.SundayとSQLと

Last updated at Posted at 2019-12-24

この記事は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についてでした。

13
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?