🙆

Laravel 今までのローカルスコープの書き方と今後のモダンな書き方

2022/12/08に公開

前書き

Laravel のローカルスコープの書き方について、今後はこんな風になるのではないか、と勝手に予想する小ネタ的な記事です😅 今までの書き方が無効になるとかいう訳では無く、スタイル的な話です。また、記事後半では、少し話が Larastan に逸れていきます。

本題

Laravel のローカルスコープと言えば、現在、一番よく見る書き方と言えば、こんな感じかと思います。

class User extends Model
{
    public function scopePopular($query)
    {
        return $query->where('votes', '>', 100);
    }
}

特に問題無いですね。ですがここ最近、海外のサイトなどを見ていると、こんな会話も見られるようになりました。

A 「ここの return 文って、return する必要無くない?」
B 「んだな~(そうだね)」

ということで、return は、カットしてしまいましょう。

進化形1、return はカット

class User extends Model
{
    public function scopePopular($query)
    {
        $query->where('votes', '>', 100);
    }
}

Laravel 7 のドキュメントでは、return 付きでしたが、現在の Laravel 8&9では、return ありと無しの両方が併記されています。ですが、Laravel 10 のドキュメントでは、return 無しに統一される予定です。

まぁ、return があると何か精神的に落ち着く所はあるかも知れませんが、内部的には、$query を return してもしなくてもどっちでも良いようにできています。(オブジェクトは参照渡しだし)

進化形2、型を書く

Laravel 10 から、かなり型を意識させる感じになっています。ということで、今後は下記の書き方が流行って行くと思われます。実際、下記が Laravel 10 のドキュメントに記載される予定です。

use Illuminate\Database\Eloquent\Builder;

class User extends Model
{
    public function scopePopular(Builder $query): void
    {
        $query->where('votes', '>', 100);
    }
}

Eloquent\Builder って、何?となりそうですね。簡単な説明としては、Laravel には、Collection とそれを拡張した Eloquent 用の Eloquent Collection があるように、クエリ文を構築してくれる Query\Builder を元にした Eloquent 用の Eloquent\Builder がある、って感じですね。

進化形3、Larastan も考慮

昨今流行りつつある静的解析の Larastan(PHPStan)のレベル 6~9(執筆現在)を満たそうとすると更に記述が必要になり、下記のようになります。

use Illuminate\Database\Eloquent\Builder;

class User extends Model
{
+   /**
+    * @param  Builder<User>  $query
+    */
    public function scopePopular(Builder $query): void
    {
        $query->where('votes', '>', 100);
    }
}

ふ~ん、って感じになってきましたが、上記で EloquentBuilder と User モデルが紐付く事を教えてやると、Larastan がエラーを出さなくなります。
そうでないと、下記のようなエラーが出ます。

Method App\Models\User::scopePopular() has parameter $query with generic class Illuminate\Database\Eloquent\Builder but does not specify
its types: TModelClass
💡 You can turn this off by setting checkGenericClassInNonGenericObjectType: false in your phpstan.neon.

このエラーが出ないように設定する事も可能な一方、敢えて上記のように書くと何か良くなる事があるのでしょうか? (・_・")?

良くなる事

ありました。Larastan のまだベータとされる checkModelProperties という機能をオンにしてみます。

phpstan.neon
parameters:
    checkModelProperties: true

そして、例えば手が滑って votes と書く所を votes999 と書いてしまったとします。

    /**
     * @param  Builder<User>  $query
     */
    public function scopePopular(Builder $query): void
    {
        $query->where('votes999', '>', 100);
    }

そうすると、Larastan 先生が、

Property 'votes999' does not exist in App\Models\User model.

とエラーを表示してくれるようになります。

Larastan は、User モデルと紐付くと分かった為、User 用のマイグレーションを調べ、「そんなフィールド無いよ!」と教えてくれている訳です。これはちょっと嬉しいですね。

ちなみに、マイグレーションファイルのみ更新しても、キャッシュを見てしまう場合は、--debug オプションを付けて1度実行するといいです。

後書き

流石に進化形3になると「ちょっと小面倒だな…。もう AI がやってくれよ! ヽ(´・д・`)ノ」と思う所もあります😅

間違い等ありましたらコメント下さい🙇‍♂️

Discussion