📝

PHPのアロー関数はLaravelのクエリビルダで活躍する

2023/02/23に公開約4,000字

皆さんPHP7.4から導入されたアロー関数は使っていますか?
最初はイマイチだったんですが使い所が見つかってきたので共有として記事を書きました

アロー関数とは

アロー関数とは、無名関数を短く記述するための構文の一つです

  • function を fn に省略できる
  • use は書かなくてもスコープ外の変数を使える
  • return は書かなくても戻り値を返せる
  • 波括弧 {} は書けない
  • 1行しか書けない

この1行しか書けないというのが致命的に使いづらく、波括弧で複数行書けてもいいじゃん!
って思うんですが、、、残念ながら現状は1行しか書けません
ですがいくつかアロー関数が活躍する場面があります

アロー関数の使い方

例えば、配列から偶数かつ一定の条件を満たす要素を抽出する場合を考えます

$array = [1, 2, 3, 4, 5];
$multiplier = 10;

// 普通に書いた例
$normal = array_filter($numbers, function ($n) use ($multiplier) {
    return $n % 2 === 0 && $n * $multiplier > 20;
});

// アロー関数を使った例
$useArrow = array_filter($numbers, fn ($n) => $n % 2 === 0 && $n * $multiplier > 20);

アロー関数を使用することで、無名関数をより短く簡潔に記述が出来ました

ただ現実的にはどうにも使いづらく、例えばarray_mapのような関数は複数行記述することが多く
残念ながらアロー関数は1行しか書けない制約があるため使用出来ないケースが多いです

しかし大活躍するケースがあります

Laravelのクエリビルダ

以下にクエリビルダでの活用例を掲載します

Laravelのクエリビルダでの活用

クエリビルダでいつ使うの?って話になると思うので特に私がよくアロー関数を使うメソッドです

  • orWhere
  • whereHas
  • join
  • when
  • unless

Laravelのアロー関数を使って、実際の実務でリファクタリングしたコード例を紹介します!
(元のソース乗せるわけにはいかないので原型が残らないレベルに改変しています)
この例では、店舗払いと銀行振込をチェックボックスで絞り込んだ検索について説明しています

クエリに条件を付与するコードがクロージャで複雑化していたのでアロー関数に置き換えました
まずは修正前のコードを見てください

// 修正前のコード
public function scopeSearchPayment($query, $bankTransfer, $shopPayment)
{
    if ($bankTransfer && $shopPayment) {
        return $query->where(function($q) {
            $q->where('is_bank', self::METHOD_BANK_ACTIVE)
                ->where(function($q) {
                    $q->whereHas('shopInfo', function($q) {
                        $q->where('bank_type', ShopInfo::TYPE_OFFICE);
                    })
                    ->orWhereHas('shopTable', function($q) {
                        $q->whereNull('ban_date');
                    });
                });
        })
        ->where(function($q) {
            $q->where('is_local', self::METHOD_LOCAL_ACTIVE)
                ->whereHas('shopInfo', function($q) {
                    $q->where('is_shop_payment', ShopInfo::TYPE_SHOP);
                })
                ->whereHas('shopTable', function($q) {
                    $q->whereNull('ban_date');
                });
        });
    } elseif ($bankTransfer) {
        return $query->where('is_bank', self::METHOD_BANK_ACTIVE)
            ->where(function($q) {
                $q->whereHas('shopInfo', function($q) {
                        $q->where('bank_type', ShopInfo::TYPE_OFFICE);
                    })
                    ->orWhereHas('shopTable', function($q) {
                        $q->whereNull('ban_date');
                    });
            });
    } elseif ($shopPayment) {
        return $query->where('is_local', self::METHOD_LOCAL_ACTIVE)
            ->whereHas('shopInfo', function($q) {
                $q->where('is_shop_payment', ShopInfo::TYPE_SHOP);
            })
            ->whereHas('shopTable', function($q) {
                $q->whereNull('ban_date');
            });
    }
    return $query;
}

これ…読めますか?w
私は途中でアレルギー反応が出そうになります笑
実際のコードはこれより更にぐちゃぐちゃでしたが、もはやネストしすぎてなにがどうなってるか読むのが大変です
(実際は大した条件分岐ではないですが…)

まずは先に結論として修正した後のコードを掲載します

// アロー関数使用バージョン
public function scopeSearchPayment($query, $bankTransfer, $shopPayment)
{
    // 銀行振込にチェックを入れた場合
    $query->when($bankTransfer, fn ($q) => $q
        ->where('is_bank', self::METHOD_BANK_ACTIVE)
        ->where(fn ($q) => $q
            ->whereHas('shopInfo', fn ($q) => $q->where('bank_type', ShopInfo::TYPE_OFFICE))
            ->orWhereHas('shopTable', fn ($q) => $q->whereNull('ban_date'))
        )
    )
    // ショップ支払いにチェックを入れた場合
    ->when($shopPayment, fn ($q) => $q
        ->where('is_local', self::METHOD_LOCAL_ACTIVE)
        ->whereHas('shopInfo', fn ($q) => $q->where('is_shop_payment', ShopInfo::TYPE_SHOP))
        ->whereHas('shopTable', fn ($q) => $q->whereNull('ban_date'))
    );
}

どうですか?これなら簡単ですよね!
このようにLaravelのクエリビルダはメソッドチェーンで書く場合が多いので
改行をしても1行のコードという事になるため、アロー関数が利用可能です

修正点は以下になります

  • 通常のクロージャで書いていた箇所をアロー関数に置き換え
  • if文でelseifで3回条件書いてる…?→2回でイイジャン!
  • どうせならwhen使って1行で書きたいよね

補足:whenはLaravelのCollectionにある
条件がtrueだった場合スコープの中身を実行するメソッドです

以上となります!

Discussion

ログインするとコメントできます