【laravel】縦持ち配列 × 複数項目を参照するバリデーション

複数行の入力フォームで、縦持ちの配列に対して複数の値を参照しながらバリデーションをしたいと思ったことはありませんか?
例えば、

  • 縦持ちの配列に対するバリデーション方法が分からない
  • 同じ行の情報(数量 × 単価 など)を組み合わせてチェックしたい
  • laravel標準のバリデーションだけでは実装できない
  • wijmo flexgridのデータをどう扱えばいいか分からない

今回は、縦持ち配列のデータに対して複数の値を参照しながら判定する
laravelのカスタムバリデーションを実際のサンプルコードを使って紹介します。

動的に行が増えるフォームや、明細入力のある画面などのケースです。
実際に私自身も、wijmoのflexgridに対してフォームの値にし、縦持ち配列のデータを扱う必要があり、
今回紹介する方法でカスタムバリデーションを実装しました。
同じような構成を扱っている方は、ぜひ参考にしてみてください。

  • 縦持ち配列に対するバリデーションができるようになる
  • 標準バリデーションでは対応できないケースの考え方が身につく
  • 動的に行が増えるフォームでもそのまま使える実装が分かる
  • wijmoのflexgridを使ったテーブルでも応用できる
目次

実装

想定データ

まず、今回想定しているデータの形を先に見ておきます。

'grid_data' => [
    0 => [
        'item_name' => 'ノートパソコン',
        'quantity'  => 5,
        'price'     => 120000,
    ],
    1 => [
        'item_name' => 'マウス',
        'quantity'  => 2,
        'price'     => 1500,
    ],
]

フォームをPOSTすると、上記のように明細ごとに情報がまとまった「縦持ちの配列」が来ることを想定しています。
今回はこのデータに対して同じ行の「数量」と「単価」を参照しながらバリデーションを行う実装を紹介していきます。

補足

wijmoの場合、flexgridはHTMLフォームのinput要素ではないため、そのままではサーバーに配列として送信されません。
そのため、以下のようにgridのデータをJSONに変換し、input hiddenを使って配列として送信する必要があります。

let formElement = document.getElementById('form');

let flexGrid = wijmo.Control.getControl('#flexGrid');//frexgridのid
let source = flexGrid.collectionView.sourceCollection;

let input = document.createElement('input');
input.type = 'hidden';
input.name = 'grid_data';
input.value = JSON.stringify(source);
formElement.appendChild(input);

formElement.method = 'POST';
formElement.action = route;//POST先のルート

formElement.submit();

実装する流れ

  • POST先のコントローラのメソッドにカスタムバリデーションを定義する
  • カスタムバリデーション内で複数の値を見て判定をするロジックを作成する
  • エラーメッセージを定義する
  • 項目に作成したバリデーションを指定する

カスタムバリデーションを定義

カスタムバリデーションの名前は「total_check」とします。
定義外の値を参照したい場合は、useを使って変数を渡します。

今回は数量が0より大きく、数量×単価が500,000を超えた場合にエラーとします。

	public function postBlog()
	{
		//金額条件
		$maxPrice = 500000;
		//数量が0より大きい場合
		Validator::extend('total_check', function ($attribute, $value, $parameters, $validator) use ($maxPrice) {
		  //getData()の中からgrid_dataを取得
			$data = $validator->getData()['grid_data'];

			list($dust1, $index, $dust2) = explode('.', $attribute);
			$meisai = $data[$index];

			//数量が0より大きいかつ、金額がmaxPriceより多い場合にエラーとする
			$total = $meisai['quantity'] * $meisai['price'];
			if ($meisai['quantity'] > 0 && $total > $maxPrice) {
				return false;
			}

			return true;
		});
		
		//エラーメッセージを定義
		$messages = [
			'meisai.*.quantity.total_check' => '金額の合計が上限を超えています。',
		];
  }

バリデーションの適用

バリデーションロジックを追加し、数量に対して作成したバリデーションを適用させます。

	public function postBlog()
	{
		//金額条件
		$maxPrice = 500000;
		//数量が0より大きい場合
		Validator::extend('total_check', function ($attribute, $value, $parameters, $validator) use ($maxPrice) {
			$data = $validator->getData()['meisai'];

			list($dust1, $index, $dust2) = explode('.', $attribute);
			$meisai = $data[$index];

			//数量が0より大きいかつ、金額がmaxPriceより多い場合にエラーとする
			$total = $meisai['quantity'] * $meisai['price'];
			if ($meisai['quantity'] > 0 && $total > $maxPrice) {
				return false;
			}

			return true;
		});

		$messages = [
			'meisai.*.quantity.total_check' => '金額の合計が上限を超えています。',
		];
		
    //↓追加↓
		// エラーチェック
		$validator = Validator::make(request()->all(), [
			'name' => ['required'],
			'age' => ['required', 'integer'],
			'meisai.*.quantity' => ['required', 'numeric', 'between:0,100', 'total_check'],
			'meisai.*.price' => ['required', 'numeric', 'between:0,1000000'],
			'meisai.*.comment' => ['nullable', 'string', 'max:100'],
		],
		$messages,
		);
		
		if ($validator->fails()) {
			// エラーの中身を取得
			$errors = $validator->errors();
			dd($errors->toArray());
		}
		//↑追加↑
	}

エラーの場合は、エラーの内容を「dd()」で画面に出力し、処理を止めます。

確認のために、数量を「5」、単価を「120000」にしてPOSTしてみます。

ちゃんと定義したエラーメッセージが出力されました。

まとめ

縦持ち配列のデータに対して複数の値を参照してチェックするlaravelのカスタムバリデーション方法を紹介しました。

標準のバリデーションでは対応しづらい複数の項目チェックも、「Validator::extend」を使って柔軟に実装できます。
また、FlexGridや動的な明細フォームなど、行数が増減するフォームでも今回の内容を参考に対応することができると思います。

横持の配列に対してのバリデーション方法は以下の記事で紹介しておりますので、こちらも併せて読んでみてください。
【laravel】配列に対してバリデーションをする方法

  • URLをコピーしました!
目次