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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 04:04:08