You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
LIERLIER
5776a0be42
|
1 year ago | |
---|---|---|
readme.md | 1 year ago |
readme.md
BitFlag 位标志
Configuration
添加文件: BitFlagBehavior.php
<?php
namespace {yourApp}\behaviors;
use yii\base\Behavior;
use yii\db\ActiveRecord;
use yii\helpers\ArrayHelper;
class BitFlagBehavior extends Behavior
{
/**
* [
* 位标志名 => [
* 标志位 => 标志名
* ]
* ...
* ]
* @var array[] $bitFlags 位标志映射表
*/
public $bitFlags = [];
/**
* @var array
*/
private $_attributes = [];
/**
* {@inheritdoc}
*/
public function init()
{
parent::init();
$this->initAttributes();
}
/**
* 初始化属性数组
*/
private function initAttributes()
{
$keys = array_merge(...array_values($this->bitFlags));
$this->_attributes = array_fill_keys($keys, null);
}
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()
{
valuewner = $this->owner;
foreach ($this->bitFlags as $bitFlag => $flags) {
$bitFlagValue = valuewner->$bitFlag;
foreach ($flags as $pos => $flag) {
valuewner->$flag = ($bitFlagValue & 1 << $pos) >> $pos;
}
}
}
/**
* 设置位标志
* @param string $bitFlag 位标志
* @param string[] $flags 标志
*/
public function setBitFlag($bitFlag, $flags)
{
valuewner = $this->owner;
foreach ($flags as $pos => $flag) {
$flagValue = valuewner->$flag;
if ($flagValue === null) {
continue;
}
valuewner->$bitFlag = $this->getBitFlagUpdate(valuewner->$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;
}
/**
* {@inheritdoc}
*/
public function __get(name)
{
return $this->_attributes[name];
}
/**
* {@inheritdoc}
*/
public function __set(name, $value)
{
$this->_attributes[name] = $value;
}
/**
* {@inheritdoc}
*/
public function canGetProperty(name, $checkVars = true)
{
return parent::canGetProperty(name, $checkVars) || ArrayHelper::keyExists(name, $this->_attributes);
}
/**
* {@inheritdoc}
*/
public function canSetProperty(name, $checkVars = true)
{
return parent::canSetProperty(name, $checkVars) || ArrayHelper::keyExists(name, $this->_attributes);
}
}
在需要的Model
里添加Behavior
<?php
namespace {yourApp}\models;
use {yourApp}\behaviors\BitFlagBehavior;
class MyModel extends base\MyModel
{
/**
* 位标志映射表:
* [
* 位标志名 => [
* 标志位 => 标志名
* ]
* ...
* ]
*/
const BIT_FLAGS = [
'status' => [
's0',
's1',
's2',
's3',
],
// 'flags' => [
// 's5', 's6', 's7', 's8'
// ]
];
public function fields()
{
$fields = parent::fields();
$flagFields = [];
foreach (self::BIT_FLAGS as $bitFlag => $flags) {
$flagFields = ArrayHelper::merge($flagFields, array_combine($flags, $flags));
unset($fields[$bitFlag]);
}
return ArrayHelper::merge($fields, $flagFields);
}
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
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
$myModels = MyModel::find()
->andWhereBitFlags(['s0' => 1, 's1' => 0])
->orWhereBitFlags(['s2' => 0, 's3' => 1])
->all();
Migration
标志位插入到第 n
位,默认值为 k
插入公式: (value >> n << (n + 1)) + (value & ((1 << n) - 1)) + (k << n)
删除公式: (value >> (n + 1) << n) + (value & ((1 << n) - 1))
migration:
<?php
use yii\db\Migration;
/**
* Class mxxxxxx_xxxxxx_update_{table}_table_{column}_column
*/
class mxxxxxx_xxxxxx_update_{table}_table_{column}_column extends Migration
{
/**
* {@inheritdoc}
*/
public function safeUp()
{
$this->update('{table}', ['{column}' => $this->getBitFlagExpression('{column}', {pos}, 'insert', {k})]);
}
/**
* {@inheritdoc}
*/
public function safeDown()
{
$this->update('{table}', ['{column}' => $this->getBitFlagExpression('{column}', {pos}, 'delete')]);
}
/**
* @param string $column
* @param int $pos
* @param string $operate
* @param int $defaultValue
* @return \yii\db\Expression
*/
public function getBitFlagExpression($column, $pos, $operate, $defaultValue = 0)
{
if ($operate == 'insert') {
$expresstion = "($column >> $pos << ($pos + 1)) + ($column & ((1 << $pos) - 1)) + ($defaultValue << $pos)";
} elseif ($operate == 'delete') {
$expresstion = "($column >> ($pos + 1) << $pos) + ($column & ((1 << $pos) - 1))";
} else {
throw new \InvalidArgumentException('$operate: '.$operate);
}
return new \yii\db\Expression($expresstion);
}
}