Laravel 实现数据软删除功能


Posted in PHP onAugust 21, 2019

对于任何一个模型,如果需要使用软删除功能,需要在模型中使用 Illuminate\Database\Eloquent\SoftDeletes 这个  trait 。软删除功能需要实现的功能有以下几点:

1.模型执行删除操作,只标记删除,不执行真正的数据删除

2.查询的时候自动过滤已经标记为删除的数据

3.可以设置是否查询已删除的数据,可以设置只查询已删除的数据

4.已删除数据可以恢复

Model的软删除功能实现

Illuminate\Database\Eloquent\Model 中delete方法源码:

public function delete()
{
 if (is_null($this->getKeyName())) {
  throw new Exception('No primary key defined on model.');
 }
 if (! $this->exists) {
  return;
 }
 if ($this->fireModelEvent('deleting') === false) {
  return false;
 }
 $this->touchOwners();
 $this->performDeleteOnModel();
 $this->fireModelEvent('deleted', false);
 return true;
}
protected function performDeleteOnModel()
{
 $this->setKeysForSaveQuery($this->newModelQuery())
 ->delete();
 $this->exists = false;
}

因为在子类中使用了 SoftDeletes trait,所以, SoftDeletes performDeleteOnModel 方法会覆盖父类的方法,最终通过  runSoftDelete 方法更新删除标记。

protected function performDeleteOnModel()
{
 if ($this->forceDeleting) {
  $this->exists = false;
  return $this->newModelQuery()->where(
    $this->getKeyName(), $this->getKey()
  )->forceDelete();
 }
 return $this->runSoftDelete();
}

protected function runSoftDelete()
{
 $query = $this->newModelQuery()
      ->where($this->getKeyName(), $this->getKey());
 $time = $this->freshTimestamp();
 $columns = [$this->getDeletedAtColumn() => $this->fromDateTime($time)];
 $this->{$this->getDeletedAtColumn()} = $time;
 if ($this->timestamps && ! is_null($this->getUpdatedAtColumn())) {
  $this->{$this->getUpdatedAtColumn()} = $time;
  $columns[$this->getUpdatedAtColumn()] = $this->fromDateTime($time);
 }
 $query->update($columns);
}

Model查询过滤删除数据

Laravel中允许在Model中 static::addGlobalScope 方法添加全局的 Scope 。这样就可以在查询条件中添加一个全局条件。Laravel中软删除数据的过滤也是使用这种方式实现的。

SoftDeletes trait中加入了 Illuminate\Database\Eloquent\SoftDeletingScope 全局的 Scope 。并在 SoftDeletingScope 中实现查询自动过滤被删除数据,指定查询已删除数据功能。

public static function bootSoftDeletes()
{
 static::addGlobalScope(new SoftDeletingScope);
}

远程关联数据的软删除处理

Scope的作用只在于当前模型,以及关联模型操作上。如果是远程关联,则还需要额外的处理。Laravel远程关联关系通过 hasManyThrough 实现。里面有两个地方涉及到软删除的查询。

protected function performJoin(Builder $query = null)
{
 $query = $query ?: $this->query;
 $farKey = $this->getQualifiedFarKeyName();
 $query->join($this->throughParent->getTable(), $this->getQualifiedParentKeyName(), '=', $farKey);
 if ($this->throughParentSoftDeletes()) {
  $query->whereNull(
   $this->throughParent->getQualifiedDeletedAtColumn()
  );
 }
}

public function throughParentSoftDeletes()
{
 return in_array(SoftDeletes::class, class_uses_recursive(
  get_class($this->throughParent)
 ));
}
public function getRelationExistenceQueryForSelfRelation(Builder $query, Builder $parentQuery, $columns = ['*'])
{
 $query->from( $query->getModel()->getTable().' as '
  .$hash = $this->getRelationCountHash()
 );
 $query->join($this->throughParent->getTable(), 
  $this->getQualifiedParentKeyName(), '=', $hash.'.'.$this->secondLocalKey
 );
 if ($this->throughParentSoftDeletes()) {
  $query->whereNull($this->throughParent->getQualifiedDeletedAtColumn());
 }
 $query->getModel()->setTable($hash);
 return $query->select($columns)->whereColumn(
  $parentQuery->getQuery()->from.'.'.$query->getModel()->getKeyName(), '=', $this->getQualifiedFirstKeyName()
 );
}

performJoin 中通过中间模型关联远程模型,会根据 throughParentSoftDeletes 判断中间模型是否有软删除,如果有软删除会过滤掉中间模型被删除的数据。

以上就是Laravel实现软删除的大概逻辑。这里有一个细节,Laravel中软删除的标记是一个时间格式的字段,默认 delete_at 。通过是否为null判断数据是否删除。

但是有的时候,项目中会使用一个整形的字段标记数据是否删除。在这样的场景下,需要对Laravel的软删除进行修改才能够实现。

主要的方案是:

1.自定义 SoftDeletes trait,修改字段名称,修改更新删除标记操作;

2.自定义 SoftDeletingScope 修改查询条件

3.自定义 HasRelationships trait,在自定义的 HasRelationships 中重写 newHasManyThrough 方法,实例化自定义的 HasManyThrough 对象

总结

以上所述是小编给大家介绍的Laravel 实现数据软删除功能,希望对大家有所帮助,如果大家有任何疑问欢迎给我留言,小编会及时回复大家的!

PHP 相关文章推荐
在apache下限制每个虚拟主机的并发数!!!!
Oct 09 PHP
php中显示数组与对象的实现代码
Apr 18 PHP
PHP基础教程(php入门基础教程)一些code代码
Jan 06 PHP
php获取新浪微博数据API实例
Nov 12 PHP
phpmailer发送邮件之后,返回收件人是否阅读了邮件的方法
Jul 19 PHP
ThinkPHP 3.2 数据分页代码分享
Oct 14 PHP
php绘制一个扇形的方法
Jan 24 PHP
3种php生成唯一id的方法
Nov 23 PHP
thinkphp框架下实现登录、注册、找回密码功能
Apr 06 PHP
PHP面向对象之事务脚本模式(详解)
Jun 07 PHP
PHP设计模式之工厂模式定义与用法详解
Apr 03 PHP
PHP中SESSION过期设置
Mar 09 PHP
PHP针对redis常用操作实例详解
Aug 17 #PHP
php5.6.x到php7.0.x特性小结
Aug 17 #PHP
PHP中非常有用却鲜有人知的函数集锦
Aug 17 #PHP
PHP中Session ID的实现原理实例分析
Aug 17 #PHP
解决php extension 加载顺序问题
Aug 16 #PHP
深入学习微信网址链接解封的防封原理visit_type
Aug 15 #PHP
Thinkphp5框架实现获取数据库数据到视图的方法
Aug 14 #PHP
You might like
定义php常量的详解
2013/06/09 PHP
PHP格式化MYSQL返回float类型的方法
2016/03/30 PHP
php fread读取文件注意事项
2016/09/24 PHP
PHP SFTP实现上传下载功能
2017/07/26 PHP
php正确输出json数据的实例讲解
2018/08/21 PHP
破解Session cookie的方法
2006/07/28 Javascript
为数据添加append,remove功能
2006/10/03 Javascript
Dom加载让图片加载完再执行的脚本代码
2008/05/15 Javascript
JQuery调用绑定click事件的3种写法
2015/03/28 Javascript
10个很棒的jQuery代码片段
2015/09/24 Javascript
JS获取地址栏参数的两种方法(简单实用)
2016/06/14 Javascript
AngularJS中$watch和$timeout的使用示例
2016/09/20 Javascript
jquery实现图片轮播器
2017/05/23 jQuery
JavaScript实现三级级联特效
2017/11/05 Javascript
原生js实现简单的焦点图效果实例
2017/12/14 Javascript
微信小程序实现animation动画
2018/01/26 Javascript
vue的style绑定background-image的方式和其他变量数据的区别详解
2018/09/03 Javascript
JavaScript实现公告栏上下滚动效果
2020/03/13 Javascript
Vue数组响应式操作及高阶函数使用代码详解
2020/08/01 Javascript
Vue中computed及watch区别实例解析
2020/08/01 Javascript
js+canvas实现图片格式webp/png/jpeg在线转换
2020/08/22 Javascript
vue + el-form 实现的多层循环表单验证
2020/11/25 Vue.js
[01:55]《走出家门看比赛》——DOTA2 2015国际邀请赛同城线下观战
2015/07/18 DOTA
python3中int(整型)的使用教程
2017/03/23 Python
django中模板的html自动转意方法
2018/05/27 Python
Python 创建新文件时避免覆盖已有的同名文件的解决方法
2018/11/16 Python
Python3.5 Pandas模块缺失值处理和层次索引实例详解
2019/04/23 Python
Python2比较当前图片跟图库哪个图片相似的方法示例
2019/09/28 Python
python实现银行管理系统
2019/10/25 Python
解决Keyerror ''acc'' KeyError: ''val_acc''问题
2020/06/18 Python
Python gevent协程切换实现详解
2020/09/14 Python
Python + opencv对拍照得到的图片进行背景去除的实现方法
2020/11/18 Python
HTML5有哪些新特征
2015/12/01 HTML / CSS
HTML5实现页面切换激活的PageVisibility API使用初探
2016/05/13 HTML / CSS
人事专员岗位职责范本
2014/03/04 职场文书
毕业生自荐信如何写
2014/03/24 职场文书