Files
sam-manage/app/Services/MenuService.php

325 lines
8.9 KiB
PHP

<?php
namespace App\Services;
use App\Models\Commons\Menu;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
class MenuService
{
/**
* 메뉴 목록 조회 (페이지네이션) - 트리 구조로 정렬
*/
public function getMenus(array $filters = [], int $perPage = 15): LengthAwarePaginator
{
$tenantId = session('selected_tenant_id');
$query = Menu::query()->withTrashed();
// 테넌트 필터링
if ($tenantId) {
// 특정 테넌트 선택 시: 해당 테넌트의 메뉴만
$query->where('tenant_id', $tenantId);
} else {
// 전체 선택 시: tenant_id가 NULL인 마스터 메뉴만
$query->whereNull('tenant_id');
}
// Soft Delete 필터
if (isset($filters['trashed'])) {
if ($filters['trashed'] === 'only') {
$query->onlyTrashed();
} elseif ($filters['trashed'] === 'with') {
$query->withTrashed();
}
}
// 검색 필터
if (! empty($filters['search'])) {
$search = $filters['search'];
$query->where(function ($q) use ($search) {
$q->where('name', 'like', "%{$search}%")
->orWhere('url', 'like', "%{$search}%");
});
}
// 활성 상태 필터
if (isset($filters['is_active'])) {
$query->where('is_active', $filters['is_active']);
}
// 부모 메뉴 필터 (트리 구조)
if (isset($filters['parent_id'])) {
if ($filters['parent_id'] === 'null') {
$query->whereNull('parent_id');
} else {
$query->where('parent_id', $filters['parent_id']);
}
}
// 모든 메뉴 가져오기 (트리 구조 정렬을 위해)
$allMenus = $query->with(['parent', 'tenant'])->orderBy('sort_order')->orderBy('id')->get();
// 트리 구조로 정렬 후 플랫한 배열로 변환
$flattenedMenus = $this->flattenMenuTree($allMenus);
// 수동 페이지네이션
$currentPage = request()->input('page', 1);
$offset = ($currentPage - 1) * $perPage;
$items = $flattenedMenus->slice($offset, $perPage)->values();
return new \Illuminate\Pagination\LengthAwarePaginator(
$items,
$flattenedMenus->count(),
$perPage,
$currentPage,
['path' => request()->url(), 'query' => request()->query()]
);
}
/**
* 트리 구조를 플랫한 배열로 변환 (depth 정보 포함)
*/
private function flattenMenuTree(Collection $menus, ?int $parentId = null, int $depth = 0): Collection
{
$result = collect();
$filteredMenus = $menus->where('parent_id', $parentId)->sortBy('sort_order');
foreach ($filteredMenus as $menu) {
$menu->depth = $depth;
// 자식 메뉴 존재 여부 확인
$menu->has_children = $menus->where('parent_id', $menu->id)->count() > 0;
$result->push($menu);
// 자식 메뉴 재귀적으로 추가
$children = $this->flattenMenuTree($menus, $menu->id, $depth + 1);
$result = $result->merge($children);
}
return $result;
}
/**
* 메뉴 상세 조회
*/
public function getMenuById(int $id): ?Menu
{
return Menu::with(['parent', 'children'])->find($id);
}
/**
* 메뉴 트리 구조로 조회 (전체)
*/
public function getMenuTree(?int $tenantId = null): Collection
{
$tenantId = $tenantId ?? session('selected_tenant_id');
$query = Menu::query()
->where('is_active', true)
->orderBy('sort_order')
->orderBy('id');
if ($tenantId) {
// 특정 테넌트 선택 시: 해당 테넌트의 메뉴만
$query->where('tenant_id', $tenantId);
} else {
// 전체 선택 시: tenant_id가 NULL인 마스터 메뉴만
$query->whereNull('tenant_id');
}
$allMenus = $query->get();
// 부모 메뉴만 필터링하고 자식 메뉴를 재귀적으로 연결
return $allMenus->where('parent_id', null)->map(function ($menu) use ($allMenus) {
$menu->children = $this->buildChildren($menu, $allMenus);
return $menu;
});
}
/**
* 재귀적으로 자식 메뉴 구성
*/
private function buildChildren(Menu $parent, Collection $allMenus): Collection
{
$children = $allMenus->where('parent_id', $parent->id);
return $children->map(function ($child) use ($allMenus) {
$child->children = $this->buildChildren($child, $allMenus);
return $child;
});
}
/**
* 부모 메뉴 목록 조회 (드롭다운용)
*/
public function getParentMenus(?int $tenantId = null): Collection
{
$tenantId = $tenantId ?? session('selected_tenant_id');
$query = Menu::query()
->where('is_active', true)
->orderBy('sort_order')
->orderBy('name');
if ($tenantId) {
// 특정 테넌트 선택 시: 해당 테넌트의 메뉴만
$query->where('tenant_id', $tenantId);
} else {
// 전체 선택 시: tenant_id가 NULL인 마스터 메뉴만
$query->whereNull('tenant_id');
}
return $query->get();
}
/**
* 메뉴 생성
*/
public function createMenu(array $data): Menu
{
$tenantId = session('selected_tenant_id');
// is_active 처리
$data['is_active'] = isset($data['is_active']) && $data['is_active'] == '1';
// hidden 처리
$data['hidden'] = isset($data['hidden']) && $data['hidden'] == '1';
// is_external 처리
$data['is_external'] = isset($data['is_external']) && $data['is_external'] == '1';
// 생성자 정보
$data['created_by'] = auth()->id();
// 테넌트 정보
if ($tenantId) {
$data['tenant_id'] = $tenantId;
}
// parent_id null 처리
if (empty($data['parent_id'])) {
$data['parent_id'] = null;
}
return Menu::create($data);
}
/**
* 메뉴 수정
*/
public function updateMenu(int $id, array $data): bool
{
$menu = $this->getMenuById($id);
if (! $menu) {
return false;
}
// is_active 처리
$data['is_active'] = isset($data['is_active']) && $data['is_active'] == '1';
// hidden 처리
$data['hidden'] = isset($data['hidden']) && $data['hidden'] == '1';
// is_external 처리
$data['is_external'] = isset($data['is_external']) && $data['is_external'] == '1';
// 수정자 정보
$data['updated_by'] = auth()->id();
// parent_id null 처리
if (empty($data['parent_id'])) {
$data['parent_id'] = null;
}
// 자기 자신을 부모로 설정하는 것 방지
if (isset($data['parent_id']) && $data['parent_id'] == $id) {
return false;
}
return $menu->update($data);
}
/**
* 메뉴 삭제 (Soft Delete)
*/
public function deleteMenu(int $id): bool
{
$menu = $this->getMenuById($id);
if (! $menu) {
return false;
}
// 자식 메뉴가 있는 경우 삭제 불가
if ($menu->children()->count() > 0) {
return false;
}
$menu->deleted_by = auth()->id();
$menu->save();
return $menu->delete();
}
/**
* 메뉴 복원
*/
public function restoreMenu(int $id): bool
{
$menu = Menu::onlyTrashed()->findOrFail($id);
return $menu->restore();
}
/**
* 메뉴 영구 삭제 (슈퍼관리자 전용)
*/
public function forceDeleteMenu(int $id): bool
{
$menu = Menu::withTrashed()->findOrFail($id);
// 자식 메뉴가 있는 경우 영구 삭제 불가
if ($menu->children()->withTrashed()->count() > 0) {
return false;
}
return $menu->forceDelete();
}
/**
* 메뉴 활성 상태 토글
*/
public function toggleActive(int $id): bool
{
$menu = $this->getMenuById($id);
if (! $menu) {
return false;
}
$menu->is_active = ! $menu->is_active;
$menu->updated_by = auth()->id();
return $menu->save();
}
/**
* 메뉴 숨김 상태 토글
*/
public function toggleHidden(int $id): bool
{
$menu = $this->getMenuById($id);
if (! $menu) {
return false;
}
$menu->hidden = ! $menu->hidden;
$menu->updated_by = auth()->id();
return $menu->save();
}
}