如何在Laravel 5.5中基于多参数筛选含关联的Profiles模型集合?
Hey,看起来你已经把筛选的基础架子搭起来了——用了PerfisFiltros和自定义的filter()方法,接下来咱们把各个筛选逻辑补全,覆盖关联分类、地区选择和跨模型的关键词搜索这几个场景,一步步来:
1. 先确认模型关联的正确性
首先得保证Perfis模型里的关联定义没问题,比如对应你提到的belongsTo(地区)和HasManyThrough(分类):
// app/Models/Perfis.php class Perfis extends Model { // 关联地区(belongsTo) public function regiao() { return $this->belongsTo(Regiao::class); } // HasManyThrough关联分类(假设中间模型是Grupo,根据你的实际关联调整字段) public function categorias() { return $this->hasManyThrough( Categoria::class, Grupo::class, 'perfil_id', // Grupo表中关联Perfis的字段 'grupo_id', // Categoria表中关联Grupo的字段 'id', // Perfis表的主键 'id' // Grupo表的主键 ); } }
2. 完善筛选器类
PerfisFiltros 这个类要承担验证筛选参数和执行筛选逻辑的职责,这里假设你用的是Laravel的表单请求类:
// app/Http/Requests/PerfisFiltros.php class PerfisFiltros extends FormRequest { // 验证规则:确保参数合法 public function rules() { return [ 'regiao_id' => 'nullable|exists:regioes,id', 'categoria_id' => 'nullable|exists:categorias,id', 'keyword' => 'nullable|string|max:255', ]; } // 核心筛选逻辑:把参数转化为查询条件 public function apply($query) { // 筛选地区:直接匹配关联ID即可 if ($this->regiao_id) { $query->where('regiao_id', $this->regiao_id); // 也可以用关联查询写法:$query->whereHas('regiao', fn($q) => $q->where('id', $this->regiao_id)); } // 筛选HasManyThrough的分类:必须用whereHas,因为是穿透关联 if ($this->categoria_id) { $query->whereHas('categorias', fn($q) => $q->where('categorias.id', $this->categoria_id)); } // 关键词搜索:覆盖Perfis自身和关联模型的字段 if ($this->keyword) { $keyword = "%{$this->keyword}%"; // 用闭包包裹所有orWhere,避免和其他筛选条件的where冲突 $query->where(function($q) use ($keyword) { $q->where('nome', 'like', $keyword) ->orWhere('descricao', 'like', $keyword) // 搜索关联的地区名称 ->orWhereHas('regiao', fn($subq) => $subq->where('nome', 'like', $keyword)) // 搜索关联的分类名称 ->orWhereHas('categorias', fn($subq) => $subq->where('nome', 'like', $keyword)); }); } // 可选:预加载关联,避免N+1查询 $query->with(['regiao', 'categorias']); return $query; } }
3. 补全
Perfis模型的filter()方法 让模型的静态方法调用筛选器的逻辑:
// app/Models/Perfis.php public static function filter(PerfisFiltros $filtros) { $query = static::query(); return $filtros->apply($query); }
4. 控制器调整(可选:分页+保持筛选状态)
把get()改成分页,同时把筛选参数带回视图,保持用户的选择状态:
// app/Http/Controllers/PerfisController.php public function index(PerfisFiltros $filtros, Request $request) { // 应用筛选并分页 $perfis = Perfis::filter($filtros)->paginate(10); // 把所有筛选参数传递给视图,保持表单选中状态 return view('perfis.index', compact('perfis'))->with($request->all()); }
5. 前端表单示例(Blade模板)
写一个能保留筛选状态的表单,方便用户操作:
<!-- resources/views/perfis/index.blade.php --> <form method="GET" action="{{ route('perfis.index') }}"> <!-- 地区下拉框 --> <div class="mb-3"> <label class="form-label">地区</label> <select name="regiao_id" class="form-select"> <option value="">全部地区</option> @foreach($regioes as $regiao) <option value="{{ $regiao->id }}" {{ request('regiao_id') == $regiao->id ? 'selected' : '' }}> {{ $regiao->nome }} </option> @endforeach </select> </div> <!-- 分类下拉框 --> <div class="mb-3"> <label class="form-label">分类</label> <select name="categoria_id" class="form-select"> <option value="">全部分类</option> @foreach($categorias as $categoria) <option value="{{ $categoria->id }}" {{ request('categoria_id') == $categoria->id ? 'selected' : '' }}> {{ $categoria->nome }} </option> @endforeach </select> </div> <!-- 关键词搜索框 --> <div class="mb-3"> <label class="form-label">关键词搜索</label> <input type="text" name="keyword" class="form-control" value="{{ request('keyword') }}" placeholder="搜索名称、描述、地区或分类..."> </div> <button type="submit" class="btn btn-primary">筛选</button> </form> <!-- 显示筛选结果 --> @foreach($perfis as $perfil) <div class="card mb-3"> <div class="card-body"> <h5 class="card-title">{{ $perfil->nome }}</h5> <p class="card-text">地区:{{ $perfil->regiao->nome }}</p> <p class="card-text">分类:{{ $perfil->categorias->pluck('nome')->implode(', ') }}</p> </div> </div> @endforeach <!-- 分页链接:保留筛选参数 --> {{ $perfis->appends(request()->all())->links() }}
几个关键注意点
- HasManyThrough筛选:必须用
whereHas,不能直接用where字段,因为穿透关联的字段不在当前模型表中。 - 关键词搜索的闭包:一定要把所有
orWhere放在闭包里,否则会和其他筛选条件的where产生逻辑冲突,导致筛选结果不符合预期。 - 性能优化:给经常搜索的字段(比如
nome、descricao)加数据库索引;用with()预加载关联,避免N+1查询。
内容的提问来源于stack exchange,提问作者Lucas Pacheco
相关产品推荐
相关产品推荐

