Laravelでcsvファイルのvalidationをする
概要
・開発でcsvインポート機能を作った際にバリデーションでちょっと手間取った
・力技かもしれないがとりあえず頑張った証拠として残しておこうと思った。
・ライブラリ等は使ってません。
これが正解かもわからないですがもっといい方法あるよとかあれば教えていただけると嬉しいです。
仕様
PHP 7.4
Laravel 8
mysql 8
DBテーブル詳細
仮にこんなテーブルがあるとします。
prohectsテーブル
またこのprojectsテーブルにはpositionsテーブルとskillsテーブルが多対多で紐づいています。
positionsテーブル
これらのテーブルにcsvから一気に登録します。
テーブルの中身よりもcsvのデータに対しても普通にバリデーションできているところがミソです。
csvファイル
csvファイルはこんな感じです。色ついてるところは気にしなくて大丈夫です。
リレーションしているところはめんどくさいのでIDで記入しました。skillとpositionは複数登録できるのでexplodeして配列で入れます。
実装
今回はRequestクラスを使ってRequestクラス内で完結するやり方です。
<?php
namespace App\Http\Requests\Test;
use App\Models\Agent;
use App\Models\Position;
use App\Models\Skill;
use App\Models\Station;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
use SplFileObject;
class TestRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules(): array
{
$skills = Skill::all();
$skill_ids = $skills->pluck('id')->toArray();
$positions = Position::all();
$position_id = $positions->pluck('id')->toArray();
$agents = Agent::all();
$agent_ids = $agents->pluck('id')->toArray();
$stations = Station::all();
$station_ids = $stations->pluck('id')->toArray();
return [
'csv_file' => ['required', 'file', 'mimetypes:text/plain', 'mimes:csv,txt'],
'csv_array' => ['required', 'array'],
'csv_array.*.name' => ['required', 'unique:projects', 'string'],
'csv_array.*.station_id' => ['required', 'integer', Rule::in($station_ids)],
'csv_array.*.description' => ['required', 'string', 'max:500'],
'csv_array.*.required_condition' => ['nullable', 'string', 'max:500'],
'csv_array.*.better_condition' => ['nullable', 'string', 'max:500'],
'csv_array.*.work_start' => ['nullable', 'string', 'max:5'],
'csv_array.*.work_end' => ['nullable', 'string', 'max:5'],
'csv_array.*.weekly_attendance' => ['nullable', 'integer', 'max:5'],
'csv_array.*.skill_ids' => ['required', 'array', 'max:10'],
'csv_array.*.skill_ids.*' => ['required', 'integer', Rule::in($skill_ids)],
'csv_array.*.position_ids' => ['required', 'array', 'max:10'],
'csv_array.*.position_ids.*' => ['nullable', 'integer', Rule::in($position_id)],
];
}
protected function prepareForValidation()
{
$file_path = $this->file('csv_file')->path();
// CSV取得
$file = new SplFileObject($file_path);
$file->setFlags(
SplFileObject::READ_CSV | // CSVとして行を読み込み
SplFileObject::READ_AHEAD | // 先読み/巻き戻しで読み込み
SplFileObject::SKIP_EMPTY | // 空行を読み飛ばす
SplFileObject::DROP_NEW_LINE // 行末の改行を読み飛ばす
);
foreach ($file as $index => $line) {
// ヘッダーを取得
if (empty($header)) {
$header = $line;
continue;
}
$csv_array[$index]['station_id'] = (int)$line[0];
$csv_array[$index]['name'] = $line[1];
$csv_array[$index]['description'] = $line[2] == "" ? null : $line[2];
$csv_array[$index]['required_condition'] = $line[3] == "" ? null : $line[3];
$csv_array[$index]['better_condition'] = $line[4] == "" ? null : $line[4];
$csv_array[$index]['work_start'] = $line[5] == "" ? null : $line[5];
$csv_array[$index]['work_end'] = $line[6] == "" ? null : $line[6];
$csv_array[$index]['weekly_attendance'] = (int)$line[7] === 0 ? null : (int)$line[7];
$csv_array[$index]['skill_ids'] = explode(',', $line[8]);
$csv_array[$index]['position_ids'] = explode(',', $line[9]);
}
$this->merge([
'csv_array' => $csv_array, //requestに項目追加
]);
}
}
ざっくり解説すると
Requestで入ってきたcsvデータを配列に入れて整理しcsv_arrayという名前で項目を追加してバリデーションしていく感じです。
prepareForValidation関数はLaravelで提供されている関数でバリデーション前にRequestの中身を加工することができます。
csvのデータは配列に加工しているので.*で取り出すことができます。
項目を追加しているので元々のcsvファイル自体のバリデーションも同時に行えます。
intにしたり中身が空だった時にnullにしたりしているところは各仕様に合わせて変更してください。
stringで避ければそのままいれるだけで大丈夫です。
以上です。
参考
Author And Source
この問題について(Laravelでcsvファイルのvalidationをする), 我々は、より多くの情報をここで見つけました https://qiita.com/wakidebase/items/d849333d6ff73bddd47d著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .