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

Laravel多层级父子关系用户姓名深度搜索需求实现

多层级父子关系的姓名链搜索实现(Laravel)

需求背景

现有Laravel项目中,User模型对应的数据表包含id、name、father_id字段,形成多层级父子关联结构。当前仅支持单级姓名模糊搜索,需升级为支持**连续姓名链(如"William David Jacob Robert John")**的4-5层父子关系匹配,返回最贴合的近似结果。例如输入"Ahmad Suleiman Mohammed Suleiman"时,需匹配「Ahmad的父亲是Suleiman,Suleiman的父亲是Mohammed,Mohammed的父亲是Suleiman」的层级关系。

现有基础代码

数据表结构

| id | name    | father_id |
|----|---------|-----------|
| 1  | John    | NULL      |
| 2  | Robert  | 1         |
| 3  | Jacob   | 2         |
| 4  | William | 3         |
| 5  | David   | 3         |
| 6  | John    | 4         |
| 7  | William | 5         |
| 8  | John    | 7         |
| 9  | Thomas  | 8         |

UsersController 搜索方法

public function search(Request $request)
{
    $q = $request->get('q');
    $users = [];

    if ($q) {
        $users = User::with('father')
            ->where(function ($query) use ($q) {
                $query->where('name', 'like', '%'.$q.'%');
            })
            ->orderBy('name', 'asc')
            ->paginate(24);
    }

    return view('users.search', compact('users'));
}

User Model 关联定义

public function father()
{
    return $this->belongsTo(User::class);
}

解决方案实现

步骤1:扩展模型关联,支持多层级父级查询

在User模型中添加递归关联与指定层级查询方法,方便后续多层级匹配:

// 递归获取所有父级关系
public function ancestors()
{
    return $this->belongsTo(User::class, 'father_id')->with('ancestors');
}

// 获取指定层级的父级(比如第n层)
public function parentLevel($level)
{
    $query = $this->belongsTo(User::class, 'father_id');
    for ($i = 1; $i < $level; $i++) {
        $query = $query->belongsTo(User::class, 'father_id', 'id');
    }
    return $query;
}

步骤2:修改搜索逻辑,实现姓名链匹配

核心思路:拆分输入的姓名链为数组,倒序后从根节点开始匹配父级,计算匹配层级数并按匹配度排序。修改UsersController的search方法:

use Illuminate\Support\Facades\DB;

public function search(Request $request)
{
    $q = trim($request->get('q'));
    $users = collect();

    if ($q) {
        // 拆分姓名链并倒序,转为「根→父→子」的匹配顺序
        $nameChain = array_reverse(explode(' ', $q));
        $chainLength = count($nameChain);
        $maxLevels = min($chainLength, 5); // 限制最大匹配层级为5

        $baseQuery = User::query();

        // 动态连表查询每一层父级
        for ($level = 0; $level < $maxLevels; $level++) {
            $alias = 'parent_' . $level;
            $baseQuery->leftJoin("users as $alias", function ($join) use ($level, $alias, $nameChain) {
                if ($level === 0) {
                    $join->on('users.father_id', '=', "$alias.id");
                } else {
                    $prevAlias = 'parent_' . ($level - 1);
                    $join->on("$prevAlias.father_id", '=', "$alias.id");
                }
                // 模糊匹配当前层级的姓名
                $join->where("$alias.name", 'like', '%' . $nameChain[$level] . '%');
            });
        }

        // 计算匹配层级数,用于排序
        $matchScoreFields = array_map(function ($level) {
            return "CASE WHEN parent_$level.id IS NOT NULL THEN 1 ELSE 0 END";
        }, range(0, $maxLevels - 1));

        $users = $baseQuery
            ->select([
                'users.*',
                DB::raw('(' . implode(' + ', $matchScoreFields) . ') as match_score')
            ])
            ->with(['ancestors']) // 预加载所有父级,避免N+1查询
            ->having('match_score', '>', 0) // 只返回至少匹配1层的结果
            ->orderBy('match_score', 'desc') // 匹配度高的结果优先
            ->orderBy('name', 'asc')
            ->paginate(24);
    }

    return view('users.search', compact('users'));
}

步骤3:前端展示优化(可选)

在搜索结果视图中,可展示完整的匹配链与匹配度:

@foreach($users as $user)
    <div class="search-result-item">
        <h3>{{ $user->name }}</h3>
        <p>匹配关系链:
            @php
                $chain = collect([$user->name]);
                $ancestor = $user->father;
                while ($ancestor && count($chain) < 5) {
                    $chain->push($ancestor->name);
                    $ancestor = $ancestor->father;
                }
                echo implode(' → ', $chain->reverse()->toArray());
            @endphp
        </p>
        <small>匹配度:{{ $user->match_score }}/{{ min(count(explode(' ', $q)),5) }}</small>
    </div>
@endforeach

关键逻辑说明

  1. 姓名链倒序处理:输入的姓名链是「子→父→祖父」的顺序,倒序后转为「祖父→父→子」,更贴合数据库的关联查询逻辑。
  2. 动态连表:根据姓名链长度生成对应数量的左连接,实现多层级模糊匹配。
  3. 匹配度排序:通过统计非空匹配层级数计算得分,确保最贴合的结果排在前列。
  4. 性能控制:限制最大匹配层级为5,避免过多连表导致性能下降;使用预加载减少数据库查询次数。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 23:40:36