Mac
Mac

Reputation: 1147

Make Laravel's notIn validation rule case insensitive

I am storing an array of strings in my database (db column type is JSON). There is a form that allows users to add a value to this array. I want to make sure there are no duplicates in this array. The notIn validation rule appears be the simplest solution to prevent duplicates but it is case sensitive. So when using notIn I am not able to prevent identical strings that have different capitalization.

$this->validate(request(), [
    'choice' => [
        'required',
        Rule::notIn($choices)
    ]
]);

Does anyone have recommendation on how I should fix this validation so that the string comparison is case insensitive?

Upvotes: 5

Views: 12774

Answers (4)

S.Kashizadeh
S.Kashizadeh

Reputation: 583

i know it is a little late, but for others i will suggest to use prepareForValidation method inside custom request class; like follow


<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class RegisterRequest extends FormRequest
{
    protected function prepareForValidation()
    {
        $this->merge([
            'choices' => strtolower($this->choices),
        ]);
    }
}

this way user input for choices are always lower case and the request itself is modified too.

Upvotes: 2

Ramy Herrira
Ramy Herrira

Reputation: 624

You could lowercase your input data as well as your current data like this:

$input = request()->all();
$input['choice'] = array_map("strtolower", $input['choice']);
request()->validate($input, [
    'choice' => [
        'required',
        Rule::notIn(array_map("strtolower", $choices))
    ]
]);

Upvotes: 8

dmajka
dmajka

Reputation: 137

You can write your own validation rule class:

use Illuminate\Contracts\Validation\Rule;
use Illuminate\Validation\Concerns\ValidatesAttributes;
use Illuminate\Validation\Rules\In;

class CaseInsensitiveInRule extends In implements Rule
{
    use ValidatesAttributes;
    private const FORMAT_FUNCTION = 'strtoupper';

    public function __construct(array $values)
    {
        $this->values = array_map(self::FORMAT_FUNCTION, $values);
    }

    public function passes($attribute, $value)
    {
        $value = call_user_func(self::FORMAT_FUNCTION, $value);

        return $this->validateIn($attribute, $value, $this->values);
    }

    public function message()
    {
        return __('validation.invalid_value');
    }
}

and next you can create a object in your request class

public function rules(): array
{
    return [
        'status' => new CaseInsensitiveInRule(['active', 'deleted'])
    ];
}

Upvotes: 2

Mac
Mac

Reputation: 1147

Thanks Ramy Herria, I was able to expand his answer to also work in a FormRequest class:

protected function validationData()
{
    $all = parent::validationData();
    //Convert request value to lowercase
    $all['choice'] = strtolower($all['choice']);
    return $all;
}

public function rules()
{
    $choices = $this->route('modelName')->choices;
    return [
        'choice' => [
            'required',
            //Also convert array to lowercase
            Rule::notIn(array_map('strtolower', $choices))
        ]
    ];
}

Upvotes: 5

Related Questions