You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.27 15:09:10