Laravel 学習中だがモデルに関して感じたことを書く。

xxxRaw メソッド

O/R マッパーは確かに便利なのだがちょっと凝ったことをしようとすると SQL だと簡単に書けるのに O/R マッパーでどういう風に表現するのか分からずに実装できないという問題がある。 CakePHP などは顕著な問題で特にアソシエーションなどを使用すると正しいはずなのに何故か動かないというコードになりがちだ。 しかも COUNT(*)SUM(*) を書きたいだけでも $query->func()->xxx() などという訳のわからない記法を要求される。 これが覚えられない。

Laravel だと DB::raw(), selectRaw()whereRaw(), orderByRaw() などの低レベルに書けるタイプのメソッドが用意されており、これを使うと対象部分だけ SQL 直で書くことができる。 単純な機構だがとても便利だ。 例えば以下のような感じだ:

/**
 * 年月ごとの記事数を返却する.
 *
 * @return \Illuminate\Http\JsonResponse
 */
public function countYearMonth(): JsonResponse
{
    $posts = Post::query()
        ->groupBy(['year', 'month'])
        ->orderByDesc('year')
        ->orderByDesc('month')
        ->get(['year', 'month', DB::raw('COUNT(id) AS count')]); // DB:raw() で SQL 直書き
    return Response::json($posts);
}

これは覚えやすい。 「何故既存の O/R マッパーは使いにくいのか」がよく分かっている人が設計した感じがする。

何もしないと取得結果がすべて string になってしまう

クエリを投げて DB からモデルに取得した結果が INTEGER カラムであったとしてもすべて string になってしまう。 これだと毎回変換しなければならず使いにくい。 これに関しては【Laravel】DBから取得した値を$castsで型変換する【Attribute Casting】に記載がある通り、モデルに型変換したいカラムを明示的に指定する。

class Post extends Model
{
    use SoftDeletes;

    // このように定義しておけば year, month, day, count は取得時に integer にキャストされる
    protected $casts = ['year' => 'integer', 'month' => 'integer', 'day' => 'integer', 'count' => 'integer'];
}

上記の例で、例えば countposts テーブル内に存在しないカラムだったとしても問題ないし、DB::raw('COUNT(id) AS count') で取った結果がちゃんと integer で返ってくる。 この辺りは便利なのか不便なのかちょっとよく分からなかった。