Laravel 10控制器闭包中访问全局变量$new_period报错排查
问题:Laravel 10控制器change方法报错“Attempt to read property "id" on null”
控制器中的change方法代码如下:
public function change($id) { DB::beginTransaction(); try{ $current_period=Period::where('current',true)->first(); $current_period->current=false; $current_period->save(); $new_period=Period::findOrFail($id); $new_period->current=true; $new_period->save(); //set person's memberSince and pay_mode to what is in the person_period pivot table $persons=Person::all(); $persons->each(function ($person,$key){ global $new_period; //dd($new_period->id); $pivot=$person->periods()->where('period_id',$new_period->id)->first(); $person->memberSince=$pivot? $pivot['memberSince']:null; $person->pay_mode=$pivot?$pivot['pay_mode']:null; $$person->save(); }); DB::commit(); $result=[]; $ps=PersonResource::collection(Person::all()); array_push($result, $ps); $pr=PeriodResource::collection(all()); array_push($result,$pr); return $result; }catch (\Exception $e) { DB::rollBack(); throw $e; } }
执行时返回错误:
message 'Attempt to read property "id" on null'
错误出现在//dd($new_period->id)的下一行,取消注释该行后仍报相同错误,但在each循环外部执行dd($new_period->id)可正常返回$new_period的正确ID,确认变量在外部存在。
原因与解决方案
1. 闭包变量传递错误
PHP闭包默认不会继承父作用域的局部变量,你用global $new_period尝试获取变量是错误的——global用于访问全局命名空间的变量,而$new_period是控制器方法内的局部变量,并非全局变量,因此闭包里的$new_period实际为null,读取id时就会触发报错。
正确的做法是通过use关键字将父作用域的$new_period显式传入闭包:
$persons->each(function ($person,$key) use ($new_period) { // ... 内部逻辑 });
2. 代码笔误
代码中$$person->save();多了一个$,属于变量变量语法,会将$person的实例值当作变量名去访问,这显然不符合需求,应该改为$person->save();。
3. 额外的语法错误
PeriodResource::collection(all());中的all()没有指定模型,会调用全局的all()函数(实际不存在),应该改为PeriodResource::collection(Period::all());。
修正后的核心代码片段
$persons=Person::all(); $persons->each(function ($person,$key) use ($new_period) { $pivot=$person->periods()->where('period_id',$new_period->id)->first(); $person->memberSince=$pivot? $pivot['memberSince']:null; $person->pay_mode=$pivot?$pivot['pay_mode']:null; $person->save(); }); DB::commit(); $result=[]; $ps=PersonResource::collection(Person::all()); array_push($result, $ps); $pr=PeriodResource::collection(Period::all()); array_push($result,$pr); return $result;
内容的提问来源于stack exchange,提问作者Meaulnes
相关产品推荐
相关产品推荐

