Selective is a Laravel package that provides super-fast, Redis bloom filter-backed validation rules for unique and exists checks.
When you have millions of rows, running standard unique:users or exists:users validation on every request can be expensive, causing database strain and slowing down your application.
Selective solves this by adding a probabilistic data structure (a Bloom Filter) in front of your database. When validating, Selective checks the bloom filter in Redis first:
- If the bloom filter says the record doesn't exist, validation passes (or fails for
exists) instantly without touching the database. - If the bloom filter says the record might exist, Selective automatically falls back to standard database validation to confirm.
- PHP 8.2+
- Laravel 10.0+ or 11.0+
- Redis with the RedisBloom module installed (e.g.
redis-stack) - Predis or PhpRedis extension
You can install the package via composer:
composer require abdelrahmandwedar/selectivePublish the config file:
php artisan vendor:publish --tag="selective-config"Use the rules exactly like you would use Laravel's standard rules.
Validates that a given attribute does not exist in a database table.
use AbdelrahmanDwedar\Selective\Rules\BloomUnique;
$request->validate([
'email' => ['required', 'email', new BloomUnique('users', 'email')],
]);Ignoring a given ID (e.g. during updates):
new BloomUnique('users', 'email')->ignore($user->id)Validates that a given attribute exists in a database table.
use AbdelrahmanDwedar\Selective\Rules\BloomExists;
$request->validate([
'email' => ['required', 'email', new BloomExists('users', 'email')],
]);To keep your bloom filters automatically updated when you create or update records, use the HasBloomFilters trait on your Eloquent models:
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use AbdelrahmanDwedar\Selective\Traits\HasBloomFilters;
class User extends Model
{
use HasBloomFilters;
// Define which columns should be synced to bloom filters
protected array $bloomFilters = ['email', 'username'];
}Note: Bloom filters do not support deletion. When a model is deleted, the item remains in the filter. This is fine because the fallback database check will handle the "false positive" when checking the deleted item.
If you are adding this package to an existing project, you need to populate the bloom filters with your existing data:
php artisan bloom:seed users emailYou can use the --fresh option to delete any existing filter and recreate it, or --error-rate and --capacity to override defaults.
View information about your bloom filters:
# Show info for a specific filter
php artisan bloom:status users email
# Show info for all selective filters
php artisan bloom:statusDelete bloom filters:
# Clear a specific filter
php artisan bloom:clear users email
# Clear all selective filters
php artisan bloom:clear --allYou can configure the package in config/selective.php:
default_error_rate: The acceptable probability of false positives (default:0.01or 1%).default_capacity: The default expected capacity of the bloom filter (default:1000).redis_connection: The Redis connection to use fromconfig/database.php(default:'default').key_prefix: Prefix for Redis keys (default:'selective:').fallback_on_error: If true, gracefully falls back to DB validation if Redis is down (default:true).auto_reserve: Automatically create filters if they don't exist (default:true).
- You define a rule:
new BloomUnique('users', 'email'). - A user submits an email.
- Selective checks the Redis bloom filter
selective:users:email. - If the filter returns
0(definitely not in filter): validation passes instantly. - If the filter returns
1(might be in filter): Selective queries theuserstable to confirm if it's a real hit or a false positive. - Validation passes or fails based on the DB result.
The MIT License (MIT). Please see License File for more information.