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

Laravel树形结构菜单对象转数组求助,树形菜单实现遇阻

我之前在Laravel项目里也碰到过一模一样的需求——把带parent_id的树形菜单数据表转成嵌套数组,结合你的表结构,给你两个亲测好用的方案:

第一步:先给模型配置关联

首先得在你的菜单模型(假设叫Menu)里定义父级和子级的关联,这是Laravel处理树形结构的基础:

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\BelongsTo;

class Menu extends Model
{
    protected $fillable = ['title', 'slug', 'parent_id'];

    // 关联父级菜单
    public function parent(): BelongsTo
    {
        return $this->belongsTo(Menu::class, 'parent_id');
    }

    // 关联子菜单,顺便按标题排序
    public function children(): HasMany
    {
        return $this->hasMany(Menu::class, 'parent_id')->orderBy('title');
    }
}
方案一:模型内封装递归方法(最直观)

直接在模型里写静态方法,把树形转数组的逻辑封装起来,调用起来特别方便:

// 在Menu模型里添加这两个方法
public static function toTreeArray(): array
{
    // 先获取所有顶级菜单(parent_id=0),预加载子菜单避免N+1查询
    $topMenus = self::where('parent_id', 0)->with('children')->get();

    return self::buildTree($topMenus);
}

private static function buildTree($menus): array
{
    $tree = [];

    foreach ($menus as $menu) {
        // 先把菜单对象转成基础数组
        $item = $menu->toArray();
        // 如果当前菜单有子菜单,递归处理子菜单
        if ($menu->children->isNotEmpty()) {
            $item['children'] = self::buildTree($menu->children);
        }
        $tree[] = $item;
    }

    return $tree;
}

调用方式也很简单,在控制器或者任何地方直接用:

$menuTree = Menu::toTreeArray();
// 打印看看结果
dd($menuTree);
方案二:利用Laravel集合操作(更灵活)

如果你不想把逻辑写在模型里,或者需要更灵活的处理(比如自定义字段),可以用集合的方法来实现:

// 在控制器里写这段代码
use App\Models\Menu;

public function getMenuTree()
{
    // 获取所有菜单,用id做键名方便快速查找
    $allMenus = Menu::all()->keyBy('id');

    // 筛选出顶级菜单,然后递归构建子菜单
    $menuTree = $allMenus->filter(function ($menu) {
        return $menu->parent_id === 0;
    })->map(function ($menu) use ($allMenus) {
        return $this->buildMenuItem($menu, $allMenus);
    })->values()->toArray();

    return $menuTree;
}

// 辅助方法:递归构建单个菜单项
private function buildMenuItem($menu, $allMenus): array
{
    $item = $menu->toArray();
    // 查找当前菜单的所有子菜单
    $children = $allMenus->filter(function ($child) use ($menu) {
        return $child->parent_id === $menu->id;
    });

    if ($children->isNotEmpty()) {
        $item['children'] = $children->map(function ($child) use ($allMenus) {
            return $this->buildMenuItem($child, $allMenus);
        })->values()->toArray();
    }

    return $item;
}

这两种方案都能完美把你的树形菜单转成嵌套数组,第一种更适合复用,第二种更灵活,你可以根据自己的需求选~

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 03:35:12