Search code examples
laravelformsvalidationlaravel-livewire

Livewire validation with custom rule and additional parameters


I have a Form where a timeslot (the validating field) might be valid for employee A (another field in the component), but not for employee B.

I have a custom validation Rule TimeSlotEmployee that checks for the correctness of the combination (employee, timeslot) through request('employee_id') and the $value parameter of the passes function, and so far it works fine.

I'm now moving this form into a Livewire component, but I'm facing difficulties because in the POST request that is triggered by the update of the timeslot field there is no employee_id, only the new timeslot value.

How can I solve this problem? Am I tackling it from the wrong angle?

Update

This is the rules function inside the TimeSlotRequest:

public function rules()
{
  return [
    'employee_id' => ['required', 'numeric'],
    'timeslot' => ['required', new TimeSlotEmployeeRule()],
  ];
}

In the TimeSlotEmployeeRule, this is the passes function:

public function passes($attribute, $value)
{
    $timesloteValue = Carbon::createFromFormat('d-m-Y H:i', $value);
    $employee = Employee::find(request('employee_id'));

    // perform checks: this part works, but requires the employee instance

   return true;
}

In the Livewire component, I use the standard updated method:

public function updated($propertyName)
{
    $this->validateOnly($propertyName);
}

Solution

  • Since the request in Livewire needs to rehydrate the component, you can never fully depend on the request() helper, particularly in subrequests - as the data being passed is Livewire's data.

    You should pass the value as a parameter to the rule instead, like so,

    <?php
    
    namespace App\Rules;
    
    use Illuminate\Contracts\Validation\Rule;
    
    class TimeSlotEmployeeRule implements Rule
    {
        public int $employee_id;
    
        public function __construct($employee_id)
        {
            $this->employee_id = $employee_id;
        }
    
        public function passes($attribute, $value)
        {
            $timesloteValue = Carbon::createFromFormat('d-m-Y H:i', $value);
            $employee = Employee::find($this->employee_id);
    
            // perform checks: this part works, but requires the employee instance
    
           return true;
        }
    } 
    

    Then in the rules, you pass in the ID,

    public function rules()
    {
      return [
        'employee_id' => ['required', 'numeric'],
        'timeslot' => ['required', new TimeSlotEmployeeRule($this->employee_id)],
      ];
    }