laravellaravel-5custom-validators

Laravel 5.5 cannot make validator in a form Request class


I want to check if a form input 'departement' is filled only if two 'villes' have the same name.

within controller this code wokds perfectly :

 $rules=[  'nom' => 'required', 'ville'=> 'required|exists:villes,nom'];
 $messages = [
      'depart.required' => 'Deux villes portent le même nom, preciser le 
 département'];
  $validator = Validator::make($request->All(), $rules,$messages);
  $validator->sometimes('depart_id', 'required|exists:departs,id', function 
  ($input) {
      return Ville::where('nom',$input->ville)->count()>1;
  });
  if ($validator->fails()) {
    return redirect('admin/etab/create')
            ->withErrors($validator)
            ->withInput();
    }

I put the same code in a Form Request class:

public function rules()
{
  $rules=[  'nom' => 'required', 'ville'=> 'required|exists:villes,nom'];
  $messages = [
      'depart.required' => 'Deux villes portent le même nom, preciser le 
  département',
  ];
  $validator = Validator::make($this->All(), $rules,$messages);
  $validator->sometimes('depart_id', 'required|exists:departs,id', function 
  ($input) {
      return Ville::where('nom',$input->ville)->count()>1;
  });
  return $validator;
}

I get "Type error: Argument 2 passed to Illuminate\Validation\Factory::make() must be of the type array, object given," I think error message is inadequate but I cannot find why this way does not work

Thanks ......


Solution

  • You can check out the FormRequest class in vendor/laravel/framework/src/Illuminate/Foundation/Http/FormRequest.php and check what it does.

    It contains these 2 method at the top:

    /**
     * Get the validator instance for the request.
     *
     * @return \Illuminate\Contracts\Validation\Validator
     */
    protected function getValidatorInstance()
    {
        $factory = $this->container->make(ValidationFactory::class);
    
        if (method_exists($this, 'validator')) {
            $validator = $this->container->call([$this, 'validator'], compact('factory'));
        } else {
            $validator = $this->createDefaultValidator($factory);
        }
    
        if (method_exists($this, 'withValidator')) {
            $this->withValidator($validator);
        }
    
        return $validator;
    }
    
    /**
     * Create the default validator instance.
     *
     * @param  \Illuminate\Contracts\Validation\Factory  $factory
     * @return \Illuminate\Contracts\Validation\Validator
     */
    protected function createDefaultValidator(ValidationFactory $factory)
    {
        return $factory->make(
            $this->validationData(), $this->container->call([$this, 'rules']),
            $this->messages(), $this->attributes()
        );
    }
    

    So you can basically provide a validator method in your own FormRequest class to create a custom Validator object, that method will get the ValidatorFactory as param.

    In your case you would not need to do this, because you just want to append the sometimes rule to a default validator. Looking at the code above, it checks for the existence of the withValidator method, if it exists, it is called:

        if (method_exists($this, 'withValidator')) {
            $this->withValidator($validator);
        }
    

    You could create the FormRequest, make sure the rules, messages and authorize methods are properly used, e.g. rules and messages return arrays and authorize returns a bool.

    Then create a withValidator method in which you attach the sometimes rule to the Validator.

    /**
     * Do foo with Validator
     *
     * @param \Illuminate\Contracts\Validation\Validator $validator
     * @return void
     */
    public function withValidator(Validator $validator)
    {
        $validator->sometimes('depart_id', 'required|exists:departs,id', function {
            return Ville::where('nom', $this->input('ville'))->count() > 1;
        });
    }
    

    This way sometimes is attached to your validator before the validation is performed.