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 论坛采集程序 模拟登陆,抓取页面 实现代码
Jul 09 PHP
PHP随机数生成代码与使用实例分析
Apr 08 PHP
PHP反射类ReflectionClass和ReflectionObject的使用方法
Nov 13 PHP
php通过递归方式复制目录和子目录的方法
Mar 13 PHP
WordPress中邮件的一些修改和自定义技巧
Dec 15 PHP
Linux下编译redis和phpredis的方法
Apr 07 PHP
thinkphp框架实现删除和批量删除
Jun 29 PHP
PHP实现的统计数据功能详解
Dec 06 PHP
ThinkPHP 3使用OSS的方法
Jul 19 PHP
PHP实现函数内修改外部变量值的方法示例
Dec 28 PHP
laravel5.6中的外键约束示例
Oct 23 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
微信公众号开发客服接口实例代码
2016/10/21 PHP
PHP实现的简单操作SQLite数据库类与用法示例
2017/06/19 PHP
Laravle eloquent 多对多模型关联实例详解
2017/11/22 PHP
laravel Model 执行事务的实现
2019/10/10 PHP
PHP实现简单用户登录界面
2019/10/23 PHP
var与Javascript变量隐式声明
2009/09/17 Javascript
jQuery学习笔记之jQuery的DOM操作
2010/12/22 Javascript
JavaScript异步调用定时方法并停止该方法实现代码
2012/03/16 Javascript
IE浏览器中图片onload事件无效的解决方法
2014/04/29 Javascript
javascript数组排序汇总
2015/07/07 Javascript
JS拖动鼠标画出方框实现鼠标选区的方法
2015/08/05 Javascript
jQuery弹簧插件编写基础之“又见弹窗”
2015/12/11 Javascript
jquery实现图片预加载
2015/12/25 Javascript
学习使用grunt来打包JavaScript和CSS程序的教程
2016/01/04 Javascript
超链接怎么正确调用javascript函数
2016/05/23 Javascript
JavaScript变量作用域_动力节点Java学院整理
2017/06/27 Javascript
利用ECharts.js画K线图的方法示例
2018/01/10 Javascript
vue中$refs的用法及作用详解
2018/04/24 Javascript
VUE DOM加载后执行自定义事件的方法
2018/09/07 Javascript
实用的Vue开发技巧
2019/05/30 Javascript
举例讲解Python中的迭代器、生成器与列表解析用法
2016/03/20 Python
在python中pandas读文件,有中文字符的方法
2018/12/12 Python
Python开发网站目录扫描器的实现
2019/02/21 Python
Python序列类型的打包和解包实例
2019/12/21 Python
python匿名函数lambda原理及实例解析
2020/02/07 Python
python连接mongodb集群方法详解
2020/02/13 Python
使用tensorflow进行音乐类型的分类
2020/08/14 Python
纯CSS3制作的简洁蓝白风格的登录模板(非IE效果更好)
2013/08/11 HTML / CSS
土木工程求职信
2014/05/29 职场文书
交通事故和解协议书
2014/09/25 职场文书
毕业证明书
2015/06/19 职场文书
2016年精神文明建设先进个人事迹材料
2016/02/29 职场文书
世界文化遗产导游词
2019/08/07 职场文书
Python matplotlib可视化之绘制韦恩图
2022/02/24 Python
Python万能模板案例之matplotlib绘制甘特图
2022/04/13 Python
Windows server 2012 NTP时间同步的实现
2022/06/25 Servers