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.

356 lines
7.9 KiB

1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
  1. # BitFlag 位标志
  2. ### Configuration
  3. #### 添加文件: `BitFlagBehavior.php`
  4. ```php
  5. <?php
  6. namespace {yourApp}\behaviors;
  7. use yii\base\Behavior;
  8. use yii\db\ActiveRecord;
  9. use yii\helpers\ArrayHelper;
  10. class BitFlagBehavior extends Behavior
  11. {
  12. /**
  13. * [
  14. * 位标志名 => [
  15. * 标志位 => 标志名
  16. * ]
  17. * ...
  18. * ]
  19. * @var array[] $bitFlags 位标志映射表
  20. */
  21. public $bitFlags = [];
  22. /**
  23. * @var array
  24. */
  25. private $_attributes = [];
  26. /**
  27. * {@inheritdoc}
  28. */
  29. public function init()
  30. {
  31. parent::init();
  32. $this->initAttributes();
  33. }
  34. /**
  35. * 初始化属性数组
  36. */
  37. private function initAttributes()
  38. {
  39. $keys = array_merge(...array_values($this->bitFlags));
  40. $this->_attributes = array_fill_keys($keys, null);
  41. }
  42. public function events()
  43. {
  44. return [
  45. ActiveRecord::EVENT_BEFORE_INSERT => 'beforeSave',
  46. ActiveRecord::EVENT_BEFORE_UPDATE => 'beforeUpdate',
  47. ActiveRecord::EVENT_AFTER_FIND => 'afterFind',
  48. ActiveRecord::EVENT_AFTER_REFRESH => 'afterFind',
  49. ];
  50. }
  51. public function beforeSave()
  52. {
  53. foreach ($this->bitFlags as $bitFlag => $flags) {
  54. $this->owner->$bitFlag = 0;
  55. $this->setBitFlag($bitFlag, $flags);
  56. }
  57. }
  58. public function beforeUpdate()
  59. {
  60. foreach ($this->bitFlags as $bitFlag => $flags) {
  61. $this->setBitFlag($bitFlag, $flags);
  62. }
  63. }
  64. public function afterFind()
  65. {
  66. valuewner = $this->owner;
  67. foreach ($this->bitFlags as $bitFlag => $flags) {
  68. $bitFlagValue = valuewner->$bitFlag;
  69. foreach ($flags as $pos => $flag) {
  70. valuewner->$flag = ($bitFlagValue & 1 << $pos) >> $pos;
  71. }
  72. }
  73. }
  74. /**
  75. * 设置位标志
  76. * @param string $bitFlag 位标志
  77. * @param string[] $flags 标志
  78. */
  79. public function setBitFlag($bitFlag, $flags)
  80. {
  81. valuewner = $this->owner;
  82. foreach ($flags as $pos => $flag) {
  83. $flagValue = valuewner->$flag;
  84. if ($flagValue === null) {
  85. continue;
  86. }
  87. valuewner->$bitFlag = $this->getBitFlagUpdate(valuewner->$bitFlag, $pos, $flagValue);
  88. }
  89. }
  90. /**
  91. * 获得更新后的位标志值
  92. * @param int $bitFlagValue 位标志值
  93. * @param int $pos 状态位置
  94. * @param bool|int $flagValue 标志值
  95. * @return int
  96. */
  97. public function getBitFlagUpdate($bitFlagValue, $pos, $flagValue)
  98. {
  99. if ($flagValue) {
  100. $bitFlagValue = $bitFlagValue | 1 << $pos;
  101. } else {
  102. $bitFlagValue = $bitFlagValue & ~(1 << $pos);
  103. }
  104. return $bitFlagValue;
  105. }
  106. /**
  107. * {@inheritdoc}
  108. */
  109. public function __get(name)
  110. {
  111. return $this->_attributes[name];
  112. }
  113. /**
  114. * {@inheritdoc}
  115. */
  116. public function __set(name, $value)
  117. {
  118. $this->_attributes[name] = $value;
  119. }
  120. /**
  121. * {@inheritdoc}
  122. */
  123. public function canGetProperty(name, $checkVars = true)
  124. {
  125. return parent::canGetProperty(name, $checkVars) || ArrayHelper::keyExists(name, $this->_attributes);
  126. }
  127. /**
  128. * {@inheritdoc}
  129. */
  130. public function canSetProperty(name, $checkVars = true)
  131. {
  132. return parent::canSetProperty(name, $checkVars) || ArrayHelper::keyExists(name, $this->_attributes);
  133. }
  134. }
  135. ```
  136. #### 在需要的`Model`里添加`Behavior`
  137. ```php
  138. <?php
  139. namespace {yourApp}\models;
  140. use {yourApp}\behaviors\BitFlagBehavior;
  141. class MyModel extends base\MyModel
  142. {
  143. /**
  144. * 位标志映射表:
  145. * [
  146. * 位标志名 => [
  147. * 标志位 => 标志名
  148. * ]
  149. * ...
  150. * ]
  151. */
  152. const BIT_FLAGS = [
  153. 'status' => [
  154. 's0',
  155. 's1',
  156. 's2',
  157. 's3',
  158. ],
  159. // 'flags' => [
  160. // 's5', 's6', 's7', 's8'
  161. // ]
  162. ];
  163. public function fields()
  164. {
  165. $fields = parent::fields();
  166. $flagFields = [];
  167. foreach (self::BIT_FLAGS as $bitFlag => $flags) {
  168. $flagFields = ArrayHelper::merge($flagFields, array_combine($flags, $flags));
  169. unset($fields[$bitFlag]);
  170. }
  171. return ArrayHelper::merge($fields, $flagFields);
  172. }
  173. public function behaviors()
  174. {
  175. return [
  176. // 其他Behavior
  177. 'bitFlagBehavior' => [
  178. 'class' => BitFlagBehavior::class,
  179. 'bitFlags' => self::BIT_FLAGS,
  180. ],
  181. ];
  182. }
  183. /**
  184. * {@inheritdoc}
  185. * 重写find()返回自定义查询类
  186. */
  187. public static function find()
  188. {
  189. $config = ['bitFlags' => self::BIT_FLAGS];
  190. return new MyModelQuery(get_called_class(), $config);
  191. }
  192. // 其他Model代码......
  193. }
  194. ```
  195. #### 添加文件 `MyModelQuery`
  196. ```php
  197. <?php
  198. namespace {yourApp}\models;
  199. use yii\db\ActiveQuery;
  200. use yii\db\Expression;
  201. class MyModelQuery extends ActiveQuery
  202. {
  203. /**
  204. * [
  205. * 位标志名 => [
  206. * 标志位 => 标志名
  207. * ]
  208. * ...
  209. * ]
  210. * @var array[] $bitFlags 融合状态表
  211. */
  212. public $bitFlags;
  213. /**
  214. * @param string[] $conditions
  215. * @return MyModelQuery
  216. */
  217. public function andWhereBitFlags($conditions = [])
  218. {
  219. return $this->andWhere($this->getBitFlagsCondition($conditions));
  220. }
  221. /**
  222. * @param string[] $conditions
  223. * @return MyModelQuery
  224. */
  225. public function orWhereBitFlags($conditions = [])
  226. {
  227. return $this->orWhere($this->getBitFlagsCondition($conditions));
  228. }
  229. /**
  230. * @param array $conditions
  231. * @return Expression
  232. */
  233. public function getBitFlagsCondition($conditions = [])
  234. {
  235. $conditionsKeys = array_keys($conditions);
  236. foreach ($this->bitFlags as $bitFlag => $flags) {
  237. $mask = $res = 0;
  238. foreach ($flags as $pos => $flag) {
  239. if (in_array($flag, $conditionsKeys)) {
  240. $mask += 1 << $pos;
  241. $res += $conditions[$flag] << $pos;
  242. }
  243. }
  244. if ($mask != 0) {
  245. $bitFlagsConditions[] = "$bitFlag & $mask = $res";
  246. }
  247. }
  248. return new Expression(implode(' and ', $bitFlagsConditions));
  249. }
  250. }
  251. ```
  252. ### Usage
  253. #### Search
  254. ```php
  255. $myModels = MyModel::find()
  256. ->andWhereBitFlags(['s0' => 1, 's1' => 0])
  257. ->orWhereBitFlags(['s2' => 0, 's3' => 1])
  258. ->all();
  259. ```
  260. #### Migration
  261. ##### 标志位插入到第 `n` 位,默认值为 `k`
  262. 插入公式: `(value >> n << (n + 1)) + (value & ((1 << n) - 1)) + (k << n)`
  263. 删除公式: `(value >> (n + 1) << n) + (value & ((1 << n) - 1))`
  264. migration:
  265. ```php
  266. <?php
  267. use yii\db\Migration;
  268. /**
  269. * Class mxxxxxx_xxxxxx_update_{table}_table_{column}_column
  270. */
  271. class mxxxxxx_xxxxxx_update_{table}_table_{column}_column extends Migration
  272. {
  273. /**
  274. * {@inheritdoc}
  275. */
  276. public function safeUp()
  277. {
  278. $this->addBitFlag('{table}', '{column}', {pos}, {defaultValue});
  279. }
  280. /**
  281. * {@inheritdoc}
  282. */
  283. public function safeDown()
  284. {
  285. $this->deleteBitFlag('{table}', '{column}', {pos});
  286. }
  287. /**
  288. * 添加标志位
  289. * @param string $table
  290. * @param string $column
  291. * @param int $pos
  292. * @param int $defaultValue
  293. */
  294. public function addBitFlag($table, $column, $pos, $defaultValue = 0)
  295. {
  296. $this->update($table, [$column => new \yii\db\Expression("($column >> $pos << ($pos + 1)) + ($column & ((1 << $pos) - 1)) + ($defaultValue << $pos)")]);
  297. }
  298. /**
  299. * 删除标志位
  300. * @param string $table
  301. * @param string $column
  302. * @param int $pos
  303. */
  304. public function deleteBitFlag($table, $column, $pos)
  305. {
  306. $this->update($table, [$column => new \yii\db\Expression("($column >> ($pos + 1) << $pos) + ($column & ((1 << $pos) - 1))")]);
  307. }
  308. }
  309. ```