fanmade/laravel-bitwise-trait

在任意类上使用位运算符的简单特质

2.0.0 2021-11-28 23:02 UTC

This package is auto-updated.

Last update: 2024-08-29 04:25:35 UTC


README

这是一个在任意类上使用位运算符的简单特质,灵感来源于 https://php.ac.cn/manual/de/language.operators.bitwise.php#108679

在阅读了这篇博客后进行了更新: https://aaronfrancis.com/2021/bitmasking-in-laravel-and-mysql

我目前只在Laravel中使用了它,但你应该可以在经过一些小的修改后在任何其他地方使用它。

PHP 版本

版本 v2.* 需要 PHP 7.4+。如果你还使用着旧版本,请使用 v1.*

安装

你可以通过 composer 安装此包

composer require fanmade/laravel-bitwise-trait

这就完了,不需要注册提供者 :)

用法

以下示例是针对默认 Laravel (5.4) 模型中的 "App" 命名空间。

你需要一个(理想情况下无符号)整数字段在数据库中存储属性。长度取决于你想要存储的值的数量。每个值只需要一个比特位,所以如果列是无符号的,每字节可以有8个值。

示例(基于 Laravel 迁移)

$table->tinyInteger('status'); // 1 byte -> maximum of 7 different values
$table->unsignedTinyInteger('status'); // maximum of 8 different values
$table->smallInteger('status'); // 2 byte -> maximum of 16 different values
$table->unsignedSmallInteger('status'); // maximum of 17 different values
$table->mediumInteger('status'); // 3 byte -> maximum of 24 different values

你大概明白了。大多数时候你可能只需要一个无符号的小整型 :)

对于多于一个数据库字段的使用场景只有少数几种,但你可以添加你想要的任意数量的字段。

在你的模型中这样包含特质

<?php 

namespace App;

use Fanmade\Bitwise\BitwiseFlagTrait;

class Message extends Model
{
  use BitwiseFlagTrait;

定义属性的最佳方式是直接在模型中通过常量。当然,你也可以自由使用配置变量或任何你喜欢的。

const MESSAGE_SENT     = 1; // BIT #1 of has the value 1
const MESSAGE_RECEIVED = 2; // BIT #2 of has the value 2
const MESSAGE_SEEN     = 4; // BIT #3 of has the value 4
const MESSAGE_READ     = 8; // BIT #4 of has the value 8

这种替代语法可能更容易阅读

const MESSAGE_SENT     = 1 << 0;
const MESSAGE_RECEIVED = 1 << 1;
const MESSAGE_SEEN     = 1 << 2;
const MESSAGE_READ     = 1 << 3;

或者直接使用二进制表示

const MESSAGE_SENT     = 0b00000001;
const MESSAGE_RECEIVED = 0b00000010;
const MESSAGE_SEEN     = 0b00000100;
const MESSAGE_READ     = 0b00001000;

要设置属性,只需像这样调用函数

$this->setFlag('status', self::MESSAGE_SENT, true);

要获取属性,只需像这样调用函数

$sent = $this->getFlag('status', self::MESSAGE_SENT);

第一个参数 ('status' 在示例中) 总是你在数据库中设置的列。也许你想要在常量或变量中定义它。

为了使你的生活更简单,我建议使用自定义的获取器和设置器。

    public function setSentAttribute($sent = true): self
    {
        $this->setFlag('status', self::MESSAGE_SENT, $sent);
        
        return $this;
    }

    public function getSentAttribute(): bool
    {
        return $this->getFlag('status', self::MESSAGE_SENT);
    }

作用域

如果你想在使用作用域时使用新字段,可以这样做

    /**
     * @param Builder $query
     * @return Builder
     */
    public function scopeUnread($query)
    {
        return $query->whereRAW('NOT status & ' . self::MESSAGE_READ);
    }

    /**
     * @param Builder $query
     * @return Builder
     */
    public function scopeRead($query)
    {
        return $query->where('status', '&', self::MESSAGE_READ);
    }