You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel Eloquent使用join时如何自动过滤关联表deleted_at=null数据?

解决Laravel中Join软删除模型自动过滤的问题

下面提供几种无需手动添加where('tb_b.deleted_at', null)的方案,保留原有join代码结构的同时自动过滤已删除数据:

方案1:全局扩展查询构建器的Join逻辑

在项目的App\Providers\AppServiceProvider的boot方法中,重写查询构建器的join方法,自动检测关联表对应的模型是否使用SoftDeletes trait,若使用则自动添加过滤条件:

use Illuminate\Database\Query\Builder;
use Illuminate\Support\Str;
use Illuminate\Database\Eloquent\SoftDeletes;

public function boot()
{
    // 先保存原生join方法
    Builder::macro('originalJoin', Builder::getMacro('join'));
    
    // 重写join方法,自动处理软删除过滤
    Builder::macro('join', function ($table, $first, $operator = null, $second = null, $type = 'inner', $where = false) {
        // 执行原生join逻辑
        $builder = $this->originalJoin($table, $first, $operator, $second, $type, $where);
        
        // 仅当查询来自模型实例时,检查关联表的软删除状态
        if (property_exists($this, 'model') && $this->model) {
            $modelClass = $this->getModelForTable($table);
            // 如果关联表对应的模型使用了SoftDeletes,自动添加过滤条件
            if ($modelClass && in_array(SoftDeletes::class, class_uses_recursive($modelClass))) {
                $builder->whereNull("{$table}.deleted_at");
            }
        }
        
        return $builder;
    });

    // 同步处理leftJoin等其他join类型
    Builder::macro('leftJoin', function ($table, $first, $operator = null, $second = null) {
        return $this->join($table, $first, $operator, $second, 'left');
    });
}

// 辅助方法:根据表名匹配对应的模型类(可根据项目结构调整)
private function getModelForTable(string $table): ?string
{
    // 默认规则:表名复数转单数驼峰,对应App\Models下的模型
    $modelName = Str::studly(Str::singular($table));
    $modelClass = "App\\Models\\{$modelName}";

    if (class_exists($modelClass)) {
        return $modelClass;
    }

    // 自定义表名-模型映射(处理特殊命名的表)
    $customMap = [
        'tb_b' => \App\Models\TbB::class,
        // 其他特殊表映射...
    ];

    return $customMap[$table] ?? null;
}

配置完成后,原有代码TabelA::join('tb_b', 'tb_a.id_b', '=', 'tb_b.id_b')->get()会自动过滤tb_b中已删除的数据,完全无需修改业务代码。

方案2:模型内定义带软删除过滤的Join Scope

如果不想全局修改查询构建器,可以在项目的基础模型(如App\Models\Model)中定义自定义Scope,专门用于关联软删除模型的join:

use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Str;

public function scopeJoinSoftDeletedTable(Builder $query, string $table, string $first, string $operator = null, string $second = null, string $type = 'inner')
{
    $query->join($table, $first, $operator, $second, $type);
    
    // 检查关联表模型是否使用SoftDeletes
    $modelClass = $this->getModelForTable($table);
    if ($modelClass && in_array(SoftDeletes::class, class_uses_recursive($modelClass))) {
        $query->whereNull("{$table}.deleted_at");
    }
    
    return $query;
}

// 辅助方法:匹配表名到模型
private function getModelForTable(string $table): ?string
{
    $modelName = Str::studly(Str::singular($table));
    $modelClass = "App\\Models\\{$modelName}";

    if (class_exists($modelClass)) {
        return $modelClass;
    }

    $customMap = [
        'tb_b' => \App\Models\TbB::class,
    ];

    return $customMap[$table] ?? null;
}

使用时只需将原有join替换为自定义Scope:

$data = TabelA::joinSoftDeletedTable('tb_b', 'tb_a.id_b', '=', 'tb_b.id_b')->get();

特殊场景:临时取消软删除过滤

如果某些业务需要查询包含已删除的数据,可以添加自定义Scope移除过滤条件:

public function scopeWithTrashedJoin(Builder $query, string $table)
{
    // 移除指定表的deleted_at过滤条件
    $query->getQuery()->wheres = collect($query->getQuery()->wheres)->filter(function ($where) use ($table) {
        return !($where['column'] === "{$table}.deleted_at" && $where['operator'] === 'is null');
    })->values()->all();
    
    return $query;
}

使用示例:

// 查询包含tb_b已删除数据的结果
$data = TabelA::join('tb_b', 'tb_a.id_b', '=', 'tb_b.id_b')->withTrashedJoin('tb_b')->get();

内容的提问来源于stack exchange,提问作者Aditya Pratama

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 14:57:47