|
@@ -0,0 +1,85 @@
|
|
|
|
|
+# Bitmasked Behavior
|
|
|
|
|
+
|
|
|
|
|
+A CakePHP behavior to allow quick row-level filtering of models via bitmasks.
|
|
|
|
|
+
|
|
|
|
|
+## Introduction
|
|
|
|
|
+Basically it encodes the array of bit flags into a single bitmask on save and vice versa on find.
|
|
|
|
|
+I created it as an extension of my pretty well working Enum stuff. It can use this type of enum declaration for our bitmask, as well.
|
|
|
|
|
+It uses constants as this is the cleanest approach to define model based field values that need to be hardcoded in your application.
|
|
|
|
|
+
|
|
|
|
|
+### Technical limitation
|
|
|
|
|
+The theoretical limit for a 64-bit integer [SQL: BIGINT unsigned] would be 64 bits (2^64).
|
|
|
|
|
+Don’t use bitmasks if you seem to need more than a hand full, though.
|
|
|
|
|
+Then you obviously do something wrong and should better use a join table.
|
|
|
|
|
+I highly recommend using `tinyint(3) unsigned` which can hold up to 8 bits – more than enough. It still only needs 1 byte.
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+## Usage
|
|
|
|
|
+Attach it to your model's `Table` class in its `initialize()` method like so:
|
|
|
|
|
+```php
|
|
|
|
|
+$this->addBehavior('Tools.Bitmasked', $options);
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+If you want to alias the field for output:
|
|
|
|
|
+```php
|
|
|
|
|
+$this->addBehavior('Tools.Bitmasked', ['mappedField' => 'statuses', 'field' => 'status']);
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+The `mappedField` param is quite handy if you want more control over your bitmask.
|
|
|
|
|
+It stores the array under this alias and does not override the bitmask key.
|
|
|
|
|
+So in our case status will always contain the integer bitmask and statuses the verbose array of it.
|
|
|
|
|
+
|
|
|
|
|
+### Defining the selectable values
|
|
|
|
|
+We first define values and make sure they follow the bitmask scheme:
|
|
|
|
|
+```
|
|
|
|
|
+1, 2, 4, 8, 16, 32, 64, 128, ...
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+I recommend using a DRY [enum approach](http://www.dereuromark.de/2010/06/24/static-enums-or-semihardcoded-attributes/), using your entity:
|
|
|
|
|
+```php
|
|
|
|
|
+// A bunch of bool values
|
|
|
|
|
+const STATUS_ACTIVE = 1;
|
|
|
|
|
+const STATUS_FEATURED = 2;
|
|
|
|
|
+const STATUS_APPROVED = 4;
|
|
|
|
|
+const STATUS_FLAGGED = 8;
|
|
|
|
|
+
|
|
|
|
|
+...
|
|
|
|
|
+
|
|
|
|
|
+public static function statuses($value = null) {
|
|
|
|
|
+ $options = [
|
|
|
|
|
+ self::STATUS_ACTIVE => __('Active'),
|
|
|
|
|
+ self::STATUS_FEATURED => __('Featured'),
|
|
|
|
|
+ self::STATUS_APPROVED => __('Approved'),
|
|
|
|
|
+ self::STATUS_FLAGGED => __('Flagged'),
|
|
|
|
|
+ ];
|
|
|
|
|
+ return parent::enum($value, $options);
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+Please note that you need to define Entity::enum() by extending my Tools Entity base class or by putting it into your own base class manually. You don’t have to use the enum approach, though.
|
|
|
|
|
+
|
|
|
|
|
+Of course it only makes sense to use bitmasks, if those values can co-exist, if you can select multiple at once. Otherwise you would want to store them separately anyway.
|
|
|
|
|
+Obviously you could also just use four or more boolean fields to achieve the same thing.
|
|
|
|
|
+
|
|
|
|
|
+So now, in the add/edit form we can:
|
|
|
|
|
+```php
|
|
|
|
|
+echo $this->Form->input('statuses', ['options' => Comment::statuses(), 'multiple' => 'checkbox']);
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+Tip: Usually, you have passed down the current entity for the form building anyway, then you don't need static access:
|
|
|
|
|
+```php
|
|
|
|
|
+echo $this->Form->create($comment);
|
|
|
|
|
+echo $this->Form->input('statuses', ['options' => $comment->statuses(), 'multiple' => 'checkbox']);
|
|
|
|
|
+...
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+It will save the final bitmask to the database field `status` as integer. For example "active and approved" would become `9`.
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+### Outview
|
|
|
|
|
+
|
|
|
|
|
+You can read more about how it began in [my blog post](http://www.dereuromark.de/2012/02/26/bitmasked-using-bitmasks-in-cakephp/).
|
|
|
|
|
+
|
|
|
|
|
+If you want to use a more DB or Config driven approach towards enums, you can also look into other plugins and CakePHP resources available, e.g. [this](https://github.com/CakeDC/Enum) implementation.
|