🫠

【Laravel】無名関数についてまとめてみた

2023/08/03に公開

はじめに

Laravelで関数を書いているときに、親スコープの変数を使うとエラーが出るタイミングがあったので調べてみました。

事象

$maxAge =20;

$query->join('members', function ($join) {
    $join->on('families.id', '=', 'members.family_id')
        ->where('members.age', '<', $maxAge); // ここで親スコープの変数($maxAge)が使えない
})

// PHP Notice:  Undefined variable: ・・・

原因

上記で言うサブクエリ部分は無名関数にあたるので、直接親スコープの変数を使うことができない

解決方法

引き継ぐ変数は、use で渡す必要がある。
これはLaravelの仕様ではなく、PHP自体の仕様。

$maxAge =20;

$query->join('members', function ($join) use ($maxAge ) {
    $join->on('families.id', '=', 'members.family_id')
        ->where('members.age', '<', $maxAge); // 使える!
})

use は定義された時点の変数を参照することに注意する。

$maxAge =20;

$query->join('users', function ($join) use ($maxAge ) {
    $maxAge = 50;
    $join->on('families.id', '=', 'users.family_id')
        ->where('users.age', '<', $maxAge); // $maxAge = 20;
})

上記を回避するには参照渡しにする必要がある。
ただし、次回 $maxAge に別の値が代入されると挙動が変わってしまうので、unset()で変数の割当を解除しておくと事故が少ない。

// unsetを使用しない場合
$maxAge =20;

$query->join('members', function ($join) use (&$maxAge ) {
    $maxAge = 50;
    $join->on('families.id', '=', 'members.family_id')
        ->where('members.age', '<', $maxAge); // $maxAge = 50;
})

// $maxAge = 50
// unsetを使用した場合
$maxAge =20;

$query->join('members', function ($join) use (&$maxAge ) {
    $maxAge = 50;
    $join->on('families.id', '=', 'members.family_id')
        ->where('members.age', '<', $maxAge); // $maxAge = 50;
        unset($maxAge);
})

// $maxAge = 20;

ちなみに、1行の無名関数であればアロー関数を使って書くことも可能。(PHP 7.4で追加)
アロー関数なら親スコープの変数も自動で認識してくれるので、場合によってはこちらの方がスッキリ書けることもある。

$maxAge =20;

$query->join('members', fn ($join) => $join->on('families.id', '=', 'members.family_id')
        ->where('members.age', '<', $maxAge));

クエリビルダの場合はアロー関数のほうが見やすいかもしれない。
https://zenn.dev/noxis/articles/6868409eb7eba5

おまけ

Laravelで無名関数が使われる例

1. ルートの定義

Route::get('/', function () {
    return view('welcome');
});

2. マイグレーションでテーブルを作成する

public function up()
{
    Schema::create('tasks', function (Blueprint $table) {
        $table->id();
        $table->string('name');
        $table->timestamps();
    });
}

3. サブクエリ

$query ->join('children', function ($join) use ($maxAge) {
     $join->on('users.id', '=', 'children.user_id')
     ->where('children.age', '<' , $maxAge)
 })

まとめ

普段何も考えずに使っているとそのエラーが出たときに対処法がわからなくなるので、きちんと理解することが大切だと思いました。
参照渡しは事故が起きそうなのでなるべく使いたくないです。
とりあえず無名関数でuseを使っているクエリビルダをアロー関数に書き換えようと思いました。

参考記事

https://zenn.dev/ikeo/articles/c8628058a03345c71a7b
https://qiita.com/westhouse_k/items/fe527b59146739cf7af3#まとめ

Discussion