Work with a bit mask

    Good day to all!
    Often there is a need to store data of a logical type for certain tables. For example, a user table, such data may be user activation fields, user blocking, etc. For such fields, it is convenient to use a bitmask in which all data is stored in one field of the table. Recently I have been working with the Yii framework. I like him and are happy with everything. So, in the process of working on several projects, I had a number of developments for working with a bit mask for this framework.
    I want to share them.

    And so, we create the model, inheriting from ActiveRecord:

    class BaseActiveRecordClass  extends CactiveRecord


    We declare the flags. For the base class, set the lock flag, which can be used for most models.

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


    The constant value corresponds to the bit number. If the bits are the same, the flags will be frayed.
    Next, we write methods for working with flags:

    
    /*
         * функция установки флага модели
         */
        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));
        }


    We make the models that should work with flags the heirs of this class and add the flags field to the table.
    Now, for example, to check if the news is blocked, you can do:

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

    or to block the news:
    $model->setFlag(News::FLAG_MANAGE_BLOCKED, 1);


    or, for example, to filter out unblocked news:
    $news=News::model()->noBlock()->findAll();


    You can add as many flags to the model as the capacity of the field type in the database allows.

    If the material turned out to be useful, I will be glad to publish a continuation of this topic, telling about ready-made tools for flag administration through CgridView and action with basic customization tools.

    Also popular now: