Pull to refresh

Работа с битовой маской

Reading time2 min
Views46K
Доброго времени суток всем!
Часто возникает необходимость хранить данные логического типа для определенных таблиц. Например, таблица пользователей, такими данными могут быть поля активация пользователя, блокирование пользователя и др. Для таких полей удобно использовать битовую маску, при которой все данные хранятся в одном поле таблицы. Последнее время работаю с фреймверком Yii. Мне он нравится и во всем устраивает. Так вот, в процессе работы над несколькими проектами у меня появилось ряд наработок для работы с битовой маской под этот фреймверк.
Ими и хочу поделиться.

И так, создаем модель, наследуя от ActiveRecord:

class BaseActiveRecordClass  extends CactiveRecord


Объявляем флаги. Для базового класса задаем флаг блокировки, который может быть использован для большинства моделей.

const FLAG_MANAGE_BLOCKED=0;    //флаг блокировки


Значение константы соответствует номеру бита. Если биты будут одинаковые, флаги будут перетираться.
Дальше пишем методы для работы с флагами:


/*
     * функция установки флага модели
     */
    public function setFlag($idBit=0,$bit=1){
        $bitFlags=1<<$idBit;
        if($bit==0){
            $this->flags=$this->flags&(~$bitFlags);
        }else{
            $this->flags=$this->flags|$bitFlags;
        }
        $this->save(true, array('flags'));
    }
    /*
     * фунукция получения флага
     */
    public function getFlag($idBit){
        $flag=(int)$this->flags;
        $flag=$flag>>$idBit;
        if($flag>0)
            $cBits=log($flag,2);
        else $cBits=0;
        $newFlag=$flag|1;
        if($newFlag==$flag)
            return 1;
        else
            return 0;
    }
    /*
     * функция фильтрации по флагам в базе данных
     * param $flags array список флагов для фильтрации
     */
    public function addFlagCriteria($flags=array()){
        $criteria=$this->getDbCriteria();
        if(!empty($flags)){
            foreach($flags as $bit => $flag){
                if(is_array($flag)){
                    $operator=($flag['operator'])?$flag['operator']:"and";
                    $check=($flag['check'])?(bool)$flag['check']:1;
                }else{
                    $operator="and";
                    $check=(bool)$flag;
                }
                $check=$check?"=":"<>";
                $criteria->addCondition("(((t.flags>>".$bit.")|1)".$check."(t.flags>>".$bit."))", $operator);
            }
        }
        return $this;
    }
    /*
     * пример фильтрации в базе по флагам
     * отфильтровывает незаблокированные сущности
     */
    public function noBlock(){
        return $this->addFlagCriteria(array(self::FLAG_MANAGE_BLOCKED=>0));
    }


Модели, которые должны работать с флагами делаем наследниками этого класса и добавляем в таблицу поле flags.
Теперь, например, чтобы проверить, заблокирована ли новость, можно выполнить:

$model= News::model()->findByPk($id);
if($model->getFlag(News::FLAG_MANAGE_BLOCKED)){
    die("Новость заблокирована");
}

или чтобы заблокировать новость:
$model->setFlag(News::FLAG_MANAGE_BLOCKED, 1);


или, например, чтобы отфильтровать незаблокированные новости:
$news=News::model()->noBlock()->findAll();


В модель можно добавить столько флагов, сколько позволяет разрядность типа поля в базе данных.

Если материал оказался полезным, я буду рад опубликовать продолжение данной темы, рассказав о готовых средствах администрирования флагов через CgridView и экшн с базовыми средствами настройки.
Tags:
Hubs:
+21
Comments37

Articles