Laravel validation: exists with additional column condition - custom validation rule
From Laravel 5.3+ you can add a custom where clause to the exists and unique rules.
Here is my scenario: I have an email verification table and I want to ensure that a passed machine code and activation code exist on the same row.
Be sure to use Illuminate\Validation\Rule;
$activationCode = $request->activation_code; $rules = [ 'mc' => [ 'required', Rule::exists('email_verifications', 'machineCode') ->where('activationCode', $activationCode), ], 'activation_code' => 'required|integer|min:5', 'operating_system' => 'required|alpha_num|max:45' ];
The first argument in the exists method is the table and the second is the custom column name I'm using for the 'mc' field. I pass the second column I want to check in a where clause.
This is pretty handy, because now I no longer need a custom Validation rule.
You want a custom validation rule, and I would create a separate class for this. But for brevity here's pretty much the same using inline closure:
// give it meaningful name, I'll go with game_fixture as an exampleValidator::extend('game_fixture', function ($attribute, $value, $parameters, $validator) { if (count($parameters) < 4) { throw new \InvalidArgumentException("Validation rule game_fixture requires 4 parameters."); } $input = $validator->getData(); $verifier = $validator->getPresenceVerifier(); $collection = $parameters[0]; $column = $parameters[1]; $extra = [$parameters[2] => array_get($input, $parameters[3])]; $count = $verifier->getMultiCount($collection, $column, (array) $value, $extra); return $count >= 1;});
Then use simply this:
$rules = array( 'game_id' => 'required|exists:games,id', // last parameter here refers to the 'game_id' value passed to the validator 'team1_id' => 'required|game_fixture:teams,id,game_id,game_id', 'team2_id' => 'required|game_fixture:teams,id,game_id,game_id');
As your rules are model property you need to make some change for them before running validator.
You could change your rules to:
public $rules = array( 'game_id' => 'required|exists:games,id', 'team1_id' => 'required|exists:teams,id,game_id,{$game_id}', 'team2_id' => 'required|exists:teams,id,game_id,{$game_id}');
and now you will need to use loop to insert correct value instead of {$game_id}
string.
I can show you how I did it in my case for editing rule:
public function validate($data, $translation, $editId = null){ $rules = $this->rules; $rules = array_intersect_key($rules, $data); foreach ($rules as $k => $v) { $rules[$k] = str_replace('{,id}',is_null($editId) ? '' : ','.$editId , $v); } $v = Validator::make($data, $rules, $translation); if ($v->fails()) { $this->errors = $v->errors(); return false; } return true;}
You can do the same in your case changing {$game_id}
into $data['game_id']
(in my case I changed {,id}
into ,$editId
EDIT
Of course If you didn't have $rules
set as property you could simply do:
$rules = array( 'game_id' => 'required|exists:games,id', 'team1_id' => 'required|exists:teams,id,game_id,'.$data['game_id'], 'team2_id' => 'required|exists:teams,id,game_id,'.$data['game_id']);
in place where you have your data set.