阅读目录
- 一、查询作用域
- 1.1 全局作用域
- 1 编写全局作用域
- 2 匿名全局作用域
- 3 取消全局作用域
- 1.2 本地作用域
- 1 编写本地作用域
- 2 动态作用域
- 二、应用示例
- 三、简单演示理解示例
一、查询作用域
1.1 全局作用域
全局作用域可以给模型的查询都添加上约束。
Laravel 的 软删除 功能就是利用此特性从数据库中获取 「未删除」的模型。
你可以编写你自己的全局作用域,很简单、方便的为每个模型查询都加上约束条件:
1 编写全局作用域
编写全局作用域很简单。
定义一个实现 Illuminate\Database\Eloquent\Scope
接口的类,并实现 apply
这个方法。
根据你的需求,在 apply
方法中加入查询的 where
条件:
<?php
namespace App\Scopes;
use Illuminate\Database\Eloquent\Scope;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;
class AgeScope implements Scope
{
/**
* 把约束加到 Eloquent 查询构造中。
*
* @param \Illuminate\Database\Eloquent\Builder $builder
* @param \Illuminate\Database\Eloquent\Model $model
* @return void
*/
public function apply(Builder $builder, Model $model)
{
$builder->where('age', '>', 200);
}
}
{tip} 如果需要在 select 语句里添加字段,应使用
addSelect
方法,而不是 select 方法。
这将有效防止无意中替换现有 select 语句的情况。
应用全局作用域
要将全局作用域分配给模型,需要重写模型的 boot
方法并使用 addGlobalScope
方法:
<?php
namespace App;
use App\Scopes\AgeScope;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
/**
* 模型的 「启动」 方法.
* @return void
*/
protected static function boot()
{
parent::boot();
static::addGlobalScope(new AgeScope);
}
}
添加作用域后,对 User::all()
的查询会生成以下 SQL 查询语句:
select * from `users` where `age` > 200
2 匿名全局作用域
Eloquent 同样允许使用闭包定义全局作用域,这样就不需要为一个简单的作用域而编写一个单独的类:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;
class User extends Model
{
/**
*模型的「启动」方法.
* @return void
*/
protected static function boot()
{
parent::boot();
static::addGlobalScope('age', function (Builder $builder) {
$builder->where('age', '>', 200);
});
}
}
3 取消全局作用域
如果需要对当前查询取消全局作用域,需要使用 withoutGlobalScope
方法。
该方法仅接受全局作用域类名作为它唯一的参数:
User::withoutGlobalScope(AgeScope::class)->get();
或者,如果使用闭包定义全局作用域的话:
User::withoutGlobalScope('age')->get();
如果你需要取消部分或者全部的全局作用域的话,需要使用 withoutGlobalScopes 方法:
// 取消所有的全局作用域...
User::withoutGlobalScopes()->get();
// 取消部分全局作用域...
User::withoutGlobalScopes([
FirstScope::class, SecondScope::class
])->get();
1.2 本地作用域
本地作用域允许定义通用的约束集合以便在应用程序中重复使用。
例如,你可能经常需要获取所有 「流行」的用户。
1 编写本地作用域
要定义这样一个范围,只需要在对应的 Eloquent 模型方法前添加 scope 前缀:
作用域总是返回一个查询构造器实例:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
/**
* 只查询受欢迎的用户的作用域.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopePopular($query)
{
return $query->where('votes', '>', 100);
}
/**
* 只查询 active 用户的作用域.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopeActive($query)
{
return $query->where('active', 1);
}
}
使用本地作用域
一旦定义了作用域,就可以在查询该模型时调用作用域方法。
不过,在调用这些方法时不必包含 scope 前缀。
甚至可以链式调用多个作用域,例如:
$users = App\User::popular()->active()->orderBy('created_at')->get();
借助 or
查询运行符整合多个 Eloquent 模型,可能需要使用闭包回调:
$users = App\User::popular()->orWhere(function (Builder $query) {
$query->active();
})->get();
因为这样可能会有点麻烦,Laravel 提供了「高阶的」 orWhere 方法,它允许你在链式调用作用域时不使用闭包:
$users = App\User::popular()->orWhere->active()->get();
2 动态作用域
有时可能地希望定义一个可以接受参数的作用域。
把额外参数传递给作用域就可以达到此目的。
作用域参数要放在 $query
参数之后:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
/**
* 将查询作用域限制为仅包含给定类型的用户。
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param mixed $type
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopeOfType($query, $type)
{
return $query->where('type', $type);
}
}
这样就可以在调用作用域时传递参数了:
$users = App\User::ofType('admin')->get();
二、应用示例
$user = new ChatUser();
$chat_user = $user::scopeNickName($user,"大金")->get();
dd($chat_user->toArray());
运行结果
rray:1 [
0 => array:35 [
"id" => 87
"mpid" => 1
"openid" => "oYxpK0RsvcwgS9DtmIOuyb_BgJbo"
"nickname" => "大金"
"headimgurl" => "https://img-home.csdnimg.cn/images/20210817021749.png"
"sex" => 1
"subscribe" => 1
"subscribe_time" => 1508920878
"unsubscribe_time" => null
"relname" => null
"signature" => null
"mobile" => null
"is_bind" => 0
"language" => "zh_CN"
"country" => "中国"
"province" => "河南"
"city" => "商丘"
"remark" => ""
"group_id" => 0
"groupid" => 0
"tagid_list" => "[]"
"score" => 0
"money" => "0.00"
"latitude" => null
"longitude" => null
"location_precision" => null
"type" => 0
"unionid" => null
"password" => null
"last_time" => 586969200
"parentid" => 1
"isfenxiao" => 0
"totle_earn" => "0.00"
"balance" => "0.00"
"fenxiao_leavel" => 2
]
]
三、简单演示理解示例
在model中,创建加上一个 scope 前缀的方法即可。
class News extends Model
{
const TABLE = 'news';
//只包含展示的新闻的查询作用域
public function scopeScoShow($query)
{
return $query->where('is_show', 1);
}
//只包含某个类型的查询作用域,带参数
public function scopeScoType($query, $type)
{
return $query->where('type', $type);
}
}
调用
//使用时调用,首字母小写
News::scoShow()->get();
//带参数
News::scoShow()->scoType(2)->get();