laravel为Model设置全局作用域

news2025/1/23 7:28:40

如果一个项目中存在这么一个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.coloradmin.cn/o/1937708.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【HarmonyOS】HarmonyOS NEXT学习日记:四、布局与容器组件

【HarmonyOS】HarmonyOS NEXT学习日记:四、布局与容器组件 学习了基础组件之后,想要利用基础组件组装成一个页面,自然就要开始学习布局相关的知识。我理解的ArkUI的布局分为两个部分 一、组件自身的通用属性,诸如weight、height、…

Direct3D入门指南:创建对象、绘制几何体

DirectX是一个复杂但功能强大的API集,掌握了DirectX,特别是Direct3D,就意味着能够开发出高性能的图形应用和游戏。下面为大家讲解Direct3D的基础入门知识,以便大家能够快速上手。 创建设备 在Direct3D中,所有图形渲染…

LeetCode刷题记录(第二天)1. 两数之和

题目&#xff1a;1. 两数之和 标签&#xff1a;数组 哈希表 题目信息&#xff1a; 思路一&#xff1a;暴力做法 直接两重for循环遍历&#xff0c;判断两数和为target的时候返回下标结果 代码实现&#xff1a; class Solution { public:vector<int> twoSum(vector&…

深度剖析机构号矩阵系统:如何根据业务需求做出明智选择

在数字化营销的浪潮中&#xff0c;短视频平台如抖音、快手等已成为品牌传播和用户互动的重要渠道。为了更高效地管理这些平台的账号&#xff0c;机构号矩阵系统应运而生。本文将深度剖析机构号矩阵系统&#xff0c;并探讨如何根据业务需求做出明智的选择。 机构号矩阵系统概述…

【Linux】socket 套接字 / 序列化与反序列化

目录 一. TCP 网络程序简易计算器1. 核心功能2. 程序结构3. 服务器初始化4. 服务器启动5. 业务处理6. 客户端初始化7. 客户端启动 二. 序列化与反序列化1. 协议2. 序列化与反序列化 一. TCP 网络程序 简易计算器 1. 核心功能 客户端向服务器发送数据, 服务器进行计算并返回结…

免费【2024】springboot OA公文发文管理系统

博主介绍&#xff1a;✌CSDN新星计划导师、Java领域优质创作者、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和学生毕业项目实战,高校老师/讲师/同行前辈交流✌ 技术范围&#xff1a;SpringBoot、Vue、SSM、HTML、Jsp、PHP、Nodejs、Python、爬虫、数据可视化…

金融数据的pandas模块应用

金融数据的pandas模块应用 数据链接&#xff1a;https://pan.baidu.com/s/1VMh8-4IeCUYXB9p3rL45qw 提取码&#xff1a;c6ys 1. 导入所需基础库 import pandas as pd import matplotlib.pyplot as plt from pylab import mpl mpl.rcParams[font.sans-serif][FangSong] mpl.rcP…

【基础】模拟题 角色授权类

3413. DHCP服务器 题目 提交记录 讨论 题解 视频讲解 动态主机配置协议&#xff08;Dynamic Host Configuration Protocol, DHCP&#xff09;是一种自动为网络客户端分配 IP 地址的网络协议。 当支持该协议的计算机刚刚接入网络时&#xff0c;它可以启动一个 DHCP 客户…

html改写vue日志

本人最近学了vue&#xff0c;想着练手的方法就是改写之前在公司开发的小系统前端&#xff0c;将前端的AJAXJSThymeleaf改为axiosvue。 改写html 将<html>中的<head>和<body>结构移除&#xff0c;将css部分移入<style>&#xff0c; 重新定义了全局的&…

视频共享融合赋能平台LntonCVS视频监控管理平台视频云解决方案

LntonCVS是基于国家标准GB28181协议开发的视频监控与云服务平台&#xff0c;支持多设备同时接入。该平台能够处理和分发多种视频流格式&#xff0c;包括RTSP、RTMP、FLV、HLS和WebRTC。主要功能包括视频直播监控、云端录像与存储、检索回放、智能告警、语音对讲和平台级联&…

约束

概述 概念 约束是作用于表中字段上的规则&#xff0c;用于限制存储在表中的数据。 目的 保证数据库中数据的正确、有效性和完整性。 分类 【注意】约束是作用于表中字段上的&#xff0c;可以在创建表/修改表的时候添加约束。 约束演示 根据需求&#xff0c;完成表结构的…

linux 之时间子系统(八):hrtime 的实现机制

一、hrtimer 概述 在Linux内核中已经存在了一个管理定时器的通用框架。不过它也有很多不足&#xff0c;最大的问题是其精度不是很高。哪怕底层的定时事件设备精度再高&#xff0c;定时器层的分辨率只能达到Tick级别&#xff0c;按照内核配置选项的不同&#xff0c;在100Hz到10…

【性能评估工具】—— SLAM性能评估工具evo的安装与常用指令的详细介绍

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、evo的安装1. 直接指令安装2. 换下载源进行安装 二、evo的使用1. 常见的数据集格式介绍3. 数据格式转换4. evo工具常用命令介绍5. 指令命令的使用 三、常用指…

科技赋能,智慧粮仓视频综合管理方案助力粮食安全

一、背景需求 随着科技的快速发展&#xff0c;智慧化、智能化管理已成为各行各业的重要发展方向。粮食仓储作为国家粮食安全战略的重要组成部分&#xff0c;其管理的科学性和智能化水平直接关系到粮食的存储安全、品质保障和运营效率。 因此&#xff0c;TSINGSEE青犀提出一套…

Agilent 安捷伦 DSO90804A 高性能示波器

Agilent 安捷伦 DSO90804A 高性能示波器 DSO90804A Infiniium 高性能示波器&#xff1a;8 GHz 8 GHz4个模拟通道高达 1 Gpts 存储器和 40 GSa/s 采样率可以提供更完整的信号迹线捕获50 mV/格时低至 1.15 mVrms 的本底噪声和深入的抖动分析功能可以确保卓越的测量精度硬件加速…

B3636 源代码

快速直达专线 原文 题解没给代码&#xff0c;所以这里给一下 #include<bits/stdc.h> using namespace std; int f[10000007]; int main(){int n;cin>>n;//int cab;f[1]0;for(int i2;i<n5;i){if(i%20)f[i]min(f[i-1]1,f[i/2]1);//是偶数都有可能else f[i]f[i-1…

如何使用简鹿水印助手或 Photoshop 给照片添加文字

在社交媒体中&#xff0c;为照片添加个性化的文字已经成为了一种流行趋势。无论是添加注释、引用名言还是表达情感&#xff0c;文字都能够为图片增添额外的意义和风格。本篇文章将使用“简鹿水印助手”和“Adobe Photoshop”这两种工具给照片添加文字的详细步骤。 使用简鹿水印…

c++信号和槽机制的轻量级实现,sigslot 库介绍及使用

Qt中的信号与槽机制很好用&#xff0c;然而只在Qt环境中。在现代 C 编程中&#xff0c;对象间的通信是一个核心问题。为了解决这个问题&#xff0c;许多库提供了信号和槽&#xff08;Signals and Slots&#xff09;机制。今天推荐分享一个轻量级的实现&#xff1a;sigslot 库。…

bootstrap-datetimepicker设置时分

bootstrap-datetimepicker设置时分 需求背景时分年月日 需求背景 在日常工作中遇到一个业务场景&#xff0c;需要时间控件来选择时分&#xff0c;但是不需要年月日的成分&#xff0c;实现之后的效果如图 那么下面就开始查找相关的时间控件插件&#xff0c;这里示例图中用到的…

9.11和9.9哪个大?

没问题 文心一言 通义千问