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 相关文章推荐
PHP脚本数据库功能详解(中)
Oct 09 PHP
php in_array 函数使用说明与in_array需要注意的地方说明
Apr 13 PHP
深入apache host的配置详解
Jun 09 PHP
PHP 动态生成静态HTML页面示例代码
Jan 15 PHP
使用PHP生成二维码的两种方法(带logo图像)
Mar 14 PHP
php防止伪造数据从地址栏URL提交的方法
Aug 24 PHP
ThinkPHP打开验证码页面显示乱码的解决方法
Dec 18 PHP
Laravel框架中实现使用阿里云ACE缓存服务
Feb 10 PHP
浅析PHP关键词替换的类(避免重复替换,保留与还原原始链接)
Sep 22 PHP
PHP实现上一篇下一篇的方法实例总结
Sep 22 PHP
php 判断页面或图片是否经过gzip压缩的方法
Apr 05 PHP
浅谈PHP5.6 与 PHP7.0 区别
Oct 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
桌面中心(二)数据库写入
2006/10/09 PHP
用php将任何格式视频转为flv的代码
2009/09/03 PHP
用PHP的超级变量$_POST获取HTML表单(HTML Form) 数据
2011/05/07 PHP
什么是PEAR?什么是PECL?PHP中两个容易混淆的概念解释
2015/07/01 PHP
培养自己的php编码规范
2015/09/28 PHP
php实现smarty模板无限极分类的方法
2015/12/07 PHP
PHP缩略图生成和图片水印制作
2017/01/07 PHP
优秀js开源框架-jQuery使用手册(1)
2007/03/10 Javascript
判断浏览器的javascript版本的代码
2010/09/03 Javascript
jQuery的$.proxy()应用示例介绍
2014/04/03 Javascript
JavaScript fontsize方法入门实例(按照指定的尺寸来显示字符串)
2014/10/17 Javascript
Jquery和BigFileUpload实现大文件上传及进度条显示
2016/06/27 Javascript
如何在JS中实现相互转换XML和JSON
2016/07/19 Javascript
JavaScript闭包和范围实例详解
2016/12/19 Javascript
轻松理解Javascript变量的相关问题
2017/01/20 Javascript
详解如何用babel转换es6的class语法
2018/04/03 Javascript
React降级配置及Ant Design配置详解
2018/12/27 Javascript
简单通过settimeout看javascript的运行机制
2019/05/10 Javascript
vue 授权获取微信openId操作
2020/11/13 Javascript
[06:50]DSPL次级职业联赛十强晋级之路
2014/11/18 DOTA
[42:32]VP vs RNG 2019国际邀请赛淘汰赛 败者组 BO3 第一场 8.21.mp4
2020/07/19 DOTA
Python的动态重新封装的教程
2015/04/11 Python
Python+Socket实现基于TCP协议的客户与服务端中文自动回复聊天功能示例
2017/08/31 Python
PyTorch线性回归和逻辑回归实战示例
2018/05/22 Python
python中的常量和变量代码详解
2018/07/25 Python
Tensorflow分批量读取数据教程
2020/02/07 Python
40行Python代码实现天气预报和每日鸡汤推送功能
2020/02/27 Python
python自定义函数def的应用详解
2020/06/03 Python
css3 position fixed固定居中问题解决方案
2014/08/19 HTML / CSS
国际知名军事风格休闲装品牌:Alpha Industries(阿尔法工业)
2017/05/24 全球购物
美国手机支架公司:PopSockets
2019/11/27 全球购物
大学生怎样进行自我评价
2013/12/07 职场文书
励志演讲稿200字
2014/08/21 职场文书
申报优秀教师材料
2014/12/16 职场文书
大学生自荐信怎么写
2015/03/26 职场文书
Golang二维切片初始化的实现
2021/04/08 Golang