YII 优雅的实现软删

大部分项目都有软删的需求,我们现项目也自己封装了一个软删除的类,但是存在不够健壮,查询数据不友好等缺陷。
原来软删除Trait 类代码;



namespace app\common\trait_class;


/**
 * 软删除工具类
 * Trait SoftDeleteTrait
 * @package app\common\trait_class
 */
trait SoftDeleteTrait
{
    /**
     * @var int 删除默认值
     */
    static $deleteDefault = 0;

    /**
     * 软删除
     * @return mixed
     * @throws \Exception
     */
    public function softDelete()
    {
        $this->{self::getDeletedAtAttribute()} = time();
        $ret = $this->save(false, [self::getDeletedAtAttribute()]);
        $this->afterSoftDelete();
        return $ret;
    }

    public function afterSoftDelete()
    {
        // Default implementation
    }

    /**
     * 软删除
     * @return mixed
     * @throws \Exception
     */
    public function delete()
    {
        return $this->softDelete();
    }

    /**
     * Gets the deleted_at attribute name
     *
     * @throws \Exception
     */
    static public function getDeletedAtAttribute()
    {
        return 'deleted_at';
    }

    /**
     * @return mixed
     * @throws \Exception
     */
    public function restore()
    {
        $this->{self::getDeletedAtAttribute()} = self::$deleteDefault;
        return $this->save(false, [self::getDeletedAtAttribute()]);
    }


    /**
     * 批量删除
     * @param null $condition
     * @param array $params
     * @return mixed
     * @throws \Exception
     */
    public static function deleteAll($condition = null, $params = [])
    {
        $deleteField = self::getDeletedAtAttribute();
        $deleteFieldValue["{$deleteField}"] = time();

        $command = static::getDb()->createCommand();
        $command->update(static::tableName(), $deleteFieldValue, $condition,$params);
        return $command->execute();
    }

    /**
     * 过滤软删除的数据
     * @return \yii\db\ActiveQuery
     * @throws \Exception
     */
    public static function find()
    {
        $deleteField = self::getDeletedAtAttribute();
        $where = [
            static::tableName() . '.' . "{$deleteField}" => self::$deleteDefault
        ];
        return parent::find()->andWhere($where);
    }

    /**
     * 查询包含软删除的数据
     * @return \yii\db\ActiveQuery
     * @throws \Exception
     */
    public static function withTrashedFind()
    {
        return parent::find();
    }

    /**
     * 只查询软删除数据
     * @return \yii\db\ActiveQuery
     * @throws \Exception
     */
    public static function onlyTrashedFind()
    {
        $deleteField = self::getDeletedAtAttribute();
        $where = ['not', [$deleteField => self::$deleteDefault]];
        return parent::find()->andWhere($where);
    }

    /**
     * 判断当前实例是否被软删除
     * @return false
     * @throws \Exception
     */
    public function trashed()
    {
        $field = $this->getDeletedAtAttribute();
        $softDelete = $this->$field ?? false;
        if ($field && ($softDelete === self::$deleteDefault)) {
            return false;
        }
        return true;
    }

}

以上代码为自己封装,好处是用Trait,可以随时声明软删,实现方便。但是存在一些问题如不够健壮,如只默认deleted_at 为删除字段,默认值为0等,并且对于数据查询不够友好等缺陷
以下是结合第三方包及Trait的实现,不仅具备方便而且安全健壮

引入第三方库

composer require yii2tech/ar-softdelete

修改Trait类



namespace app\common\trait_class;

use yii2tech\ar\softdelete\SoftDeleteBehavior;
use yii2tech\ar\softdelete\SoftDeleteQueryBehavior;

/**
 * 软删除工具类
 * Trait SoftDeleteTrait
 * @package app\common\trait_class
 */
trait SoftDeleteTrait
{
    public function behaviors()
    {
        return [
            'softDeleteBehavior' => [
                'class' => SoftDeleteBehavior::class,
                'softDeleteAttributeValues' => [
                    'deleted_at' => function ($model) {
                        return time();
                    }
                ],
                'restoreAttributeValues' => [
                    'deleted_at' => 0
                ]
            ],
        ];
    }

    /**
     * @return \yii\db\ActiveQuery|SoftDeleteQueryBehavior
     */
    public static function find()
    {
        $query = parent::find();
        $query->attachBehavior('softDelete', SoftDeleteQueryBehavior::class);
        return $query;
    }

	/**
	 * 声明软删则不能批量删除
     * @param null $condition
     * @param array $params
     * @throws Exception
     */
    public static function deleteAll($condition = null, $params = [])
    {
        throw new Exception("软删不支持批量删除");
    }
}

因为这个第三类库的deleted()函数的删除时间默认为softDeleteAttributeValues的值,所以需要重写deleted()方法;如在Users中
重写Users 的find()方法使其能调用User 的Query Scope 类

class users extends \yii\db\ActiveRecord
{
    /**
     * @return GoodsGroup|object|\yii\db\ActiveQuery
     * @throws \yii\base\InvalidConfigException
     */
    public static function find()
    {
        return new UserQuery(get_called_class());
    }
}

User Query Scope

class UserQuery extends ActiveQuery
{
	
    /**
     * 重写覆盖Soft delete 的deleted()
     * @return GoodsGroup
     */
    public function deleted()
    {
        return $this->andFilterWhere(['>', 'deleted_at', 0]);
    }
    ...
}

在仓库层use SoftDeleteTrait;实现软删

总结

  1. 应用的第三类库是Behavior 行为实现的经典应用。
  2. 熟知当前类,Trait,父类库的方法调用覆盖顺序,并类库的deleted()函数重写
  3. 需要注意的是User需要新增Behavior附加行为的时候注意Behavior()会被重写

你可能感兴趣的:(php,php,开发语言)