Laravel8 循环迭代中保存Model仅第一条数据生效如何解决
问题修复方案
核心错误原因
- 循环内提前返回:你的
return $result;语句写在了for循环内部,第一次遍历手机号执行完充值逻辑后,就会直接终止程序返回结果,后续的手机号遍历逻辑完全不会执行,这是仅第一个手机号能保存的核心原因。 - 其他潜在错误:
Msisdn::create()传参格式错误:数组第一个元素是无键的字符串'requestType',不符合模型字段赋值的键值对要求,会导致数据写入异常。- 调用
virtual_recharge方法时,$customerMsisdn、$transactionDescription两个变量未定义,运行时会抛出变量未定义的错误。 - 余额校验逻辑错误:原判断公式
($amount * count($phones))-$amount少算了单份金额,比如3个手机号每个5元总消耗应为15元,原公式计算结果为10元,会导致余额校验逻辑失效。 - 每次循环都重复查询、更新余额表,会产生不必要的数据库IO,也存在并发扣减的安全隐患。
修复后代码参考
$product = $request->product; $float = Balance::where('user_id', Auth::user()->id)->first(); $bundleId = $request->bundle; if(is_null($product)){ return "Please select product"; }else if($product ==1){//airtime $amount = $request->amount; $airtimePhoneNumbers = $request->airtimePhoneNumbers; $resultList = []; // 收集所有手机号的充值结果 if(str_contains($airtimePhoneNumbers, ';')){ $phones = explode(';', $airtimePhoneNumbers); $totalAmount = $amount * count($phones); // 修正余额校验逻辑 if($float->float < $totalAmount){ return 'Not enough float to perfom this operation'; }else{ foreach ($phones as $phone) { $rsn = mt_rand(100000000, 999999999); $sessionToken =(string) Str::uuid(); $transactionDescription = 'descripti1on'; Msisdn::create([ 'requestType' => 0, // 补上缺失的键值对 'product'=>$product, 'amount' => $amount, 'sessionToken'=>$sessionToken, 'bundleId'=>'airtime', 'rsn'=>$rsn, 'customerMsisdn'=>$phone, 'trxnDescription'=>$transactionDescription, ]); // 调用充值接口,补上缺失的变量 $result = $this->asapCore->virtual_recharge($amount, $sessionToken, $rsn, $phone, $transactionDescription, $requestType = 0); $resultList[] = [ 'phone' => $phone, 'result' => $result ]; } // 循环结束后统一扣减余额 $float->float = $float->float - $totalAmount; $float->save(); // 所有手机号处理完成后再统一返回结果 return $resultList; } }else{ // 单手机号处理逻辑 } }
额外优化建议
如果担心充值过程中出现异常导致部分成功部分失败,可以引入数据库事务,保证数据一致性:所有手机号写入和余额扣减要么全部成功,要么全部回滚。
内容的提问来源于stack exchange,提问作者kalombo grace
相关产品推荐
相关产品推荐

