You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

102 lines
2.9 KiB

<?php
namespace App\Services;
use App\Models\AdminUser;
use App\Models\Menu;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
class AdminMenuService
{
/** @return array<int, array<string, mixed>> */
public function treeForUser(AdminUser $user): array
{
$menus = $this->menusQueryForUser($user);
$tree = $this->buildTree($menus);
return $tree->values()->all();
}
protected function menusQueryForUser(AdminUser $user): Collection
{
$base = Menu::query()->where('status', 1)->orderBy('sort');
if ($user->isSuperAdmin()) {
return $base->get();
}
$roleIds = $user->roles()->where('status', 1)->pluck('id');
$menuIds = DB::table('role_menus')
->whereIn('role_id', $roleIds)
->pluck('menu_id')
->unique()
->all();
if ($menuIds === []) {
return collect();
}
$expandedIds = $this->expandMenuParents($menuIds);
return $base->whereIn('id', $expandedIds)->get();
}
/**
* @param array<int, int> $menuIds
* @return array<int, int>
*/
protected function expandMenuParents(array $menuIds): array
{
$idSet = collect($menuIds)->flip()->all();
$queue = $menuIds;
while ($queue !== []) {
$id = array_pop($queue);
$m = Menu::query()->find($id);
if ($m && $m->parent_id && ! isset($idSet[$m->parent_id])) {
$idSet[$m->parent_id] = 0;
$queue[] = $m->parent_id;
}
}
return array_map('intval', array_keys($idSet));
}
/**
* @param Collection<int, Menu> $menus
* @return \Illuminate\Support\Collection<int, array<string, mixed>>
*/
protected function buildTree(Collection $menus): Collection
{
$byParent = $menus->groupBy(fn (Menu $m) => $m->parent_id ?? 0);
$build = function ($parentId) use (&$build, $byParent): Collection {
return ($byParent->get($parentId, collect()))->map(function (Menu $m) use (&$build) {
$node = [
'id' => $m->id,
'parent_id' => $m->parent_id,
'path' => $m->path,
'name' => $m->name,
'title' => $m->title,
'component' => $m->component,
'icon' => $m->icon,
'sort' => $m->sort,
'visible' => $m->visible,
'keep_alive' => $m->keep_alive,
'permission_code' => $m->permission_code,
'status' => (int) $m->status,
];
$children = $build($m->id);
if ($children->isNotEmpty()) {
$node['children'] = $children->values()->all();
}
return $node;
});
};
return $build(0);
}
}