spatie/laravel-model-flags
Add lightweight “flags” to Eloquent models via a trait—store process state without extra columns. Check, set, and clear flags, and query with flagged/notFlagged scopes. Ideal for idempotent, restartable jobs like one-time emails or migrations.
composer require spatie/laravel-model-flags
php artisan vendor:publish --tag="model-flags-migrations"
php artisan migrate
use Spatie\ModelFlags\Models\Concerns\HasFlags;
class User extends Model
{
use HasFlags;
}
// Flag a user as having received a notification
$user->flag('received_notification');
// Check if a user has the flag
if (!$user->hasFlag('received_notification')) {
// Send notification
$user->flag('received_notification');
}
// Set a flag
$user->flag('processed');
// Check existence
if ($user->hasFlag('processed')) {
// Do something
}
// Remove a flag
$user->unflag('processed');
// Process only unflagged users
User::notFlagged('processed')
->chunk(100, function ($users) {
foreach ($users as $user) {
// Process user
$user->flag('processed');
}
});
// Get when a flag was last set
$lastProcessedAt = $user->lastFlaggedAt('processed');
// Get the most recent flag timestamp
$lastActivityAt = $user->lastFlaggedAt();
use Spatie\ModelFlags\Enums\FlagName;
class User extends Model
{
use HasFlags;
protected string $flagNameEnum = FlagName::class;
}
enum FlagName: string
{
case PROCESSED = 'processed';
case NOTIFIED = 'notified';
}
// Usage
$user->flag(FlagName::PROCESSED);
// Remove a flag from all models
Flag::where('name', 'old_flag')->delete();
// Idempotent command example
User::notFlagged('sent_email')
->each(function (User $user) {
Mail::to($user)->send(new WelcomeEmail());
$user->flag('sent_email');
});
// Flag on event trigger
event(new UserRegistered($user));
$user->flag('registered_via_event');
// Auto-flag on model creation
public function created(Model $model)
{
$model->flag('created_via_observer');
}
Missing Migration:
php artisan migrate after publishing migrations will cause ModelNotFoundException.Flag Name Collisions:
Performance with Large Datasets:
flagged() or notFlagged() can be slow on large tables. Consider adding database indexes:
Schema::table('flags', function (Blueprint $table) {
$table->index(['model_type', 'model_id', 'name']);
});
Model Deletion:
$model->flags()->delete() if needed.Enum Validation:
Check Flag Existence:
dd($user->flags); // Inspect all flags
dd($user->flagNames()); // Get flag names as array
Query Debugging:
// Debug scope queries
User::notFlagged('test')->toSql();
Timestamp Issues:
lastFlaggedAt() returns unexpected values, verify the flags table's updated_at column is properly populated.Custom Flag Model:
Flag model by publishing the config:
php artisan vendor:publish --tag="model-flags-config"
config/model-flags.php:
'flag_model' => App\Models\CustomFlag::class,
Custom Flag Storage:
Flag model to add custom logic (e.g., soft deletes, additional metadata).Bulk Operations:
flags()->delete() for bulk flag removal:
$user->flags()->where('name', 'temp_flag')->delete();
Array Unflagging (v1.5.0+):
// Remove multiple flags at once
$user->unflag(['flag1', 'flag2', 'flag3']);
// Flag only if condition is met
if ($user->shouldReceiveNotification()) {
$user->flag('notification_sent');
}
// Policy example
public function viewAny(User $user)
{
return $user->hasFlag('admin_access');
}
// Soft-expiry via timestamp checks
if ($user->lastFlaggedAt('temporary_flag') < now()->subHours(1)) {
$user->unflag('temporary_flag');
}
// In tests
$user->flag('test_flag');
$this->assertTrue($user->hasFlag('test_flag'));
// Reset flags
$user->flags()->delete();
How can I help you explore Laravel packages today?