大部分项目都有软删的需求,我们现项目也自己封装了一个软删除的类,但是存在不够健壮,查询数据不友好等缺陷。
原来软删除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;实现软删