3分钟短文:Laravel 从软删除说到模型作用域的概念
引言
上一节我们讲了通过模型方法新建条目,或者更新数据。对于写操作还有更为重要的一个方法, 就是数据的删除。删除数据,有物理删除和软删除的区别。
我们从软删除的使用,再顺便说一说模型内的作用域的概念。
代码时间
常规的删除操作分两步进行,一步是把数据从数据库中查询出来,使用laravel模型的方法, 则返回的是一个模型对象。第二步,调用模型对象的delete方法。代码如下:
$contact = Contact::find(5);$contact->delete();
如果像上面的代码那样,已知数据条目的ID,那么可以直接使用destroy方法进行删除:
Contact::destroy(1);
该方法可以可以用于批量删除传入的指定ID数组的条目:
Contact::destroy([1, 5, 7]);
当然了,delete方法只是链式调用的一个方法,我们通过查询构造器过滤后的数据集, 都可以调用该方法将其删除:
Contact::where('updated_at', '>', Carbon::now()->subYear())->delete();
上面代码实现的是,超过一年没有更新过的contact,将其删除。这可能会有很多,也没有问题。
上面的delete方法,destroy方法,都是对数据的物理删除。数据库的表内记录直接移除了,这在重要的表, 比如user,order,payment这些关系用户权限,资金支付等等的重要数据资源上,物理删除是不被允许的。
所以引入了软删除的概念,就是在表内添加一个字段,用于标记,这一行条目是否算是删除状态。在laravel中, 这个软删除字段默认是 deleted_at。你也可以在模型中手动指定。
如果你使用系统的migrate方法创建迁移文件,那么只用在构造方法中添加如下代码:
Schema::table('contacts', function (Blueprint $table) {
$table->softDeletes();});
那么迁移成功后,生成的contacts表内会添加deleted_at字段。
然后在模型中,引入软删除的功能,将其进行全局生效的使用。模型中相关代码如下:
use IlluminateDatabaseEloquentModel;use IlluminateDatabaseEloquentSoftDeletes;class Contact extends Model{
use SoftDeletes; // 这是一个 trait
protected $dates = ['deleted_at']; // 指定deleted_at的datetime格式}
通过追寻源代码,我们注意到 SoftDeletes 其实是注册了一个模型内的全局作用域方法:
public static function bootSoftDeletes(){
static::addGlobalScope(new SoftDeletingScope);}
这样在应用程序内,使用该模型的所有方法,都会被追加全局可见的查询条件。我们看源码这样一段,就是用于标记进行删除的:
protected function runSoftDelete(){
$time = $this->freshTimestamp();
// ...
$this->{$this->getDeletedAtColumn()} = $time;
// ...
$query->update($columns);}
为了说明问题,我们把中间几行代码都省略了。大家注意, 想我们的常规操作一样,就是获取一个时间戳$time, 然后把字段赋值:
$this->deleted_at = $time;
最后使用update方法更新模型,并修改数据库条目。是不是把知识点都连贯起来了?
既然说到了模型作用域,我们不妨延伸一下,说说这个设计点,以及适用的场景。
比如说有一个查询条件在代码内到处都要用,有没有简写方法,写一次其他地方可以随意调用呢?这就是本地作用域的方法了。来看一个实例:
$activeVips = Contact::where('vip', true)->where('trial', false)->get();
比如说这两个where约束很常用,我们要是能简写为类似下面这样会更直观:
$activeVips = Contact::activeVips()->get();
实现此方法,很简单,只需在模型文件内添加如下方法:
class Contact extends Model{
public function scopeActiveVips($query)
{
return $query->where('vip', true)->where('trial', false);
}}
给本地的作用域方法添加传入的参数:
class Contact extends Model{
public function scopeStatus($query, $status)
{
return $query->where('status', $status);
}}
用的时候直接把数据作为入参传送即可:
$friends = Contact::status('friend')->get();
这些方法都是要手动显式调用才能使用的。如果是想软删除条目那样,默认把所有的查询都追加 自定义的查询条件,就需要我们上面说的全局作用域了。
声明一个全局作用域很简单,只需在模型文件内添加如下代码:
class Contact extends Model{
protected static function boot()
{
parent::boot();
static::addGlobalScope('active', function (Builder $builder) {
$builder->where('active', true);
});
}}
那么所有的模型查询,都会默认加上声明中的where约束。一下子节省了很多冗余的代码。
如果你的全局作用域写的逻辑会有点多喝复杂,可以将其独立出来,写成类,以便调用。创建 app/Scopes/ActiveScope.php 文件:
namespace AppScopes;use IlluminateDatabaseEloquentScope;use IlluminateDatabaseEloquentModel;use IlluminateDatabaseEloquentBuilder;class ActiveScope implements Scope{
public function apply(Builder $builder, Model $model)
{
return $builder->where('active', true);
}}
在模型 Contact 内调用:
use AppScopesActiveScope;use IlluminateDatabaseEloquentModel;class Contact extends Model{
protected static function boot()
{
parent::boot();
static::addGlobalScope(new ActiveScope);
}}
如果你的代码中这样的全局作用域用处很多,许多表结构,或者模型的设计逻辑上, 都兼容了此用法,那么独立成一个Scope类更为实用。
写在最后
本文从laravel模型的写操作删除动作,讲到了软删除的概念。进而引申出来本地作用域和全局作用域的使用。软删除几乎贯穿了我们应用的始终,需要大家勤学苦练。作用域的概念,设计起来很灵活, 但是维护需要一些设计规范,更适合团队作战的中型大型应用。
Happy coding :-)
- activiti-explorer部署笔记
- 没有准考证号我是如何暴力查询英语六级成绩的
- asp.net web api 2.2 基础框架(带例子)
- 走近科学:如何一步一步解码复杂的恶意软件
- OpenAI 开源机器人模拟 Python 库:优化API接口提升400%处理速度
- 远控木马上演白利用偷天神技:揭秘假破解工具背后的盗刷暗流
- 设计模式学习(三): 装饰者模式 (附C#实现)
- 浅谈非PE的攻击技巧
- hbase源码系列(十)HLog与日志恢复
- hbase源码系列(六)HMaster启动过程
- 如何写好一篇漏洞报告(国外篇)
- hbase源码系列(八)从Snapshot恢复表
- hbase源码系列(七)Snapshot的过程
- CVE-2017-3085:Adobe Flash泄漏Windows用户凭证
- php概述
- php教程
- php环境搭建
- PHP书写格式
- php变量
- php常量
- PHP注释
- php数组
- php字符串 string
- PHP整型 integer
- PHP浮点型 float
- php布尔型
- php数据类型之数组
- php数据类型之对象
- php数据类型之null
- php数据类型之间的转换
- php运算符
- php表达式
- PHP循环控制
- PHP流程控制
- php函数
- php全局变量
- PHP魔术变量
- php命名空间
- php 日期
- PHP包含文件
- php文件
- PHP 文件上传
- php Cookies
- php Sessions
- php email
- php安全email
- php错误处理
- PHP异常处理
- php过滤器
- PHP 高级过滤器
- php json
- php 表单
- PHP MySQL 简介
- PHP 连接 MySQL
- php创建数据库
- php 创建表
- php mysq 插入数据
- PHP MySQL 插入多条数据
- PHP MySQL 预处理语句
- php mysql 读取数据
- php mysql where
- PHP MySQL Order By
- PHP MySQL Update
- PHP MySQL Delete
- php ODBC
- 关于跨域以及Spring Boot的解决方案
- FFmpeg进行音频的解码和播放
- Gitbook 安装及使用
- MySQL 视图、过程、函数
- 基于Spring Boot + Dubbo的全链路日志追踪(二)
- 基于Spring Boot + Dubbo的全链路日志追踪(一)
- 使用C语言编写Python扩展包
- PlantUML基本使用(一)--时序图
- gRPC基本使用(一)--java与go之间的相互调用
- confd基本使用--Nginx配置自动化
- JVM自定义类加载器
- Java代理相关:JDK动态代理、CGLIB动态代理
- Tomcat CPU占用100%异常分析与处理
- Solr基本搭建及MySQL配置
- Tomcat, Jre 证书相关