Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I add a join to a custom exists rule for a laravel validator?

The validator in laravel can have a customization of the exists database rule, for instance if you need to check an extra column. An example from the manual:

use Illuminate\Validation\Rule;

Validator::make($data, [
    'email' => [
        'required',
        Rule::exists('staff')->where(function ($query) {
            $query->where('account_id', 1);
        }),
    ],
]);

The query in the closure is not typehinted, so i'm not quite positive what kind of object this is. I can see that the DatabaseRule itself only has some function about where, wherenot etc, but i'm looking to add a join to the mix.

Given example says that the email must exist for a staff with account_id = 1, but what if the team (all staff are part of a team, which is a separte table) should have a certain property, e.g. team.active = 1 ?
the whole staff/team thing is an example of course


So in the end I'm wondering: how can I add a join to this rule so we make sure that the staff's team has a column 'active' that is 1.

My first issue might be: what is the type of that $query? I would imagine that something like this would be great, but there's no reason to suspect this is valid:

Rule::exists('staff')->where(function ($query) {
    $query
        ->join('team', 'team.team_id', '=', 'staff.team_id')
        ->where('team.active', 1);
})

This does not seem to work. The strange thing is that the join itself does not cause an error, but seems to be ignored:

Column not found:
1054 Unknown column 'team.active' in 'where clause'
(SQL: select count(*) as aggregate from staff where email = -thevalue- and (team.active = 1))

I would have expected this to work (small change as I'm gambling that this function is available here), or get an error because I'm calling a non-existent function. But what I get is a query that is build but without the join.

From the comments I added a $query->toSQL() to that function. This does show a rather expected result, but it does not compute with the error I'm getting:

select * from `staff`
inner join `teams` on `teams`.`team_id` = `staff`.`team_id`
where `teams`.`active` = ?
like image 253
Nanne Avatar asked Oct 11 '17 08:10

Nanne


People also ask

How do you add a validation rule in laravel?

You should add all your validation logic in the passes() function. It should return true or false based on the logic you have written in the function. The message() function returns a string that specifies the error message to be displayed in case the validation fails.

What is the method used to configure validation rules in form request laravel?

Laravel Form Request class comes with two default methods auth() and rules() . You can perform any authorization logic in auth() method whether the current user is allowed to request or not. And in rules() method you can write all your validation rule.

What is bail rule in laravel?

The bail validation rule applies to a "multi-rule" attribute. It does not stop running validation for other attributes. From the documentation: $request->validate([ 'title' => 'bail|required|unique:posts|max:255', 'body' => 'required', ]);


1 Answers

It seems that the DatabaseRule does some magic with whatever you provided in that closure. In the end, it only seems to look at provided where clauses (see Exists::__toString). The joins have been saved as the echo inside the closure shows, but then the exists rule only looks at the where. That's the reason for the unknown column error it seems.

I did try the using function, which seems better suited than the where I had in there first, but that doesn't help, as the system makes it into a string, which forms a validation-string that just doesn't seem to support joins.

Long story short, I just created a custom validation rule with the passes function looking something like this:

public function passes($attribute, $value)
{
    $count = DB::table('staff')
        ->join('teams', 'teams.team_id', '=', 'staff.team_id')
        ->where([
            ['teams.active', '=', 1],
            ['staff.email', '=', $value]
        ])->count();

    return $count > 0;
}
like image 126
Nanne Avatar answered Sep 19 '22 17:09

Nanne