Example of Custom validation rules in Melcus/parking-system

File app/Rules/ExistingReservationRuleForInterval.php (link to Github)
use App\Models\Spot;
use Carbon\Carbon;
use Illuminate\Contracts\Validation\Rule;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Arr;

class ExistingReservationRuleForInterval implements Rule
{
    public function __construct(public $spot_id = null)
    {
    }

    public function passes($attribute, $value): bool
    {
        if (!$this->spot_id) {
            return false;
        }

        return Spot::where('id', $this->spot_id)->whereHas('reservations', function (Builder $query) use ($value) {
                $query->where([
                    ['start', '<=', Carbon::parse(Arr::get($value, 'end'))],
                    ['end', '>=', Carbon::parse(Arr::get($value, 'start'))],
                ]);
            })->count() === 0;
    }

    public function message(): string
    {
        return 'A reservation for this spot with this period is not valid.';
    }
}
File app/Http/Requests/ReservationCreateRequest.php (link to Github)
use App\Rules\ExistingReservationRuleForInterval;
use Illuminate\Foundation\Http\FormRequest;

class ReservationCreateRequest extends FormRequest
{
    public function authorize(): bool
    {
        return true;
    }

    public function rules(): array
    {
        return [
            'start'   => ['required', 'date', 'after_or_equal:now'],
            'end'     => ['required', 'date', 'after:start'],
            'spot_id' => ['required'],
            'range'   => [new ExistingReservationRuleForInterval($this->get('spot_id'))]
        ];
    }

    protected function prepareForValidation()
    {
        return $this->merge([
            'range' => [
                'start' => $this->get('start'),
                'end'   => $this->get('end')
            ]
        ]);
    }
}

Additional resources on custom validation rules: