0

我正在使用 Laravel 5.6 并为我的表单设置了表单请求验证,该表单提交单行,对其进行验证,然后添加到数据库中。这一切都很好。

对于多行的批量导入,我有一个 CSV 导入。CSV 被解析为一个数组,数组的每一行都包含与我的表单中提供的完全相同类型的数据。因此它可以使用相同的验证规则。

我有点迷失如何实际实现这一点;从 CSV 解析的数据在一个数组中,而不是表单验证请求正在寻找的请求对象。

有没有人有关于能够在不重复代码的情况下对表单和 CSV 使用我的表单验证的最佳方式的任何提示?

编辑

如果有人感兴趣,我的最终解决方案是不使用表单请求验证。在我的例子中,将验证规则和消息添加到控制器内的一些受保护函数中更容易。这意味着它们可以在需要它的每个控制器功能(存储、csvStore 等)中重复使用,而无需重复代码。在这种情况下,我不确定表单请求验证功能有什么优势。

//reformat CSV into array
$master = [];
$line_id = 1;
foreach ($data as $row) {
    //skip blank rows
    if (empty($row['sku'])) continue;
    //build master
    foreach($row as $key => $value){
        if(!empty($value)) $master[$row['sku']][$key] = $row[$key];
    }
    //add line number for debugging
    $master[$row['sku']]['line_number'] = $line_id;
    $line_id++;
}

//Validate each row of CSV individually
$error_messages = new MessageBag();
$error_count = 0;
$duplicate_count = 0;
if(empty($master)){
    //empty $master
    $error_messages->add('', 'CSV file does not contain valid data or is empty');
    flash()->message('Nothing was imported');
    return redirect()->back()->withErrors($error_messages);
} else {
    foreach($master as $row){
        $validator = Validator::make($row,$this->createValidationRules(), $this->createValidationMessages());

        //Check validation
        if ($validator->fails()){
            $master[$row['sku']]['valid'] = false;
            if(isset($validator->failed()['sku']['Unique'])){
                $duplicate_count ++;
                if(!request('ignore-duplicates') && !request('ignore-errors')) $error_messages->merge($validator->errors()->messages()); //save error messages
            } else {
                $error_count ++;
                if(!request('ignore-errors')) $error_messages->merge($validator->errors()->messages()); //save error messages
            }
        } else {
            $master[$row['sku']]['valid'] = true;
        }
    }
}

//add successful rows to DB
$success_count = 0;
foreach($master as $row){
    if($row['valid'] == true){
        $productCreate = new ProductCreate();
        $productCreate->create($row);
        $success_count++;
    }
}

然后,我使用成功/错误/重复计数来发回合适的错误消息包和/或闪存消息。

4

1 回答 1

1

您可以通过创建一个请求对象宏将 CSV 转换为数组来处理它,然后使用中间件解析传入的请求(如果它是 csv 文件)并将其合并到传入的请求中。然后您的应用程序的验证可以使用数组验证对其进行验证。

首先让服务提供商容纳您的请求宏:

php artisan make:provider RequestMacroParseCsvProvider

然后在服务提供者中:

在顶部添加这个以拉入 Request 类:

use Illuminate\Http\Request;

在提供者的注册方法里面:

Request::macro('parseCsv', function ($fileNameKey) {
    // Note: while working inside of the request macro closure, you can access the request object by referencing $this->{key_of_request_item}

    // You will be running your parser against $fileNameKey which will be the key of the request file coming in. So you'd access it like:
    if ($this->hasFile($fileNameKey)) {
        // Your code to parse the csv would go here. Instantiate your csv parsing class or whatever...
        // $file = $this->file($fileNameKey);
        // Store the parsed csv in an array, maybe named $parsedCsv?
    }

    return empty($parsedCsv) ? [] : $parsedCsv;
});

将服务提供商注册到您的config/app.php

App\Providers\RequestMacroParseCsvProvider::class,

创建中间件以检查传入请求是否包含 csv

php artisan make:middleware MergeCsvArrayIntoRequest

handle方法中:

if ($request->has('your_csv_request_key')) {
    $parsedCsv = $request->parseCsv('your_csv_request_key');

    // Then add it into the request with a key of 'parsedCsv' or whatever you want to call it
    $request->merge(['parsedCsv' => $parsedCsv]);
}

return $next($request);

在您的中注册您的中间件app/Http/Kernel.php

protected $middleware = [
    ...
    \App\Http\Middleware\MergeCsvArrayIntoRequest::class,
    ...
];

$routeMiddleware如果您不希望它是全球性的,或者将其放入。

'parse.csv' => \App\Http\Middleware\MergeCsvArrayIntoRequest::class,

现在,您的中间件正在拦截和转换您上传的任何 CSV 文件,您可以parsedCsv使用 Laravel 的数组验证来验证请求密钥。

如果您愿意,您绝对可以进行一些改进以使其更加灵活。我在另一个项目中做了类似但与文件不完全相关的事情,在该项目中我需要在请求得到控制器验证之前对其进行修改并且它起作用了。

希望这可以帮助。

于 2018-06-08T17:18:24.830 回答