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 相关文章推荐
fleaphp常用方法分页之Pager使用方法
Apr 23 PHP
Array of country list in PHP with Zend Framework
Oct 17 PHP
PHP模板引擎Smarty的缓存使用总结
Apr 24 PHP
Yii入门教程之目录结构、入口文件及路由设置
Nov 25 PHP
Laravel 5框架学习之数据库迁移(Migrations)
Apr 08 PHP
php发送html格式文本邮件的方法
Jun 10 PHP
php实现多城市切换特效
Aug 09 PHP
PHP5.5.15+Apache2.4.10+MySQL5.6.20配置方法分享
May 06 PHP
linux平台编译安装PHP7并安装Redis扩展与Swoole扩展实例教程
Sep 30 PHP
自制PHP框架之路由与控制器
May 07 PHP
PHP基于GD库实现的生成图片缩略图函数示例
Jul 05 PHP
PHP使用文件锁解决高并发问题示例
Mar 29 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
星际争霸秘籍
2020/03/04 星际争霸
web方式ftp
2006/10/09 PHP
PHP OPP机制和模式简介(抽象类、接口和契约式编程)
2014/06/09 PHP
PHP 面向对象程序设计(oop)学习笔记(三) - 单例模式和工厂模式
2014/06/12 PHP
jQuery UI 应用不同Theme的办法
2010/09/12 Javascript
jQuery powerFloat万能浮动层下拉层插件使用介绍
2010/12/27 Javascript
JS获取页面窗口大小的代码解读
2011/12/01 Javascript
EasyUI的treegrid组件动态加载数据问题的解决办法
2011/12/11 Javascript
Jquery中给animation加更多的运作效果实例
2013/09/05 Javascript
设为首页和收藏的Javascript代码(亲测兼容IE,Firefox,chrome等浏览器)
2013/11/18 Javascript
javascript中createElement的两种创建方式
2015/05/14 Javascript
JS中处理时间之setUTCMinutes()方法的使用
2015/06/12 Javascript
JQuery中DOM事件冒泡实例分析
2015/06/13 Javascript
AngularJS的一些基本样式初窥
2015/07/27 Javascript
基于jquery实现鼠标左右拖动滑块滑动附源码下载
2015/12/23 Javascript
如何用JavaScript实现动态修改CSS样式表
2016/05/20 Javascript
详解基于Angular4+ server render(服务端渲染)开发教程
2017/08/28 Javascript
一次微信小程序内地图的使用实战记录
2019/09/09 Javascript
javascript实现蒙版与禁止页面滚动
2020/01/11 Javascript
详解使用mocha对webpack打包的项目进行"冒烟测试"的大致流程
2020/04/27 Javascript
Vue——解决报错 Computed property "****" was assigned to but it has no setter.
2020/12/19 Vue.js
Python基于hashlib模块的文件MD5一致性加密验证示例
2018/02/10 Python
将Dataframe数据转化为ndarry数据的方法
2018/06/28 Python
Python语言检测模块langid和langdetect的使用实例
2019/02/19 Python
Python中利用LSTM模型进行时间序列预测分析的实现
2019/07/26 Python
运行tensorflow python程序,限制对GPU和CPU的占用操作
2020/02/06 Python
Python使用内置函数setattr设置对象的属性值
2020/10/16 Python
Python中对象的比较操作==和is区别详析
2021/02/12 Python
AmazeUI 等分网格的实现示例
2020/08/25 HTML / CSS
英国领先的游戏零售商:GAME
2019/09/24 全球购物
UNOde50美国官网:西班牙珠宝品牌
2020/08/15 全球购物
linux面试题参考答案(8)
2015/08/11 面试题
司机辞职报告范文
2014/01/20 职场文书
新学期教师寄语
2014/04/02 职场文书
篮球比赛拉拉队口号
2014/06/10 职场文书
放飞理想主题班会
2015/08/14 职场文书