在 Laravel 中,从 400 行的 Import Controllers 到 20 行的 Configs

发布: (2026年2月5日 GMT+8 06:35)
6 min read
原文: Dev.to

Source: Dev.to

《从400行导入控制器到20行配置》在 Laravel 中的封面图片

Robin

我们都熟悉的“导入噩梦”

如果你用 Laravel 构建过业务应用,你一定收到过这样的需求:

“作为管理员,我需要上传一个包含 50,000 条商品的 CSV,以更新我们的库存水平。”

我在不同项目中至少看到过十几次同样的请求。听起来很简单,于是你会挑一个 CSV 解析库——比如 league/csvmaatwebsite/excel——然后开始编写控制器。

十分钟后,你已经陷入细节之中:

  • “如何在不耗尽内存的情况下验证第 49,000 行?”
  • “客户把列叫‘E‑Mail’,但有时又叫‘Email Address’。”
  • “我需要通过名称查找分类 ID,如果不存在就创建它。”
  • “客户想要一个‘Dry Run’,在正式提交前先看到错误。”

你的控制器最终会变成一个 400 行的怪物,里面充斥着 while 循环、try‑catch 块以及手写的验证逻辑。它难以测试,难以阅读,且在重构时让人望而生畏。

一定有更好的办法。

介绍 Laravel Ingest

我创建了 Laravel Ingest 来终止这种混乱。

Laravel Ingest 是一个 基于配置的框架,用于数据导入。与其编写过程式脚本,你只需定义 要导入什么,包会处理 如何导入

它负责繁重的工作:

  • 流式处理 & 队列: 零内存问题,无论是 100 行还是 100 万行。
  • 映射 & 转换: 流畅的 API,将 CSV 列映射到 Eloquent 属性。
  • 关系: 自动解析 BelongsToBelongsToMany 关联。
  • 干运行: 模拟导入以发现错误,且不触及数据库。
  • API & CLI: 自动生成的端点和 Artisan 命令。

要求

  • PHP 8.3+
  • Laravel 10、11 或 12

工作原理

假设我们想要导入用户。我们不使用控制器,而是创建一个 Importer 类

1. 声明式配置

fromSource(SourceType::UPLOAD)
    // 通过 email 标识记录
    ->keyedBy('email')
    // 如果 email 已存在,更新记录
    ->onDuplicate(DuplicateStrategy::UPDATE)
    // 将 CSV 中的 'Full Name' 映射到数据库的 'name'
    ->map('Full Name', 'name')
    // 将 CSV 中的 'E‑Mail'(或 'Email')映射到数据库的 'email'
    ->map(['E‑Mail', 'Email'], 'email')
    // 将 'yes/no' 字符串转换为布尔值
    ->mapAndTransform('Is Admin', 'is_admin', fn($val) => $val === 'yes')
    // 为每行使用 Laravel 验证规则
    ->validate([
        'Full Name' => 'required|string|min:3',
        'E‑Mail'    => 'required|email',
    ]);

2. 注册 Importer

AppServiceProvider 中,只需给它打上标签:

$this->app->tag([UserImporter::class], 'ingest.definition');

3. 运行导入

你不需要为上传编写控制器。Laravel Ingest 会自动提供 API 端点和 Artisan 命令。

通过 CLI(适用于定时任务或 S3 导入):

php artisan ingest:run user-importer --file=users.csv

通过 API(供前端使用):

POST /api/v1/ingest/upload/user-importer
Body: file=@users.csv

杀手特性

1. 处理关联终于变得简单

通常,导入关联数据(例如通过名称将产品分配到类别)需要繁琐的查找逻辑。Ingest 只需一行代码即可实现:

->relate(
    sourceColumn: 'Category Name',
    relation: 'category',
    relatedModel: Category::class,
    lookupColumn: 'name',
    createIfMissing: true
)

2. 开箱即用的干运行

客户讨厌导入在中途失败的情况。使用 Ingest,您可以触发一次模拟,运行所有验证和转换,但会回滚更改:

php artisan ingest:run user-importer --file=users.csv --dry-run

3. 错误分析 API

当行导入失败时,您不仅仅会得到日志文件。Ingest 会在数据库中记录失败的行,并提供一个端点下载仅包含失败行的 CSV,其中包括错误信息。用户可以在 Excel 中修复错误后,仅重新上传这些行。

底层原理

Laravel Ingest 站在巨人的肩膀上。它使用 spatie/simple-excel 逐行流式读取文件(保持内存使用平稳),并将行块推送到 Laravel 队列。这意味着即使进行大规模导入,您的应用仍然保持响应。

即使导入 500 MB 的文件时也是如此。

开始使用

安装非常简单:

composer require zappzerapp/laravel-ingest
php artisan vendor:publish --provider="LaravelIngest\IngestServiceProvider"
php artisan migrate

资源

总结

我创建这个包是因为我厌倦了在每个项目中编写相同脆弱的导入代码。如果你曾经对 “CSV 上传” 这几个字感到畏惧,试试 Laravel Ingest 吧。

觉得它有用吗?在 GitHub 上给仓库加星,如果有功能需求请提交 issue,或者在下方留下评论。我很想了解你是如何使用它的!

Back to Blog

相关文章

阅读更多 »