Laravel 9 + PHP 8.1环境下使用maatwebsite/excel 3.1实现Excel导入后获取已保存数据ID并通过ID更新行的技术问询
获取Excel导入后模型的ID解决方案
你遇到的问题本质是:当使用WithBatchInserts和ToModel接口时,maatwebsite/excel会执行批量插入SQL,这种方式下Laravel无法自动为每个模型实例填充数据库生成的ID(因为批量插入仅返回最后一条插入记录的ID,而非所有行)。下面提供两种可行的解决方案,你可以根据数据量大小选择:
方案一:逐个保存模型(适合小数据量)
这种方式放弃批量插入,直接在model方法中调用create()保存模型,这样每个模型实例会自动携带数据库生成的ID。
修改导入类代码
use Illuminate\Support\Collection; use Maatwebsite\Excel\Concerns\ToModel; use Maatwebsite\Excel\Concerns\WithHeadingRow; use Maatwebsite\Excel\Concerns\WithValidation; class BankTransfersHistoryImport implements ToModel, WithHeadingRow, WithValidation { use Importable; private $rows; public function __construct() { $this->rows = collect(); } /** * @param array $row * @return \Illuminate\Database\Eloquent\Model|null */ public function model(array $row) { // 直接使用create()保存,而非新建未保存的模型 $bankTransferHistory = BankTransfersHistory::create([ 'loanId' => $row['loanId'], 'actionDate' => transformDate($row['actionDate']), 'worth' => $row['worth'], // 其他字段 ]); $this->rows->push($bankTransferHistory); return $bankTransferHistory; } public function getImportedData(): Collection { return $this->rows; } public function headingRow(): int { return 2; } public function rules(): array { return [ '*.loanId' => ['required', 'numeric'], // 其他验证规则 ]; } }
调整控制器代码
移除重复的toCollection()调用,只保留一次import()即可:
public function store(Request $request) { $request->validate([ 'file' => 'required|mimes:xls,xlsx', ]); $file = $request->file('file'); $import = new BankTransfersHistoryImport; try { $import->import($file); // 现在getImportedData返回的集合包含带ID的模型实例 $importedData = $import->getImportedData(); // 直接通过模型ID进行后续更新操作 $importedData->each(function ($item) { // 示例:更新offerId字段 $item->update(['offerId' => $yourOfferId]); }); return response()->json([ "message" => "导入成功", "data" => $importedData ]); } catch (\Maatwebsite\Excel\Validators\ValidationException $e) { $failures = $e->failures(); // 错误处理逻辑不变 return response()->json($failures, 422); } }
方案二:批量插入后查询ID(适合大数据量)
如果数据量很大,批量插入的性能优势更重要,可以先批量插入数据,再通过唯一标识(loanId + actionDate)查询获取所有插入记录的ID。
修改导入类代码
改用ToCollection接口手动处理批量插入:
use Illuminate\Support\Collection; use Illuminate\Support\Facades\DB; use Maatwebsite\Excel\Concerns\ToCollection; use Maatwebsite\Excel\Concerns\WithHeadingRow; use Maatwebsite\Excel\Concerns\WithValidation; class BankTransfersHistoryImport implements ToCollection, WithHeadingRow, WithValidation { use Importable; private $importedModels; public function __construct() { $this->importedModels = collect(); } public function collection(Collection $rows) { // 准备批量插入的数据 $insertData = $rows->map(function ($row) { $actionDate = transformDate($row['actionDate']); return [ 'loanId' => $row['loanId'], 'actionDate' => $actionDate, 'worth' => $row['worth'], 'created_at' => now(), 'updated_at' => now(), // 其他字段 ]; })->toArray(); // 执行批量插入 BankTransfersHistory::insert($insertData); // 通过唯一组合查询所有插入的记录 $uniquePairs = $rows->map(function ($row) { $actionDate = transformDate($row['actionDate'])->format('Y-m-d H:i:s'); // 匹配数据库日期格式 return [$row['loanId'], $actionDate]; }); $this->importedModels = BankTransfersHistory::whereIn( DB::raw('(loanId, actionDate)'), $uniquePairs->toArray() )->get(); } public function getImportedData(): Collection { return $this->importedModels; } public function headingRow(): int { return 2; } public function rules(): array { return [ '*.loanId' => ['required', 'numeric'], // 其他验证规则 ]; } }
控制器代码调整
同样移除重复的toCollection()调用,逻辑和方案一一致,getImportedData()会返回带ID的模型集合。
注意事项
- 方案一中,逐个保存会产生更多的数据库查询,数据量超过1000条时建议使用方案二。
- 方案二中,确保
loanId + actionDate是唯一组合,否则可能查询到非本次插入的记录。如果没有唯一约束,建议先清理重复数据再导入。
内容的提问来源于stack exchange,提问作者yakov
相关产品推荐
相关产品推荐

