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

Laravel中导入CSV文件后如何更新已存在的数据库记录?

解决Laravel Excel导入CSV时重复记录的更新问题

这个场景我太熟悉了——默认的ToModel逻辑会直接创建新模型,一旦数据库里有重复的唯一字段(比如你的email),就会触发SQL约束错误。我们可以用**Upsert(插入或更新)**逻辑来处理,下面给你两种简单可行的实现方式:

方案一:使用Laravel Excel自带的WithUpserts trait(推荐)

这是最简洁的方式,Laravel Excel专门提供了这个特性来处理重复记录的更新逻辑:

  1. 在导入类中引入WithUpserts trait
  2. 定义uniqueBy方法,指定用来判断记录是否存在的唯一字段(比如email,因为邮箱是用户的天然唯一标识)
  3. 保持model方法不变,Laravel Excel会自动帮你完成“存在则更新,不存在则插入”的操作

修改后的代码如下:

use Maatwebsite\Excel\Concerns\ToModel;
use Maatwebsite\Excel\Concerns\WithCustomCsvSettings;
use Maatwebsite\Excel\Concerns\WithHeadingRow;
use Maatwebsite\Excel\Concerns\WithUpserts;
use Illuminate\Support\Facades\Hash;
use Carbon\Carbon;

class UsersImport implements ToModel, WithCustomCsvSettings, WithHeadingRow, WithUpserts {
    /**
     * 指定判断记录是否存在的唯一字段(支持多字段组合,比如['email', 'phone'])
     */
    public function uniqueBy()
    {
        return 'email';
    }

    /**
     * @param array $row
     *
     * @return \Illuminate\Database\Eloquent\Model|null
     */
    public function model(array $row)
    {
        return new User([
            'firstname' => $row['firstname'],
            'lastname' => $row['lastname'],
            'email' => $row['email'],
            'password' => Hash::make($row['pass']), // 重要:记得加密明文密码,不要直接存储原始值
            'created_at' => Carbon::parse($row['registered']), // 转换为Carbon实例,确保时间格式符合数据库要求
        ]);
    }

    public function getCsvSettings(): array
    {
        return [
            'delimiter' => ","
        ];
    }
}

方案二:手动使用ToCollection+Laravel的upsert方法(更灵活)

如果你需要精细控制要更新的字段(比如不想覆盖已有的created_at时间),可以改用ToCollection接口,手动处理数据后调用Laravel原生的upsert方法:

use Maatwebsite\Excel\Concerns\ToCollection;
use Maatwebsite\Excel\Concerns\WithCustomCsvSettings;
use Maatwebsite\Excel\Concerns\WithHeadingRow;
use Illuminate\Support\Facades\Hash;
use Carbon\Carbon;
use Illuminate\Support\Collection;

class UsersImport implements ToCollection, WithCustomCsvSettings, WithHeadingRow {
    public function collection(Collection $rows)
    {
        // 将CSV行转换为数据库可接受的格式
        $userData = $rows->map(function ($row) {
            return [
                'firstname' => $row['firstname'],
                'lastname' => $row['lastname'],
                'email' => $row['email'],
                'password' => Hash::make($row['pass']),
                'created_at' => Carbon::parse($row['registered']),
            ];
        })->toArray();

        // 调用Laravel原生upsert:参数1是数据,参数2是唯一判断字段,参数3是需要更新的字段
        User::upsert(
            $userData,
            ['email'], // 用邮箱判断记录是否存在
            ['firstname', 'lastname', 'password'] // 仅更新这些字段,created_at不会被修改
        );
    }

    public function getCsvSettings(): array
    {
        return [
            'delimiter' => ","
        ];
    }
}

关键注意事项

  • 唯一索引:确保你的email字段在数据库表中设置了唯一索引,否则upsert逻辑无法准确判断记录是否存在
  • 密码安全:绝对不要直接存储CSV里的明文密码,必须用Hash::make()加密后再存入数据库
  • 时间格式:created_at字段要转换为Carbon实例,避免因格式不兼容导致的数据库错误
  • 字段控制:方案一默认会更新所有字段;如果需要保留某些字段的原有值,方案二更适合你

内容的提问来源于stack exchange,提问作者Abdo Rabah

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 02:57:44