where('mt_idx', $mtIdx) ->where('lg_idx', $lgIdx) ->orderBy('mm_num', 'ASC') ->findAll(); } /** * 특정 mb_level에 노출할 메뉴만 필터링 (mm_is_view=Y, mm_level에 해당 레벨 포함 또는 빈값). * lg_idx 기준 해당 지자체 메뉴만 대상. super admin(4)은 mm_level 무관하게 해당 지자체 메뉴 전체 노출. */ public function getVisibleByLevel(int $mtIdx, int $mbLevel, int $lgIdx): array { $all = $this->getAllByType($mtIdx, $lgIdx); if ($mbLevel === \Config\Roles::LEVEL_SUPER_ADMIN) { return array_values(array_filter($all, static fn ($row) => (string) $row->mm_is_view === 'Y')); } $levelStr = (string) $mbLevel; $out = []; foreach ($all as $row) { if ((string) $row->mm_is_view !== 'Y') { continue; } if ($row->mm_level === '' || $row->mm_level === null) { $out[] = $row; continue; } $levels = array_map('trim', explode(',', (string) $row->mm_level)); if (in_array($levelStr, $levels, true)) { $out[] = $row; } } return $out; } public function getItem(int $mmIdx): ?object { return $this->find($mmIdx); } /** * 하위 메뉴 개수 */ public function getChildCount(int $mmIdx): int { return $this->where('mm_pidx', $mmIdx)->countAllResults(); } /** * 순서 변경 (mm_idx 배열 순서대로 mm_num 부여). 해당 지자체 소속 메뉴만 갱신. */ public function setOrder(array $mmIdxList, int $lgIdx): void { foreach ($mmIdxList as $i => $mmIdx) { $row = $this->find((int) $mmIdx); if ($row && (int) $row->lg_idx === $lgIdx) { $this->update((int) $mmIdx, ['mm_num' => $i]); } } } /** * 추가 시 같은 레벨에서 mm_num 결정 (동일 지자체·메뉴종류·부모·깊이 기준) */ public function getNextNum(int $mtIdx, int $lgIdx, int $mmPidx, int $mmDep): int { return $this->where('mt_idx', $mtIdx) ->where('lg_idx', $lgIdx) ->where('mm_pidx', $mmPidx) ->where('mm_dep', $mmDep) ->countAllResults(); } /** * 해당 메뉴가 지정 지자체 소속인지 여부 */ public function belongsToLg(int $mmIdx, int $lgIdx): bool { $row = $this->select('mm_idx')->where('mm_idx', $mmIdx)->where('lg_idx', $lgIdx)->first(); return $row !== null; } /** * 자식 있으면 삭제 불가 */ public function deleteSafe(int $mmIdx): array { $row = $this->find($mmIdx); if (! $row) { return ['ok' => false, 'msg' => '메뉴를 찾을 수 없습니다.']; } $childCount = $this->getChildCount($mmIdx); if ($childCount > 0) { return ['ok' => false, 'msg' => '하위 메뉴가 있으면 삭제할 수 없습니다.']; } $this->delete($mmIdx); if ((int) $row->mm_pidx > 0) { $this->updateCnode((int) $row->mm_pidx, -1); } return ['ok' => true]; } public function updateCnode(int $mmPidx, int $delta): void { $row = $this->find($mmPidx); if (! $row) { return; } $newVal = max(0, (int) $row->mm_cnode + $delta); $this->update($mmPidx, ['mm_cnode' => $newVal]); } /** * 기본 지자체의 메뉴 구조를 다른 지자체로 복사. * mt_idx, srcLg, destLg 조합으로 이미 메뉴가 있으면 아무 작업도 하지 않는다. * * 기본 정책: srcLg(예: 1번 지자체)에 템플릿 메뉴가 있고, * destLg(예: 남구청)에는 아직 메뉴가 없을 때 호출. */ public function copyDefaultsFromLg(int $mtIdx, int $srcLg, int $destLg): void { if ($srcLg === $destLg) { return; } // 이미 대상 지자체에 메뉴가 있으면 복사하지 않음 if ($this->where('mt_idx', $mtIdx)->where('lg_idx', $destLg)->countAllResults() > 0) { return; } // 원본 메뉴(트리 전체) 조회 $source = $this->where('mt_idx', $mtIdx) ->where('lg_idx', $srcLg) ->orderBy('mm_dep', 'ASC') ->orderBy('mm_num', 'ASC') ->findAll(); if (empty($source)) { return; } $idMap = []; foreach ($source as $row) { $oldId = (int) $row->mm_idx; $oldP = (int) $row->mm_pidx; $newPidx = 0; if ($oldP > 0 && isset($idMap[$oldP])) { $newPidx = $idMap[$oldP]; } $data = [ 'mt_idx' => $mtIdx, 'lg_idx' => $destLg, 'mm_name' => $row->mm_name, 'mm_link' => $row->mm_link, 'mm_pidx' => $newPidx, 'mm_dep' => $row->mm_dep, 'mm_num' => $row->mm_num, 'mm_cnode' => $row->mm_cnode, 'mm_level' => $row->mm_level, 'mm_is_view' => $row->mm_is_view, ]; $this->insert($data); $newId = (int) $this->getInsertID(); $idMap[$oldId] = $newId; } } }