Search code examples
phplaravellaravel-validationlaravel-nova

Laravel Nova Conditional Validation : required_if + exists rule


I'm trying to validate 'parent_code' attr regarding 'level' field value

Here is what i'm trying to achieve :

'parent_code' is only required when 'level' is != 0 (This part works fine)

and when it's set, it must also exists in table 'products' ('product_code' : the column name that will be used)

My current code (doesn't work properly)

Product resource class

public function fields(Request $request) {
        return [
            ID::make()->sortable(),

            Text::make('Product code', 'product_code')
                ->rules('required')
                ->creationRules('unique:products,product_code')
                ->updateRules('unique:products,product_code,{{resourceId}}')->hideFromIndex(),


            Text::make('Product short name', 'product_short_name')->onlyOnForms(), 


            Textarea::make('Product name', 'product_name')
                ->rules('required')
                ->sortable()
                ->onlyOnForms(),

            Text::make('Parent code', 'parent_code')
                ->rules(['required_if:level,2,4,6', 'exists:products,product_code'])
                ->hideFromIndex(), 

            Select::make('Level', 'level')->options([
                '0' => 'Sector level',
                '2' => 'H2',
                '4' => 'H4',
                '6' => 'H6',
            ])->rules('required')->sortable(),    

        ];
}

Create Product Form

Create product form

Thank you for your help.


Solution

  • The proper way would have been to use the sometimes() method on the validator instance, but you can't access it from Laravel Nova rules.

    You can just define the rules as a closure that recieves the current incoming request and check the value to dinamically build the rules array:

    Laravel Collections solution

    Text::make('Parent code', 'parent_code')
        ->hideFromIndex()
        ->rules(function ($request) {
            // You could also use "->when(...)" instead of "->unless(...)"
            // and invert the condition (first argument)
            return collect(['required_if:level,2,4,6'])
                ->unless($request->level === 0, function ($rules) {
                    return $rules->push('exists:products,product_code');
                })
                ->toArray();
        }),
    

    The logic without using collections is the same, just use basic if statements to dynamically add conditions:

    Plain PHP Arrays Solution

    Text::make('Parent code', 'parent_code')
        ->hideFromIndex()
        ->rules(function ($request) {
            return [
                'required_if:level,2,4,6',
                ($request->level !== 0) ? 'exists:products,product_code' : '',
            ];
        }),
    

    or (variant):

    Text::make('Parent code', 'parent_code')
        ->hideFromIndex()
        ->rules(function ($request) {
            $rules = ['required_if:level,2,4,6'];
    
            if ($request->level !== 0) {
                $rules[] = 'exists:products,product_code';
            }
    
            return $rules;
        }),