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

Binary Flags Laravel Package

reinder83/binary-flags

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require reinder83/binary-flags
    
  2. Basic Usage:

    use Reinder83\BinaryFlags\BinaryFlags;
    
    $flags = new BinaryFlags();
    $flags->setFlag(1); // Set flag 1
    $flags->toggleFlag(2); // Toggle flag 2 (sets it)
    $flags->unsetFlag(3); // Unset flag 3 (does nothing if not set)
    
    if ($flags->hasFlag(1)) {
        // Flag 1 is set
    }
    
  3. Database Storage:

    • Store as UNSIGNED BIGINT in MySQL (or equivalent in other DBs).
    • Serialize/deserialize with:
      $flags->fromInt($storedValue); // Load from DB
      $storedValue = $flags->toInt(); // Save to DB
      
  4. First Use Case:

    • Model attributes (e.g., user roles, permissions):
      use Reinder83\BinaryFlags\Traits\InteractsWithNumericFlags;
      
      class User extends Model
      {
          use InteractsWithNumericFlags;
      
          protected $flags = [];
      }
      

Implementation Patterns

Core Workflows

1. Model Integration

  • Trait Usage:
    use Reinder83\BinaryFlags\Traits\InteractsWithNumericFlags;
    
    class Post extends Model
    {
        use InteractsWithNumericFlags;
    
        protected $flags = [
            'is_published' => 1,
            'is_featured' => 2,
            'is_archived' => 4,
        ];
    
        public function publish()
        {
            $this->setFlag('is_published');
            $this->save();
        }
    }
    
  • Enum-Based Flags (Laravel 8.43+):
    use Reinder83\BinaryFlags\BinaryEnumFlags;
    
    enum PostStatus: int
    {
        PUBLISHED = 1,
        DRAFT = 2,
        ARCHIVED = 4;
    }
    
    $flags = new BinaryEnumFlags(PostStatus::PUBLISHED);
    $flags->toggleFlag(PostStatus::ARCHIVED);
    

2. Database Operations

  • Migration:
    Schema::create('posts', function (Blueprint $table) {
        $table->id();
        $table->unsignedBigInteger('flags')->default(0);
        // ...
    });
    
  • Query Filtering:
    $publishedPosts = Post::where('flags', '>=', 1)->get();
    // Or use a query scope:
    $posts->whereHasFlag('is_published');
    

3. Bitmask Operations

  • Combining Flags:
    $mask = $flags->getMask(['is_published', 'is_featured']);
    $flags->setFlags($mask); // Set multiple flags at once
    
  • Checking Multiple Flags:
    if ($flags->hasAnyFlag([1, 2])) {
        // At least one of flag 1 or 2 is set
    }
    

4. Validation

  • Form Request:
    public function rules()
    {
        return [
            'flags' => ['required', 'integer', function ($attribute, $value, $fail) {
                $flags = new BinaryFlags($value);
                if (!$flags->hasFlag(1) && !$flags->hasFlag(2)) {
                    $fail('At least one permission flag is required.');
                }
            }],
        ];
    }
    

Integration Tips

Laravel Eloquent

  • Accessors/Mutators:
    public function getFlagsAttribute($value)
    {
        return (new BinaryFlags($value))->toArray($this->flags);
    }
    
    public function setFlagsAttribute($value)
    {
        $this->attributes['flags'] = (new BinaryFlags())->fromArray($value)->toInt();
    }
    

API Responses

  • JSON Serialization:
    protected $appends = ['flags'];
    
    public function getFlagsAttribute()
    {
        return (new BinaryFlags($this->attributes['flags']))->toArray($this->flags);
    }
    

Testing

  • Factories:
    $factory->define(Post::class, function (Faker $faker) {
        return [
            'flags' => (new BinaryFlags())->setFlag(1)->toInt(),
        ];
    });
    

Gotchas and Tips

Pitfalls

  1. Bit Depth Limitations:

    • 32-bit systems: Max 32 flags (values 1 to 2^32-1).
    • 64-bit systems: Max 64 flags (values 1 to 2^64-1).
    • Fix: Validate flag values before use:
      if ($flag > (PHP_INT_MAX >> 1)) {
          throw new \InvalidArgumentException('Flag exceeds system bit depth.');
      }
      
  2. Floating-Point Deprecation:

    • Avoid passing float values (e.g., 1.0 instead of 1). Use int explicitly.
    • Workaround: Cast inputs:
      $flags->setFlag((int) $request->input('flag'));
      
  3. Database Collisions:

    • Ensure UNSIGNED BIGINT is used (not INT or SMALLINT).
    • Migration Check:
      $table->unsignedBigInteger('flags')->default(0);
      
  4. Trait Conflicts:

    • Avoid naming conflicts with existing flags property/methods.
    • Solution: Use explicit trait namespaces or rename properties:
      use InteractsWithNumericFlags as BinaryFlagsTrait;
      
  5. Enum Pitfalls:

    • Enums must implement Bitmaskable or use BinaryEnumFlags:
      enum Status implements Bitmaskable {
          ACTIVE = 1;
          // ...
      }
      

Debugging Tips

  1. Flag Visualization:

    • Log binary representation for debugging:
      \Log::debug('Flags binary:', [
          'value' => $flags->toInt(),
          'binary' => decbin($flags->toInt()),
      ]);
      
  2. Common Issues:

    • Flag Not Setting?
      • Check for duplicate flag values (e.g., 1 and 1 << 0 are the same).
      • Verify bit shifting: Use 1 << $index for dynamic flags.
    • Database Corruption?
      • Reset flags with:
        $flags->reset();
        
  3. Performance:

    • Batch operations (e.g., setFlags()) are faster than individual setFlag() calls.
    • Avoid hasFlag() in loops; cache results if checking repeatedly.

Extension Points

  1. Custom Storage:

    • Extend BinaryFlags to support alternative storage (e.g., Redis):
      class RedisBinaryFlags extends BinaryFlags
      {
          public function __construct($key, $initialValue = 0)
          {
              $this->key = $key;
              $this->fromInt($initialValue);
          }
      
          public function toInt()
          {
              return (int) Redis::get($this->key);
          }
      
          public function save()
          {
              Redis::set($this->key, $this->toInt());
          }
      }
      
  2. Validation Rules:

    • Create a custom rule for flag constraints:
      use Reinder83\BinaryFlags\BinaryFlags;
      
      class FlagsRule extends Rule
      {
          public function passes($attribute, $value)
          {
              $flags = new BinaryFlags($value);
              return $flags->hasFlag(1) || $flags->hasFlag(2);
          }
      }
      
  3. Query Builder Extensions:

    • Add scopes to Eloquent:
      public function scopeHasFlag(Builder $query, string $flagName)
      {
          return $query->where('flags', '>=', $this->flags[$flagName]);
      }
      
  4. Event Triggers:

    • Dispatch events on flag changes:
      use Reinder83\BinaryFlags\Traits\InteractsWithNumericFlags;
      
      class User extends Model
      {
          use InteractsWithNumericFlags;
      
          protected $flags = ['is_admin' => 1];
      
          protected static function booted()
          {
              static::saved(function ($model) {
                  if ($model->wasChanged('flags')) {
                      event(new FlagsUpdated($model));
                  }
              });
          }
      }
      
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.
milito/query-filter
apiboxsym/user-bundle
apiboxsym/health-check-bundle
jayeshmepani/jpl-moshier-ephemeris-php
elnasnato/laraliveui
labrodev/rest-sdk
sampaui/sampaui
babelqueue/php-sdk
facebook/capi-param-builder-php
babelqueue/symfony
hamzi/corewatch
minionfactory/raw-hydrator
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours