本文目录导读:

在 Laravel 中处理外部数据导入,通常需要解决数据获取、数据清洗、批量存储和错误处理这几个核心问题,以下是几种常见的处理方案和最佳实践。
数据获取方式
从文件导入(CSV/Excel)
use Illuminate\Support\Facades\Storage;
use League\Csv\Reader;
// 从上传文件读取CSV
$file = request()->file('import_file');
$csv = Reader::createFromPath($file->getPathname(), 'r');
$csv->setHeaderOffset(0); // 第一行为表头
foreach ($csv as $record) {
// $record 是关联数组
$data[] = [
'name' => $record['Name'],
'email' => $record['Email'],
'created_at' => now(),
'updated_at' => now(),
];
}
从API获取
use Illuminate\Support\Facades\Http;
$response = Http::timeout(30)->retry(3, 100)->get('https://api.example.com/data');
$records = $response->json();
从数据库连接
// 在config/database.php配置外部数据库连接
$users = DB::connection('external_db')
->table('users')
->where('updated_at', '>', $lastSync)
->get();
数据清洗与验证
使用 Laravel Validation
use Illuminate\Support\Facades\Validator;
$rules = [
'*.name' => 'required|string|max:255',
'*.email' => 'required|email|unique:users,email',
'*.age' => 'nullable|integer|min:0|max:150',
];
$validator = Validator::make($data, $rules);
if ($validator->fails()) {
// 收集错误信息
$errors = $validator->errors()->all();
Log::warning('Import validation failed', ['errors' => $errors]);
return back()->withErrors($validator);
}
数据转换与清洗
$cleanData = collect($rawData)->map(function ($item) {
return [
'name' => trim($item['name'] ?? ''),
'email' => strtolower(trim($item['email'] ?? '')),
'amount' => (float) str_replace(['$', ','], '', $item['amount']),
'status' => in_array($item['status'], ['active', 'inactive']) ? $item['status'] : 'unknown',
];
});
批量插入与更新
批量插入(性能最优)
// 每次插入1000条
$chunks = array_chunk($cleanData, 1000);
foreach ($chunks as $chunk) {
User::insert($chunk);
}
UPSERT(存在则更新,不存在则插入)
use Illuminate\Support\Facades\DB;
// 基于email字段判断重复
DB::table('users')->upsert(
$cleanData,
['email'], // 唯一键
['name', 'phone', 'updated_at'] // 需要更新的字段
);
使用 firstOrCreate / updateOrCreate
foreach ($cleanData as $data) {
User::updateOrCreate(
['email' => $data['email']],
$data
);
}
队列处理(推荐大量数据)
创建 Job
// app/Jobs/ImportUsersJob.php
class ImportUsersJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
protected $chunk;
public function __construct(array $chunk)
{
$this->chunk = $chunk;
}
public function handle()
{
DB::transaction(function () {
foreach ($this->chunk as $row) {
User::create($row);
}
});
}
public function failed(\Throwable $exception)
{
Log::error('Import chunk failed', [
'error' => $exception->getMessage(),
'chunk' => $this->chunk
]);
}
}
// 使用
$chunks = array_chunk($cleanData, 500);
foreach ($chunks as $chunk) {
ImportUsersJob::dispatch($chunk);
}
完整示例:CSV导入控制器
<?php
namespace App\Http\Controllers;
use App\Jobs\ImportUsersJob;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Validator;
use League\Csv\Reader;
class ImportController extends Controller
{
public function importUsers(Request $request)
{
// 1. 验证上传文件
$request->validate([
'file' => 'required|file|mimes:csv,txt|max:10240',
]);
try {
// 2. 读取CSV
$csv = Reader::createFromPath($request->file('file')->getPathname(), 'r');
$csv->setHeaderOffset(0);
$records = [];
$errors = [];
$rowNumber = 0;
// 3. 逐行处理
foreach ($csv as $row) {
$rowNumber++;
// 验证单行数据
$validator = Validator::make($row, [
'name' => 'required|string|max:255',
'email' => 'required|email',
]);
if ($validator->fails()) {
$errors[] = "Row {$rowNumber}: " . implode(', ', $validator->errors()->all());
continue;
}
$records[] = [
'name' => trim($row['name']),
'email' => strtolower(trim($row['email'])),
'phone' => $row['phone'] ?? null,
'created_at' => now(),
'updated_at' => now(),
];
}
// 4. 批量导入(使用队列或直接插入)
if (!empty($records)) {
$chunks = array_chunk($records, 500);
foreach ($chunks as $chunk) {
ImportUsersJob::dispatch($chunk);
}
}
// 5. 返回结果
return response()->json([
'success' => true,
'imported' => count($records),
'errors' => $errors,
'message' => count($records) . ' records queued for import'
]);
} catch (\Exception $e) {
Log::error('Import failed: ' . $e->getMessage());
return response()->json([
'success' => false,
'message' => 'Import failed: ' . $e->getMessage()
], 500);
}
}
}
推荐包
- Maatwebsite/Laravel-Excel:强大的Excel/CSV处理,支持导入、导出、批量验证
- Laravel-Excel 导入示例:
use Maatwebsite\Excel\Facades\Excel; use App\Imports\UsersImport;
Excel::import(new UsersImport, $request->file('file'));
## 7. 关键注意事项
1. **内存管理**:大文件使用`lazyCollection()`或队列分块处理
2. **事务处理**:每块数据使用独立事务,避免全部回滚
3. **错误处理**:记录错误行,允许部分导入成功
4. **唯一约束**:使用`upsert`或先查询后插入
5. **性能监控**:记录导入时间、数量、失败原因
6. **用户反馈**:提供进度条或异步通知
##
| 场景 | 推荐方案 |
|------|----------|
| 小数据量 (<1000) | 直接同步插入 + 验证 |
| 中等数据量 (1000-10000) | 批量chunk插入 + 事务 |
| 大数据量 (>10000) | 队列分片处理 + 事务 |
| 需要更新已有数据 | `upsert` 或 `updateOrCreate` |
| 复杂格式(Excel) | Maatwebsite/Laravel-Excel |
选择方案时,根据数据量大小、实时性要求和错误容忍度来决定是同步处理还是异步队列处理。