如何在Laravel模型查询中将软删除条件设为首个子句?
解决方案:调整软删除全局作用域的添加顺序
要让deleted_at IS NULL条件自动排在WHERE子句最前面,无需额外添加作用域,只需调整软删除全局作用域的添加时机即可:
在你的Company模型中重写boot方法,手动优先添加SoftDeletingScope:
<?php namespace App; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\Eloquent\SoftDeletingScope; use Illuminate\Database\Query\Builder; /** * @method static|Builder createdAtBigger(\DateTime $datetime) * @method static|Builder nameLike(string $name) * @method static|Builder oneOfBoard(array $boardIds) */ class Company extends Model { use SoftDeletes; protected static function boot() { // 先添加软删除的全局作用域,确保它的条件最先被加到查询里 static::addGlobalScope(new SoftDeletingScope); // 再执行父类和其他trait的初始化逻辑 parent::boot(); } // 你的现有作用域方法保持不变 public function scopeCreatedAtBigger(Builder $builder, $datetime) { return $builder->where('created_at', '>', $datetime); } public function scopeNameLike(Builder $builder, $name) { return $builder->where('name', 'LIKE', '%'.$name.'%'); } public function scopeOneOfBoard(Builder $builder, $boardIds) { return $builder->whereIn('board_id', $boardIds); } /** * the other methods and scopes * ... * ... * ... */ }
为什么这样有效?
Laravel的全局作用域是按添加顺序生成WHERE条件的:默认情况下SoftDeletes trait的初始化逻辑在模型自身boot方法之后执行,所以它的whereNull('deleted_at')会被追加到条件末尾。
现在我们把软删除作用域的添加放在模型boot方法最开头,让它成为第一个被应用的全局作用域,对应的条件自然就会排在WHERE子句的最前面。
效果验证
执行你原来的查询代码后,生成的SQL会变成:
SELECT * FROM company WHERE deleted_at IS NULL AND name LIKE '%XX Company%' AND created_at > '2022-02-14' AND board_id IN (1,2,3,4);
数据库会先通过deleted_at的索引过滤掉绝大多数已删除的行,再执行后续的模糊查询和范围查询,直接提升查询效率。
这个改动不需要修改任何现有业务代码,所有调用Company模型的地方都会自动生效,完全满足你的需求。
内容的提问来源于stack exchange,提问作者biyro2
相关产品推荐
相关产品推荐

