|
|
# BitFlag 位标志
### Configuration
##### 添加文件: `BitFlagBehavior.php`
```php <?php namespace {yourApp}\behaviors;
use yii\base\Behavior; use yii\db\ActiveRecord; use yii\db\Expression;
class BitFlagBehavior extends Behavior { /** * [ * 位标志名 => [ * 标志位 => 标志名 * ] * ... * ] * @var array[] $bitFlags 位标志映射表 */ public $bitFlags = [];
public function events() { return [ ActiveRecord::EVENT_BEFORE_INSERT => 'beforeSave', ActiveRecord::EVENT_BEFORE_UPDATE => 'beforeUpdate', ActiveRecord::EVENT_AFTER_FIND => '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 <?php
namespace {yourApp}\models;
use {yourApp}\behaviors\BitFlagBehavior; class MyModel extends base\MyModel { /** * @var int 状态 */ public $s0; public $s1; public $s2; public $s3; public $s5; public $s6; public $s7; public $s8;
/** * 位标志映射表: * [ * 位标志名 => [ * 标志位 => 标志名 * ] * ... * ] */ const BIT_FLAGS = [ 'status' => [ 's0', 's1', 's2', 's3', ], // 'flags' => [ // 's5', 's6', 's7', 's8' // ] ];
// 其他Model代码......
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); } } ```
##### 添加文件 `MyModelQuery`
```php <?php namespace {yourApp}\models;
use yii\db\ActiveQuery; use yii\db\Expression;
class MyModelQuery extends ActiveQuery { /** * [ * 位标志名 => [ * 标志位 => 标志名 * ] * ... * ] * @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(); ```
|