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

如何在Laravel的FormRequest中实现CSV逐行验证替代Service类?

背景

我已开发完成招聘媒体管理后台,为实现导入CSV文件的逐行验证,当前采用了Service类的实现方式,具体代码如下:

视图(View)

<div id="main">
        <h1 id="page_title">求人票一覧</h1>
        @if ($errors->get('upload_errors'))
            @foreach ($errors->get('upload_errors') as $error)
                <li class="error_text" style="color: #f44336;">{{ $error }}</li>
            @endforeach
        @endif
</div>

控制器(Controller)

use App\Imports\JobsImport;
use Illuminate\Http\Request;
use Maatwebsite\Excel\Facades\Excel;

class JobsController extends \App\Http\Controllers\Controller
{
  public function __construct(
          JobService $jobService,
      ) {
          $this->jobService = $jobService;
      }

  public function import(Request $request)
      {
          $csv = $request->file("csv");
          $csvArray = Excel::toArray(new JobsImport, $csv);
          $errors = $this->jobService->validateCsv($csvArray[0]);

          if (!is_null($errors)) {
              session()->flash("errors", $errors);
              return redirect("admin/job");
          }
          ImportCsvJobs::dispatch($csv);
          return redirect("admin/job");
      }
}

服务类(JobService)

namespace App\Services\Admin;

use Illuminate\Support\Facades\Validator;
use Illuminate\Support\MessageBag;
use Illuminate\Support\ViewErrorBag;
use Exception;

class JobService
{
    public function validateCsv(array $csvArray)
    {
        $rules = [
            "求人ID" => [
                "required",
                "integer"
            ],
            "求人名" => [
                "required",
            ],
            "企業名" => [
                "required",
            ],
            "園名" => [
                "required",
            ],
            "担当者名" => [
                "required",
            ],
            "雇用形態" => [
                "required",
            ],
            "職種" => [
                "required",
            ],
            "おすすめポイント1" => [
                "required",
            ],
            "おすすめポイント2" => [
                "required",
            ],
            "こんな方におすすめ1" => [
                "required",
            ],
            "こんな方におすすめ2" => [
                "required",
            ],
            "求める人物像" => [
                "required",
            ],
            "応募資格" => [
                "required",
            ],
            "給与形態" => [
                "required",
            ],
            "最低給与額" => [
                "required",
            ],
            "最高給与額" => [
                "required",
            ],
            "給与・待遇" => [
                "required",
            ],
            "勤務時間" => [
                "required",
            ],
            "固定時間勤務" => [
                "required",
            ],
            "休日" => [
                "required",
            ],
            "福利厚生" => [
                "required",
            ],
            "交通費" => [
                "required",
            ],
        ];

        $attributes = [
            "求人ID" => "ID", // job posting ID
        ];
        $uploadErrorList = [];
        foreach ($csvArray as $key => $value) {
            if (!is_array($value)) continue;
            $validator = Validator::make($value, $rules, $attributes);
            if ($validator->fails()) {
                $errorMessage = array_map(fn ($message) => $key + 1 . "行目: " . $message, $validator->errors()->all());
                $uploadErrorList = array_merge($uploadErrorList, $errorMessage);
            }
        };
        if (empty($errorMessage)) return null;

        $errors = new ViewErrorBag();
        $messages = new MessageBag(["upload_errors" => $uploadErrorList]);
        $errors->put("default", $messages);
        return $errors;
    }
}
问题

能否将上述CSV逐行验证逻辑迁移至FormRequest中实现?若可行则无需在Controller中处理验证,代码结构将更优。

解答

当然可以把CSV逐行验证逻辑迁移到FormRequest中,这样能让控制器代码更清爽,完全符合Laravel的验证规范,代码结构也会更合理。具体操作步骤如下:

1. 创建自定义FormRequest

执行Artisan命令生成FormRequest文件:

php artisan make:request ImportJobCsvRequest

修改生成的ImportJobCsvRequest.php文件,实现验证逻辑:

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\MessageBag;
use Maatwebsite\Excel\Facades\Excel;
use App\Imports\JobsImport;

class ImportJobCsvRequest extends FormRequest
{
    /**
     * 确定用户是否有权限进行此请求
     */
    public function authorize(): bool
    {
        return true; // 可根据实际权限需求调整
    }

    /**
     * 获取上传文件的基础验证规则
     */
    public function rules(): array
    {
        return [
            'csv' => 'required|file|mimes:csv,txt',
        ];
    }

    /**
     * 提前解析CSV文件,方便后续逐行验证
     */
    protected function prepareForValidation()
    {
        if ($this->hasFile('csv')) {
            $csvArray = Excel::toArray(new JobsImport, $this->file('csv'));
            $this->merge(['csv_data' => $csvArray[0]]);
        }
    }

    /**
     * 添加CSV逐行验证的自定义逻辑
     */
    public function withValidator($validator)
    {
        $validator->after(function ($validator) {
            if ($this->has('csv_data')) {
                $csvData = $this->input('csv_data');
                $rules = [
                    "求人ID" => ["required", "integer"],
                    "求人名" => ["required"],
                    "企業名" => ["required"],
                    "園名" => ["required"],
                    "担当者名" => ["required"],
                    "雇用形態" => ["required"],
                    "職種" => ["required"],
                    "おすすめポイント1" => ["required"],
                    "おすすめポイント2" => ["required"],
                    "こんな方におすすめ1" => ["required"],
                    "こんな方におすすめ2" => ["required"],
                    "求める人物像" => ["required"],
                    "応募資格" => ["required"],
                    "給与形態" => ["required"],
                    "最低給与額" => ["required"],
                    "最高給与額" => ["required"],
                    "給与・待遇" => ["required"],
                    "勤務時間" => ["required"],
                    "固定時間勤務" => ["required"],
                    "休日" => ["required"],
                    "福利厚生" => ["required"],
                    "交通費" => ["required"],
                ];

                $attributes = [
                    "求人ID" => "ID",
                ];

                $uploadErrorList = [];
                foreach ($csvData as $key => $row) {
                    if (!is_array($row)) continue;
                    $rowValidator = Validator::make($row, $rules, [], $attributes);
                    if ($rowValidator->fails()) {
                        $errorMessages = array_map(fn ($msg) => ($key + 1) . "行目: " . $msg, $rowValidator->errors()->all());
                        $uploadErrorList = array_merge($uploadErrorList, $errorMessages);
                    }
                }

                if (!empty($uploadErrorList)) {
                    $messageBag = new MessageBag(['upload_errors' => $uploadErrorList]);
                    $validator->errors()->merge($messageBag);
                }
            }
        });
    }
}

2. 简化控制器代码

移除JobService依赖,直接使用自定义的FormRequest:

use App\Imports\JobsImport;
use App\Http\Requests\ImportJobCsvRequest;
use Maatwebsite\Excel\Facades\Excel;

class JobsController extends \App\Http\Controllers\Controller
{
    public function import(ImportJobCsvRequest $request)
    {
        // 经过FormRequest验证后,此处无需再处理验证逻辑
        $csv = $request->file("csv");
        ImportCsvJobs::dispatch($csv);
        return redirect("admin/job");
    }
}

3. 视图无需修改

原视图的错误展示逻辑可以直接复用,FormRequest验证失败后会自动将错误信息存入session,和之前的逻辑保持一致。

这样调整后,验证逻辑被封装在FormRequest中,控制器只负责处理核心业务流程,代码结构更清晰,符合Laravel的MVC设计原则。

内容的提问来源于stack exchange,提问作者Hayato Kuroda

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 23:42:03