15

Given an array of integers:

[1, 2, 3, 4, ...]

How can I use the validator to check if each of these exists in a table? Possible without a foreach loop?

$validator = Validator::make($request->all(), [
    'array' => 'required|exists:users,id'
]);
user7675955
  • 153
  • 1
  • 1
  • 4

4 Answers4

50

Your validation as written should work. If the exists validation is used with an array, it will automatically use where in for the exists query.

So, given your validation as you have written, the validation will get the count of the users records where the id field is in the list of ids provided by your array input.

Therefore, if your array is [1, 2, 3, 4], it will get the count where users.id in (1,2,3,4), and compare that to the count of the elements in your array array (which is 4). If the query count is >= the array count, validation passes.

Two things to be careful about here: if the column you're checking is not unique, or if your array data has duplicate elements.

If the column you're checking is not unique, it's possible your query count will be >= the array count, but not all ids from your array actually exist. If your array is [1, 2, 3, 4], but your table has four records with id 1, validation will pass even though records with ids 2, 3, and 4 don't exist.

For duplicate array values, if your array was [1, 1], but you only have one record with an id of 1, validation would fail because the query count will be 1, but your array count is 2.

To work around these two caveats, you can do individual array element validation. Your rules would look something like:

$request = [
    'ids' => [1, 2, 3, 4],
];

$rules = [
    'ids' => 'required|array',
    'ids.*' => 'exists:users,id', // check each item in the array
];

$validator = Validator::make($request, $rules);

dd($validator->passes(), $validator->messages()->toArray());

Keep in mind that each element will be validated individually, so it will run a new query for each element in the ids array.

patricus
  • 59,488
  • 15
  • 143
  • 145
  • 4
    Is there a way to do a bulk check, i.e. check all in a single query? – user7675955 Mar 08 '17 at 06:17
  • 1
    Its worth mentioning that the following code works if the input is a comma separated string `$validator = Validator::make($request->all(), [ 'array' => 'required|exists:users,id' ]);` – Abdelalim Hassouna Sep 01 '20 at 10:25
4

You can make your custom rule:

public function validateArrayInt($attribute, $value, $parameters){  
    return array_filter(value, 'is_int')
}

Then:

$validator = Validator::make($request->all(), [
    'array' => ['required', 'array_int', 'exists:users,id']
]);
Community
  • 1
  • 1
manix
  • 14,537
  • 11
  • 70
  • 107
0

If you're looking a solution that will:

  • work with duplicate values
  • and return specific error messages for values not found in the database executing a single SQL query,

you can use a custom rule like the one below:

namespace App\Rules;

use Illuminate\Contracts\Validation\InvokableRule;
use Illuminate\Support\Facades\DB;

class AllExist implements InvokableRule
{
    public function __construct(private string $table, private string $column) {}

    public function __invoke($attribute, $value, $fail)
    {
        if (!is_array($value)) return $fail('The value of :attribute must be an array.');
        if (empty($this->table) || empty($this->column)) return $fail('The table and column must be set for.');
        if (empty($value)) return;

        try {
            $value = array_unique($value);
            $dbIds = DB::table($this->table)
                ->selectRaw("distinct {$this->column} id")
                ->whereIn($this->column, $value)
                ->pluck('id');
            foreach ($value as $v) {
                if (!$dbIds->contains($v)) {
                    return $fail("The value '$v' of :attribute does not exist in {$this->table} table under {$this->column} column.");
                }
            }
        } catch (\Exception $e) {
            return $fail('Validation failed: ' . $e->getMessage());
        }
    }
}

Using the custom rule:

$validator = Validator::make($request, [
    'array' => ['required', 'array', new AllExist('users', 'id')],
]);

if ($validator->fails()) {
    // TODO: handle errors
}

Mateusz
  • 2,340
  • 25
  • 24
-5

Try this to check whether your response array is json.

return response()->json($yourArr)->setEncodingOptions(JSON_NUMERIC_CHECK);
Pathros
  • 10,042
  • 20
  • 90
  • 156
Heartbeat
  • 142
  • 9