50

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

Jarek Tkaczyk
  • 78,987
  • 25
  • 159
  • 157
Jonathon
  • 15,873
  • 11
  • 73
  • 92
  • 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. – Jin Jul 11 '17 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 – RoduanKD Jun 04 '20 at 08:32

8 Answers8

53

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.

Tofandel
  • 3,006
  • 1
  • 29
  • 48
roerjo
  • 790
  • 6
  • 12
  • @AaronHill, how did you do in laravel v5.5? – LINKeRxUA Jan 09 '18 at 13:33
  • @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 – Tofandel Jul 10 '20 at 06:49
32

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'
);
Jarek Tkaczyk
  • 78,987
  • 25
  • 159
  • 157
  • 1
    This seems to work perfectly, thanks. I'll have to read up on custom validation rules. Where should one place custom validation classes? Cheers – Jonathon Sep 30 '14 at 14:22
  • 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. – Jarek Tkaczyk Sep 30 '14 at 14:24
13

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.

Marcin Nabiałek
  • 109,655
  • 42
  • 258
  • 291
7

check NULL Condition

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

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

Tamil
  • 311
  • 4
  • 9
4

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
);
2

try this its works for me

'email'=>'required|unique:admintable,Email,'.$adminid.',admin_id',
user3151197
  • 347
  • 1
  • 3
  • 14
  • 2
    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 – Tofandel Jul 10 '20 at 06:56
  • hey @Tofandel call regex to just keep numbers and remove other characters for adminid before call for validation ;) – user3151197 Jul 16 '20 at 06:17
  • Or just use something of the like.. `'email' => ['required', 'unique' => ['admintable', 'Email', $adminid, 'admin_id']]` – Tofandel Jul 16 '20 at 17:21
  • 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. – mp035 Aug 27 '21 at 22:42
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.

Rohit Suthar
  • 3,528
  • 1
  • 42
  • 48
Mudit Gulgulia
  • 1,131
  • 7
  • 21
-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