Skip to content

Conversation

mostafaznv
Copy link

@mostafaznv mostafaznv commented Aug 9, 2025

This pull request introduces comprehensive support for bitmask-based enums in Laravel Eloquent models, including a new attribute cast (AsBitmask), expressive query scopes for bitmask columns, and thorough documentation and test coverage. The changes make it much easier to store, retrieve, and query sets of enum values efficiently in a single database column.

Laravel Bitmask Enum Support

  • New Eloquent Attribute Cast:
  • Adds AsBitmask cast to allow storing and retrieving multiple enum values as a single integer column in Eloquent models. Handles conversion from arrays, strings, single enum cases, and Bitmask objects, and supports serialization.
  • Documentation added for using bitmask enums with Eloquent attribute casting, including usage examples and configuration. (docs/casting.md, docs/bitmasks.md)

  • Bitmask Query Scopes:
  • Introduces InteractsWithBitmask trait providing whereBitmask and orWhereBitmask query scopes for expressive filtering of bitmask columns.
  • Documentation for these query scopes and how to use them in Eloquent models. (docs/laravel.eloquent.md)

Testing and Fixtures

  • Adds BitmaskPreferenceEnum as a fixture for testing bitmask enum functionality.
  • Adds CastsBitmaskEnumsModel for testing Eloquent integration with bitmask enums.

CI Workflow Enhancements
Expands GitHub Actions test matrix to cover multiple PHP and Laravel versions, and adjusts Composer install steps accordingly.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't want these changes. A lower and upper bound ensures that something works in between as well since php and laravel are very strict on breaking changes. Unless you have an edge case that requires testing on versions in between, we can simply do with what we had.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are always features that are not present in early Laravel versions. For example, the $withoutObjectCaching property inside Laravel’s custom casts was added to L10.x, L11.x, and L12.x recently and is not present in, for example, 10.0.0. So I changed the dependency version to prefer-stable and removed the prefer-lowest option.

As for the PHP/Laravel matrix, I can remove PHP 8.2 and 8.3, as well as Laravel 11.*

Copy link
Owner

@henzeb henzeb Aug 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's where the composer.json comes in. But there is already casting for enums and that never failed tests on lowest versions.

There is more wrong with this PR than just this.

  • I have switched to Pest, so it would be nice to have pest tests here.
  • I am not sure about the casting implementation as it uses the Bitmask helper class directly and I do not understand the why. The Laravel out of the box casting already stores and creates from the integer
  • querying on bitmasks seems interesting, but for each combination of cases, the enum should have a case that combines it all. (for Permission Read and Write you should have a ReadWrite case)

Copy link
Author

@mostafaznv mostafaznv Aug 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it did not fail on the lowest versions before because there weren’t any newly added features on your side. But since I implemented a custom cast, and it must not be cached, I had to use $withoutObjectCaching, which is quite new and causes tests to fail.

  • I will try my best to convert tests to Pest.
  • I’m not sure Laravel’s casting feature can handle bitmasks. We need to convert bitmasks to integers during saves and convert integers to bitmasks during retrievals. So I implemented a custom cast to take over this responsibility.
  • The ReadWrite case is a specific use case and is only related to the permissions system. But I wanted to make it possible for users to query over multiple cases. For example, imagine notification preferences in an iOS application where we want to give users the ability to enable/disable receiving notifications in multiple scenarios (for example, when a post is liked, when someone follows them, when a direct message is received, …).
    One scenario is to create a related table and store all these preferences key by key. The other scenario is to use bitwise operations in SQL (SELECT * FROM users WHERE flags & 1 = 1;)

Copy link
Author

@mostafaznv mostafaznv Aug 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well,
I switched the tests to Pest, and I also reverted the tests.yml file to the original one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants