Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Model Flags Laravel Package

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.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Install the package:
    composer require spatie/laravel-model-flags
    
  2. Publish migrations and run them:
    php artisan vendor:publish --tag="model-flags-migrations"
    php artisan migrate
    
  3. Use the trait in your model:
    use Spatie\ModelFlags\Models\Concerns\HasFlags;
    
    class User extends Model
    {
        use HasFlags;
    }
    

First Use Case: Idempotent Operations

Start by flagging a model to mark it as processed:

$user->flag('processed_payment');

Check if a model has a flag:

if (!$user->hasFlag('processed_payment')) {
    // Process payment logic
    $user->flag('processed_payment');
}

Implementation Patterns

Core Workflows

1. Flagging and Checking

  • Single flag:
    $user->flag('verified_email');
    $user->hasFlag('verified_email'); // true
    
  • Multiple flags:
    $user->flag(['verified_email', 'active_subscription']);
    $user->hasFlag('verified_email'); // true
    $user->hasFlag('active_subscription'); // true
    

2. Querying Flagged Models

  • Scoped queries:
    User::flagged('verified_email')->get(); // All users with 'verified_email'
    User::notFlagged('verified_email')->get(); // All users without 'verified_email'
    
  • Combining scopes:
    User::flagged('verified_email')
         ->notFlagged('active_subscription')
         ->get();
    

3. Bulk Operations

  • Flag all models in a collection:
    User::where('role', 'admin')->get()->each->flag('admin_notified');
    
  • Unflag all models:
    User::flagged('admin_notified')->each->unflag('admin_notified');
    

4. Tracking Flag Timestamps

  • Last flagged time:
    $user->lastFlaggedAt('verified_email'); // Carbon instance
    $user->lastFlaggedAt(); // Last flagged time across all flags
    

Integration Tips

1. Artisan Commands

Use flags to make commands idempotent:

// In an Artisan command
User::notFlagged('sent_welcome_email')
    ->each(function (User $user) {
        Mail::to($user->email)->send(new WelcomeEmail());
        $user->flag('sent_welcome_email');
    });

2. Event Listeners

Trigger actions when flags are set:

use Spatie\ModelFlags\Events\Flagged;

Flagged::listen(function (Flagged $event) {
    if ($event->flagName === 'verified_email') {
        event(new UserVerified($event->model));
    }
});

3. API Responses

Include flag status in API responses:

return UserResource::make($user)->additional([
    'flags' => $user->flagNames(),
]);

4. Testing

Mock flags in tests:

$user = User::factory()->create();
$user->shouldReceive('flag')->once()->with('test_flag');

Gotchas and Tips

Pitfalls

1. Flag Name Collisions

  • Ensure flag names are unique across your application. Reusing names (e.g., 'active' for different purposes) can lead to unintended behavior.
  • Solution: Use namespaced flags (e.g., 'auth.verified_email', 'billing.active_subscription').

2. Performance with Large Datasets

  • Scoping queries (flagged(), notFlagged()) can be slow on large tables.
  • Solution: Add an index to the flags table:
    Schema::table('flags', function (Blueprint $table) {
        $table->index(['model_type', 'model_id', 'name']);
    });
    

3. Flag Persistence

  • Flags are not deleted when the parent model is deleted (unless using Laravel 11+ with the 1.1.0+ update).
  • Solution: Manually delete flags or use a model observer:
    User::observe(FlagObserver::class);
    
    class FlagObserver {
        public function deleted(User $user) {
            $user->flags()->delete();
        }
    }
    

4. Concurrency Issues

  • Race conditions can occur when multiple processes flag/unflag the same model simultaneously.
  • Solution: Use database transactions or optimistic locking:
    DB::transaction(function () use ($user) {
        if (!$user->hasFlag('processed')) {
            $user->flag('processed');
            // Process logic
        }
    });
    

Debugging Tips

1. Check Flag Existence

  • Verify flags exist in the database:
    \DB::table('flags')->where('name', 'verified_email')->exists();
    

2. Inspect Flag Relationships

  • Debug the flags relation:
    $user->flags()->with('flaggable')->get();
    

3. Log Flag Operations

  • Add logging for critical flags:
    $user->flag('admin_action');
    \Log::info("Flagged user {$user->id} with 'admin_action'");
    

Extension Points

1. Custom Flag Model

  • Override the default Flag model in config/model-flags.php:
    'flag_model' => App\Models\CustomFlag::class,
    
  • Extend functionality (e.g., add metadata):
    class CustomFlag extends \Spatie\ModelFlags\Models\Flag {
        protected $casts = [
            'metadata' => 'array',
        ];
    }
    

2. Custom Flag Names

  • Use enums for type safety (Laravel 10+):
    use Spatie\ModelFlags\Enums\FlagName;
    
    $user->flag(FlagName::VerifiedEmail);
    

3. Soft Deletes

  • Enable soft deletes for flags:
    use Illuminate\Database\Eloquent\SoftDeletes;
    
    class Flag extends \Spatie\ModelFlags\Models\Flag {
        use SoftDeletes;
    }
    

4. Event Customization

  • Extend the Flagged event:
    class CustomFlagged implements ShouldBroadcast {
        public function broadcastOn() {
            return new PrivateChannel('flags');
        }
    }
    

Pro Tips

1. Flag Groups

  • Group related flags (e.g., 'auth.*') and use a prefix:
    $user->flag('auth.verified');
    $user->hasFlag('auth.*'); // Wildcard check (requires custom scope)
    

2. Flag Expiry

  • Add a expires_at column to flags and check expiry:
    $user->flags()->where('expires_at', '>', now())->exists();
    

3. Flag Analytics

  • Track flag usage:
    \DB::table('flags')
        ->where('name', 'verified_email')
        ->count(); // Total flagged models
    

4. Flag Migration

  • Customize the flags table migration:
    Schema::create('flags', function (Blueprint $table) {
        $table->id();
        $table->string('name');
        $table->string('model_type');
        $table->unsignedBigInteger('model_id');
        $table->timestamps();
        $table->json('metadata')->nullable(); // Add custom fields
    });
    

5. Flag Validation

  • Validate flag names in your application:
    use Illuminate\Support\Facades\Validator;
    
    $validator = Validator::make(['flag' => 'invalid flag'], [
        'flag' => 'required|regex:/^[a-z_]+(\.[a-z_]+)*$/i',
    ]);
    
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
nexmo/api-specification
capell-app/block-library
axium/identity
cetria/laravel-dummy-models
cetria/reflection-helper
agropredict/sso-auth-bundle
evolvestudio/spam-protection
datacore/hub-sdk
develia/commons
cuci/prototurk-sdk
cuci/prototurk-sdk-symfony
develia/geo-bundle
dreamzy/livewire-charts
touchestate-sdk/php-sdk
ecotone/kafka
22h/doctrine-garbage-collection-bundle
agtp/agtp-php
agtp/mod-php
splash/sonata-admin
splash/metadata