56

Is there is a way of referencing another field when specifying the exists validation rule in Laravel? I want to be able to say that input a must exist in table a, input b must exist in table b AND the value for column x in table b must equal input a.

Best explained by example:

public $rules = array(
    'game_id' => 'required|exists:games,id',
    'team1_id' => 'required|exists:teams,id,game_id,<game_id input value here>',
    'team2_id' => 'required|exists:teams,id,game_id,<game_id input value here>'
);

So with my validation rules I want to be able to make sure that:

  • game_id exists within the games table (id field)
  • team1_id exists within the teams table (id field) and the game_id column (in the teams table) must equal the value of the game_id input.
  • As above for team2_id

So, if in my form, I entered 1 for game_id, I want to be able to ensure that the record within the teams table for both team1_id and team2_id have the value 1 for game_id.

I hope this makes sense.

Thanks

2
  • 1
    I've been using this format since 5.3 and it works for me. I'm pretty sure it's valid, although I haven't seen it in their docs. $this->mymodel->create_rules['company_id'] = 'required|exists:companies,id,type_id,' . $id; This means the company_id must exists in its own table and the type_id field must be a value of $id. Commented Jul 11, 2017 at 13:27
  • 3
    @Jin thank you so much I've tested your format and it work with me also (Laravel 7) I think that your comment is much better that the marked answer Commented Jun 4, 2020 at 8:32

9 Answers 9

57

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.

Sign up to request clarification or add additional context in comments.

2 Comments

@AaronHill, how did you do in laravel v5.5?
@roerjo Why do you say it doesn't work on laravel 5.5? It still works in laravel 7.x.. Btw you don't need a closure you can just do ->where('activationCode', $activationCode) directly
35

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 example
Validator::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'
);

2 Comments

This seems to work perfectly, thanks. I'll have to read up on custom validation rules. Where should one place custom validation classes? Cheers
You probably have some structure (or should have) for your custom code, so put it there. In Laravel 5 you would create a new service provider, but, that's different story.
15

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.

Comments

10

check NULL Condition

'game_id' => 'required|exists:games,id,another_column,NULL',

you can add more condition using (,) column name and value.

Comments

5

another way can you try these

public $rules = array(
   'game_id' => 'required|exists:games,id',
   'team1_id' => 'required|exists:teams,id,game_id,'.$request->game_id,
   'team2_id' => 'required|exists:teams,id,game_id,'.$request->game_id
);

Comments

2

try this its works for me

'email'=>'required|unique:admintable,Email,'.$adminid.',admin_id',

4 Comments

Don't use this if the admin id comes from the request... This is susceptible to variable injection and somebody might trick your validation with some pipes and comas.. Or worse even lead to remote code execution
hey @Tofandel call regex to just keep numbers and remove other characters for adminid before call for validation ;)
Or just use something of the like.. 'email' => ['required', 'unique' => ['admintable', 'Email', $adminid, 'admin_id']]
In the instance that admin_id comes from the request you would likely have a validator for it anyway 'admin_id' => 'required|integer|exists:admins,id' so the argument is kind of moot.
1

If anybody else is still finding a better solution with Laravel 6 or higher you can simply add the field name with the "exist" rule, something like this.

$rules = $request->validate([
   'game_id' => 'required|exists:games,id',
   'team1_id' => 'required|exists:teams,id',
   'team2_id' => 'required|exists:teams,id'
]);

and to check if game_id is present on the "teams" table or not you can use IN: rule for that.

something like that:

$rules = $request->validate([
   'game_id' => 'required|exists:games,id',
   'team1_id' => 'required|exists:teams,id|in:games',
   'team2_id' => 'required|exists:teams,id|in:games',
]);

I hope that will help someone.

Comments

0

you could get some value in the Rules method like $this->value and then use rules like same for string against that value

1 Comment

As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.
-1
$pdf = $request->file('resume')->getClientOriginalName();
 
   if (file_exists(public_path('PDF/'.$pdf))) {
      
      return back()->withErrors(["resume" => "This File is Already Exits"])->withInput();
        
    }else{
        $request->resume->move(public_path('PDF'), $pdf);
    }

You have to add Like this Error message, This is Working for me

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.