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 16786b65d6 readme 1 year ago
readme.md readme 1 year ago

readme.md

status-combination 融合状态

Configuration

添加文件: StatusBehavior.php
<?php
namespace {yourApp}\behaviors;

use yii\base\Behavior;
use yii\db\ActiveRecord;
use yii\db\Expression;

class StatusBehavior extends Behavior
{
    /**
     * [
     *     融合属性 => [
     *         状态位 => 状态名
     *     ]
     *     ...
     * ]
     * @var array[] $statusAttributes 融合状态表
     */
    public $statusAttributes = [];

    public function events()
    {
        return [
            ActiveRecord::EVENT_BEFORE_INSERT => 'beforeSave',
            ActiveRecord::EVENT_BEFORE_UPDATE => 'beforeUpdate',
            ActiveRecord::EVENT_AFTER_FIND => 'afterFind',
        ];
    }

    public function beforeSave()
    {
        foreach ($this->statusAttributes as $field => $attributes) {
            $this->owner->$field = 0;
            $this->setStatusCombination($field, $attributes);
        }
    }

    public function beforeUpdate()
    {
        foreach ($this->statusAttributes as $field => $attributes) {
            $this->setStatusCombination($field, $attributes);
        }
    }

    public function afterFind()
    {
        $owner = $this->owner;
        foreach ($this->statusAttributes as $field => $attributes) {
            $status = $owner->$field;
            foreach ($attributes as $pos => $attribute) {
                $owner->$attribute = ($status & 1 << $pos) >> $pos;
            }
        }
    }

    /**
     * 设置融合字段
     * @param string $field 融合字段名
     * @param string[] $attributes 字段
     */
    public function setStatusCombination($field, $attributes)
    {
        $owner = $this->owner;
        foreach ($attributes as $pos => $attribute) {
            $status = $owner->$attribute;
            if ($status === null) {
                continue;
            }
            $owner->$field = $this->getUpdateStatusCombination($owner->$field, $pos, $status);
        }
    }

    /**
     * 获得更新后的融合状态
     * @param int $statusCombination 融合状态
     * @param int $pos 状态位置
     * @param bool|int $status 状态
     * @return int
     */
    public function getUpdateStatusCombination($statusCombination, $pos, $status)
    {
        if ($status) {
            $statusCombination = $statusCombination | 1 << $pos;
        } else {
            $statusCombination = $statusCombination & ~(1 << $pos);
        }
        return $statusCombination;
    }
}
在需要的Model里添加Behavior
<?php

namespace {yourApp}\models;

use {yourApp}\behaviors\StatusBehavior;
 
class MyModel extends base\MyModel
{
    /**
     * @var int 状态
     */
    public $s0;
    public $s1;
    public $s2;
    public $s3;
    public $s5;
    public $s6;
    public $s7;
    public $s8;

    /**
     * 融合状态表:
     * [
     *     融合属性 => [
     *         状态位 => 状态名
     *     ]
     *     ...
     * ]
     */
    const STATUS_ATTRIBUTES = [
        'status' => [
            's0',
            's1',
            's2',
            's3',
        ],
        // 'flags' => [
        //     's5', 's6', 's7', 's8'
        // ]
    ];

    // 其他Model代码......

    public function behaviors()
    {
        return [
            // 其他Behavior

            'statusBehavior' => [
                'class' => StatusBehavior::class,
                'statusAttributes' => self::STATUS_ATTRIBUTES,
            ],
        ];
    }

    /**
     * {@inheritdoc}
     * 重写find()返回自定义查询类
     */
    public static function find()
    {
        $config = ['statusAttributes' => self::STATUS_ATTRIBUTES];
        return new MyModelQuery(get_called_class(), $config);
    }
}
添加文件 MyModelQuery
<?php
namespace {yourApp}\models;

use yii\db\ActiveQuery;
use yii\db\Expression;

class MyModelQuery extends ActiveQuery
{
    /**
     * [
     *     融合属性 => [
     *         状态位 => 状态名
     *     ]
     *     ...
     * ]
     * @var array[] $statusAttributes 融合状态表
     */
    public $statusAttributes;

    /**
     * @param string[] $queryParams
     * @return MyModelQuery
     */
    public function andWhereStatus($queryParams = [])
    {
        return $this->andWhere($this->getStatusCondition($queryParams));
    }

    /**
     * @param string[] $queryParams
     * @return MyModelQuery
     */
    public function orWhereStatus($queryParams = [])
    {
        return $this->orWhere($this->getStatusCondition($queryParams));
    }

    /**
     * @param array $queryParams
     * @return Expression
     */
    public function getStatusCondition($queryParams = [])
    {
        $queryAttributes = array_keys($queryParams);
        foreach ($this->statusAttributes as $field => $attributes) {
            $mask = $res = 0;
            foreach ($attributes as $pos => $attribute) {
                if (in_array($attribute, $queryAttributes)) {
                    $mask += 1 << $pos;
                    $res += $queryParams[$attribute] << $pos;
                }
            }
            if ($mask != 0) {
                $conditions[] = "$field & $mask = $res";
            }
        }
        return new Expression(implode('and', $conditions));
    }
}

Usage

$myModels = MyModel::find()
            ->andWhereStatus(['s0' => 1, 's1' => 0])
            ->orWhereStatus(['s2' => 0, 's3' => 1])
            ->all();