# BitFlag 位标志 ### Configuration ##### 添加文件: `BitFlagBehavior.php` ```php [ * 标志位 => 标志名 * ] * ... * ] * @var array[] $bitFlags 位标志映射表 */ public $bitFlags = []; public function events() { return [ ActiveRecord::EVENT_BEFORE_INSERT => 'beforeSave', ActiveRecord::EVENT_BEFORE_UPDATE => 'beforeUpdate', ActiveRecord::EVENT_AFTER_FIND => 'afterFind', ActiveRecord::EVENT_AFTER_REFRESH => 'afterFind', ]; } public function beforeSave() { foreach ($this->bitFlags as $bitFlag => $flags) { $this->owner->$bitFlag = 0; $this->setBitFlag($bitFlag, $flags); } } public function beforeUpdate() { foreach ($this->bitFlags as $bitFlag => $flags) { $this->setBitFlag($bitFlag, $flags); } } public function afterFind() { $owner = $this->owner; foreach ($this->bitFlags as $bitFlag => $flags) { $bitFlagValue = $owner->$bitFlag; foreach ($flags as $pos => $flag) { $owner->$flag = ($bitFlagValue & 1 << $pos) >> $pos; } } } /** * 设置位标志 * @param string $bitFlag 位标志 * @param string[] $flags 标志 */ public function setBitFlag($bitFlag, $flags) { $owner = $this->owner; foreach ($flags as $pos => $flag) { $flagValue = $owner->$flag; if ($flagValue === null) { continue; } $owner->$bitFlag = $this->getBitFlagUpdate($owner->$bitFlag, $pos, $flagValue); } } /** * 获得更新后的位标志值 * @param int $bitFlagValue 位标志值 * @param int $pos 状态位置 * @param bool|int $flagValue 标志值 * @return int */ public function getBitFlagUpdate($bitFlagValue, $pos, $flagValue) { if ($flagValue) { $bitFlagValue = $bitFlagValue | 1 << $pos; } else { $bitFlagValue = $bitFlagValue & ~(1 << $pos); } return $bitFlagValue; } } ``` ##### 在需要的`Model`里添加`Behavior` ```php [ * 标志位 => 标志名 * ] * ... * ] */ const BIT_FLAGS = [ 'status' => [ 's0', 's1', 's2', 's3', ], // 'flags' => [ // 's5', 's6', 's7', 's8' // ] ]; public function fields() { $fields = []; foreach (self::BIT_FLAGS as $bitFlag) { $fields = ArrayHelper::merge($fields, array_combine($bitFlag, $bitFlag)); } return ArrayHelper::merge(parent::fields(), $fields); } public function behaviors() { return [ // 其他Behavior 'bitFlagBehavior' => [ 'class' => BitFlagBehavior::class, 'bitFlags' => self::BIT_FLAGS, ], ]; } /** * {@inheritdoc} * 重写find()返回自定义查询类 */ public static function find() { $config = ['bitFlags' => self::BIT_FLAGS]; return new MyModelQuery(get_called_class(), $config); } // 其他Model代码...... } ``` ##### 添加文件 `MyModelQuery` ```php [ * 标志位 => 标志名 * ] * ... * ] * @var array[] $bitFlags 融合状态表 */ public $bitFlags; /** * @param string[] $conditions * @return MyModelQuery */ public function andWhereBitFlags($conditions = []) { return $this->andWhere($this->getBitFlagsCondition($conditions)); } /** * @param string[] $conditions * @return MyModelQuery */ public function orWhereBitFlags($conditions = []) { return $this->orWhere($this->getBitFlagsCondition($conditions)); } /** * @param array $conditions * @return Expression */ public function getBitFlagsCondition($conditions = []) { $conditionsKeys = array_keys($conditions); foreach ($this->bitFlags as $bitFlag => $flags) { $mask = $res = 0; foreach ($flags as $pos => $flag) { if (in_array($flag, $conditionsKeys)) { $mask += 1 << $pos; $res += $conditions[$flag] << $pos; } } if ($mask != 0) { $bitFlagsConditions[] = "$bitFlag & $mask = $res"; } } return new Expression(implode(' and ', $bitFlagsConditions)); } } ``` ### Usage ##### Search ```php $myModels = MyModel::find() ->andWhereBitFlags(['s0' => 1, 's1' => 0]) ->orWhereBitFlags(['s2' => 0, 's3' => 1]) ->all(); ```