laravel为Model设置全局作用域

如果一个项目中存在这么一个sql条件在任何情况下或大多数情况都会被使用,同时很容易被开发者遗忘,那么就非常适用于今天要提到的这个功能,Eloquent\Model的全局作用域。

        首先看一个示例,有个数据表,结构如下:

现用Laravel写一个查询语句:

$post = PostModel::where('cate_id', 2)->toSql();

打印$post结果是:"select * from `news_post` where `cate_id` = ?"

现在就是只想查属于ID为001的学校的post数据,其他许多表都有这个school_id字段,都需要在查询时使用。这就是我们需要解决的简化的操作,实现自动在sql条件中添加"and school_id='ID001'"

结合官方文档和搜索引擎查询结果,找到一个实现方案就是设置全局作用域:

首先创建基础Model并 添加scope:

namespace App\Models;use App\Models\Scopes\MyScopes;
use Illuminate\Database\Eloquent\Model;class BaseModel extends Model
{public $school_id = '';public $alias = '';public function __construct(array $attributes = []){$this->bootIfNotBooted();$this->initializeTraits();$this->syncOriginal();$token = app()->get('mytoken');$user = getDataByToken($token); //根据token获取存储在redis里的用户数据if(!empty($user) && isset($user['school_id'])){$attributes['school_id'] = $this->school_id = $user['school_id'];}$this->fill($attributes);}protected static function boot(){parent::boot();static::addGlobalScope(new MyScopes());}}

在构造方法中通过登录token信息获取用户的基础信息包括所属学校ID,赋值给model属性,同时在boot中addGlobalScope全局添加自定义作用域,MyScopes如下:

namespace App\Models\Scopes;use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Scope;class MyScopes implements Scope{public function apply(Builder $builder, Model $model){$from = $builder->getQuery()->from;//表 + 别名(如果有的话)if(is_string($from)){ //不考虑非字符串情况,如子查询$fromArr = explode(' ', str_ireplace(' as ', ' ', $from));$alias = isset($fromArr[1]) ? $fromArr[1] : '';//添加全局条件if(!empty($model->school_id)){$fieldName = $alias ? $alias . '.' . 'school_id' : 'school_id';$builder->where($fieldName, $model->school_id);}}}
}

根据model属性是否有值决定是否添加查询条件,$builder->getQuery()获取到的是Query\Builder实例:

同时依据from来判断是否存在别名的情况,附加别名,适配为表设置别名或联查情况。

最后在后续创建Model时都需要继承BaseModel即可。

这样重新执行最开始的sql查询语句,打印出来的结果就是:

"select * from `news_post` where `cate_id` = ? and `school_id` = ?"

这样就达到了预期的效果,自动添加查询条件了,下面再试试增删改

更新:

DB::enableQueryLog();
PostModel::where('cate_id', 2)->update(['is_hot' => 1]);
$log = DB::getQueryLog();
var_dump($log);exit;

打印结果:

array(1){
    [
        0
    ]=>array(3){
        [
            "query"
        ]=>string(75)"update `news_post` set `is_hot` = ? where `cate_id` = ? and `school_id` = ?"[
            "bindings"
        ]=>array(3){
            [
                0
            ]=>int(1)[
                1
            ]=>int(2)[
                2
            ]=>string(5)"ID001"
        }[
            "time"
        ]=>float(310.23)
    }
}

删除:

DB::enableQueryLog();
PostModel::where(['title' => '春天', 'is_hot' => 0])->delete();
$log = DB::getQueryLog();
var_dump($log);exit;

打印结果:

array(1){
    [
        0
    ]=>array(3){
        [
            "query"
        ]=>string(80)"delete from `news_post` where (`title` = ? and `is_hot` = ?) and `school_id` = ?"[
            "bindings"
        ]=>array(3){
            [
                0
            ]=>string(6)"春天"[
                1
            ]=>int(0)[
                2
            ]=>string(5)"ID001"
        }[
            "time"
        ]=>float(217.09)
    }
}

 增:

DB::enableQueryLog();
$model = new PostModel();
$model->setAttribute('title', '冬天');
$model->setAttribute('content', '冬天,寒风彻骨!');
$model->setAttribute('uid', 1);
$model->setAttribute('cate_id', 2);
$model->setAttribute('createtime', time());
/* $model->title='冬天';
$model->content='冬天,寒风彻骨!';
$model->uid=1;
$model->cate_id=2;
$model->createtime=time();*/
$model->save();
$log = DB::getQueryLog();
var_dump($log);exit;

打印结果:

array(1){
    [
        0
    ]=>array(3){
        [
            "query"
        ]=>string(115)"insert into `news_post` (`school_id`, `title`, `content`, `uid`, `cate_id`, `createtime`) values (?, ?, ?,
?, ?, ?)"[
            "bindings"
        ]=>array(6){
            [
                0
            ]=>string(5)"ID001"[
                1
            ]=>string(6)"冬天"[
                2
            ]=>string(22)"冬天,寒风彻骨!"[
                3
            ]=>int(1)[
                4
            ]=>int(2)[
                5
            ]=>int(1721413065)
        }[
            "time"
        ]=>float(48.68)
    }
}

插入时也自动加入了school_id的数据

插入有多重形式,试试其他的:

create方法:

DB::enableQueryLog();
$data = ['title' => '秋天','content' => '秋天,是收获的季节,也是思念的季节,它以一种宁静而深沉的美,让人沉醉!','uid' => 2,'cate_id' => 2,'createtime' => time(),
];
PostModel::create($data);
$log = DB::getQueryLog();
var_dump($log);exit;

打印结果:

array(1){
    [
        0
    ]=>array(3){
        [
            "query"
        ]=>string(115)"insert into `news_post` (`title`, `content`, `uid`, `cate_id`, `createtime`, `school_id`) values (?, ?, ?,
?, ?, ?)"[
            "bindings"
        ]=>array(6){
            [
                0
            ]=>string(6)"秋天"[
                1
            ]=>string(105)"秋天,是收获的季节,也是思念的季节,它以一种宁静而深沉的美,让人沉醉!"[
                2
            ]=>int(2)[
                3
            ]=>int(2)[
                4
            ]=>int(1721413472)[
                5
            ]=>string(5)"ID001"
        }[
            "time"
        ]=>float(142.4)
    }
}

由上可知 create方法同样适用。

insert方法:

DB::enableQueryLog();
$data = ['title' => '秋天','content' => '秋风送爽,落叶轻舞,绘就一幅金色的画卷。','uid' => 2,'cate_id' => 2,'createtime' => time(),
];
PostModel::insert($data);
$log = DB::getQueryLog();
var_dump($log);exit;

打印结果:

array(1){
    [
        0
    ]=>array(3){
        [
            "query"
        ]=>string(99)"insert into `news_post` (`title`, `content`, `uid`, `cate_id`, `createtime`) values (?, ?, ?, ?, ?)"[
            "bindings"
        ]=>array(5){
            [
                0
            ]=>string(6)"秋天"[
                1
            ]=>string(60)"秋风送爽,落叶轻舞,绘就一幅金色的画卷。"[
                2
            ]=>int(2)[
                3
            ]=>int(2)[
                4
            ]=>int(1721413774)
        }[
            "time"
        ]=>float(145.6)
    }
}

由上可见insert方法似乎不适用。其实也可以从源码中看出端倪,Illuminate\Database\Eloquent\Builder中有一个属性:

表明了这些方法都是从query builder返回结果的。且在Eloquent\Builder中确实没有找到insert方法的定义,这也就导致基于Eloquent\Builder的一些操作没有生效。

同时使用Illuminate\Support\Facades\DB的操作也是基于query builder的亦无法使用作用域或model属性添加额外条件。

在Illuminate\Database\Query\Builder中找到了一个属性如下介绍:

 感觉似乎可以在query执行前进行干预,查询相关资料,在provider中尝试注册了下但没有生效,若有成功的大佬希望能交流一下。

接下来尝试下连表查询:

$log = PostModel::from('post as p')->leftjoin('user as u', 'p.uid', '=', 'u.id')->where(['p.cate_id' => '2'])->toSql();

打印结果:

 "select * from `news_post` as `news_p` left join `news_user` as `news_u` on `news_p`.`uid` = `news_u`.`id` where (`news_p`.`cate_id` = ?) and `news_p`.`school_id` = ?"

 子表查询:

DB::enableQueryLog();
$subQuery = PostModel::where('is_hot', 1)->select(['uid', 'title','cate_id']);
$post = PostModel::fromSub($subQuery, 'p')->where('cate_id', 2)->select(['title'])->get();
$log = DB::getQueryLog();
var_dump($log);exit;

打印结果:

array(1){
    [
        0
    ]=>array(3){
        [
            "query"
        ]=>string(142)"select `title` from (select `uid`, `title`, `cate_id` from `news_post` where `is_hot` = ? and `school_id` =
?) as `news_p` where `cate_id` = ?"[
            "bindings"
        ]=>array(3){
            [
                0
            ]=>int(1)[
                1
            ]=>string(5)"ID001"[
                2
            ]=>int(2)
        }[
            "time"
        ]=>float(23.76)
    }
}

 子表条件查询:

DB::enableQueryLog();
$subQuery = PostModel::where('is_hot', 1)->select([DB::raw('DISTINCT(uid)')]);
UserModel::whereIn('uid', $subQuery)->get(['username']);
$log = DB::getQueryLog();
var_dump($log);exit;

打印结果:

array(1){
    [
        0
    ]=>array(3){
        [
            "query"
        ]=>string(148)"select `username` from `news_user` where `uid` in (select DISTINCT(uid) from `news_post` where `is_hot` = ?
and `school_id` = ?) and `school_id` = ?"[
            "bindings"
        ]=>array(3){
            [
                0
            ]=>int(1)[
                1
            ]=>string(5)"ID001"[
                2
            ]=>string(5)"ID001"
        }[
            "time"
        ]=>float(31.77)
    }
}

 测试的差不多了,综上除了insert方法,基于Model的增删改查操作,全局作用域基本都能生效,

且insert方法可以被save或create方法代替。

而基于Facades\DB的数据库操作如何全局添加条件还未找到可实现的方案,目前想到的是把Facades\DB进行封装,调用封装好的方法或对象,另外可以从Query\Builder的$beforeQueryCallbacks找突破口。希望有好的解决方案的大佬能分享出来,感谢!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/382783.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

深入浅出WebRTC—NACK

WebRTC 中的 NACK(Negative Acknowledgment)机制是实时通信中处理网络丢包的关键组件。网络丢包是常见的现象,尤其是在无线网络或不稳定连接中。NACK 机制旨在通过请求重传丢失的数据包来减少这种影响,从而保持通信的连续性和质量…

vue学习笔记(十一)——开发心得(axios的封装、promise细节、vue-router开发中的使用)

1. axios的网络请求的封装 1.1 为什么要封装api? 代码分层,便于以后的修改,无需触碰逻辑页面 目标: 网络请求,不散落在各个逻辑页面里,封装起来方便以后修改 1.2 封装api步骤 ① 在项目 src 下新建目录 utlis &am…

C++——初识模板

前言 模板是C中的重大板块,是使C真正超越C语言的工具,在C模板没有设计出来之前其实C是没有那么被行业和社会所认可的,本节我们将初步了解C中的模板(仅作大致讲解,具体的细枝末节将会再过几节讲解)&#xf…

Qt多语言功能实现

本文介绍Qt多语言功能实现。 应用程序多语言支持是常用功能,比如产品需要出口到不同语种的国家。采用Qt的多语言支持工具可以方便实现应用程序的多语言功能。本文以中英文语言切换为例,简要介绍Qt的多语言功能实现。 1.界面设计 界面设计需要考虑使用…

【数据分享】2013-2022年我国省市县三级的逐日SO2数据(excel\shp格式\免费获取)

空气质量数据是在我们日常研究中经常使用的数据!之前我们给大家分享了2000——2022年的省市县三级的逐日PM2.5数据和2013-2022年的省市县三级的逐日CO数据(均可查看之前的文章获悉详情)! 本次我们分享的是我国2013——2022年的省…

数据隐私保护与区块链技术的结合:新兴趋势分析

在当今数字化时代,数据隐私保护成为了一个备受关注的重要话题。随着个人数据的不断生成和流通,如何有效保护用户的隐私成为了技术创新的一个重要方向。区块链技术作为一种去中心化、安全性高且可追溯的技术手段,正在逐渐成为解决数据隐私保护…

golang 基础 泛型编程

(一) 示例1 package _caseimport "fmt"// 定义用户类型的结构体 type user struct {ID int64Name stringAge uint8 }// 定义地址类型的结构体 type address struct {ID intProvince stringCity string }// 集合转列表函数&#…

杰发科技Bootloader(2)—— 基于7840的Keil配置地址

序 在7840的sample代码里面有一个简单的Boot跳转APP的示例 PFlash地址从0开始 DFlash的地址从1000000开始 Boot解析 他的boot地址配置为0 Boot的代码主要是这几行,主要作用就是Flash的跳转 int main(void) {SystemClock_Config();InitDebug();printf("demo…

Leetcode 721.账户合并(hash+dfs)☆

思路: 最核心的地方在于如何合并?这里是通过具有相同的email进行账户的合并,这个相同的email类似于图中的共同节点将两个账户连接起来,所以将原来 账户名 -> 邮件1 邮件2.。。变成hash 邮件1 ->账户id1,账户id2…

ModelArts中sinh算子的开发

一、环境配置 1、创建notebook并连接 使用ModelArts新建一个notebook,我这里镜像选择第一个,里面含有cann和Ascend910处理器,我这里环境只能使用ssh连接,创建一个密钥对,保存到C盘中的user/Administrator/目录下。 在网页中选择使用vscode接入,等待vscode打开后,选择密…

【数据结构初阶】一篇文章带你超深度理解【单链表】

hi ! 目录 前言: 1、链表的概念和结构 2、单链表(Single List,简写SList)的实现 2.1 定义链表(结点)的结构 2.2 创建一个链表 2.3 打印链表 2.4 尾插 2.5 头插 2.6 尾删 2.7 头…

PT2262-IR

PT2262是一款很古老的编码芯片,其兼容型号有:SC2262,AD2262,SC2260(需改变匹配电阻)等。 依据其datasheet,PT2262射频模式工作原理: CODE BITS A Code Bit is the basic component of the encoded waveform, and ca…

Docker核心技术:Docker原理之Cgroups

云原生学习路线导航页(持续更新中) 本文是 Docker核心技术 系列文章:Docker原理之Cgroups,其他文章快捷链接如下: 应用架构演进容器技术要解决哪些问题Docker的基本使用Docker是如何实现的 Docker核心技术:…

Maven学习——Maven的下载、安装与配置(详细攻略!)

目录 前言 1.下载与安装 2.配置Maven的环境变量 3.配置Maven的本地仓库 4. 配置Maven的镜像远程仓库 前言 我在之前写了一篇博客,是介绍Maven的基本概念和下载安装,但是由于篇幅过长,Maven的下载与安装写的并不详细🐶&#x…

Windows系统设置暂停更新,暂停时间可达3000天,“永久”暂停更新,亲测有效

好多小伙伴被Windows系统的更新搞得很烦,经常在使用中自己下载更新包,占用网路资源,过段时间就要更新,特别讨厌 今天教你一招,可以暂停更新长达3000天,亲测有效 1、打开系统CMD命令执行窗口,输…

Ideal窗口中左右侧栏消失了

不知道大家在工作过程中有没有遇到过此类问题,不论是Maven项目还是Gradle项目,突然发现Ideal窗口右侧图标丢失了,同事今天突然说大象图标不见了,不知道怎样刷新gradle。 不要慌张,下面提供一些解决思路: 1…

超声波俱乐部:AI应用大爆发前夜,场景、闭环与LLM进化

7月13日,第十九期超声波俱乐部内部分享会在北京望京举行,本期的主题是:AI应用大爆发前夜,场景、闭环与LLM进化。 到场的嘉宾有:超声波创始人杨子超,超声波联合创始人、和牛商业创始人刘思雨,豆…

硅纪元视角 | 语音克隆突破:微软VALL-E 2,Deepfake新纪元!

在数字化浪潮的推动下,人工智能(AI)正成为塑造未来的关键力量。硅纪元视角栏目紧跟AI科技的最新发展,捕捉行业动态;提供深入的新闻解读,助您洞悉技术背后的逻辑;汇聚行业专家的见解,…

IP协议和路由转发

文章目录 IP协议IP报头网段划分特殊的IP私有IP和公有IP IP分片 路由 IP协议 IP协议提供了一种能力,将数据报从A主机送到B主机,TCP可以保证可靠性,所以TCP/IP协议可以将数据可靠的从A主机送到B主机。 IP报头 4位版本号(version): 指定IP协议…

Java 面试 | Redis

目录 1. 在项目中缓存是如何使用的?2. 为啥在项目中要用缓存?3. 缓存如果使用不当会造成什么后果?4. redis 和 memcached 有什么区别?5. redis 的线程模型是什么?6. 为什么单线程的 redis 比多线程的 memcached 效率要…