diff --git a/app/Controllers/Admin/Menu.php b/app/Controllers/Admin/Menu.php index 0307f64..fc5f125 100644 --- a/app/Controllers/Admin/Menu.php +++ b/app/Controllers/Admin/Menu.php @@ -256,8 +256,31 @@ class Menu extends BaseController return redirect()->to(base_url('admin/select-local-government')) ->with('error', '지자체를 선택하세요.'); } - $ids = $this->request->getPost('mm_idx'); $postMtIdx = (int) $this->request->getPost('mt_idx'); + + // 순서 + 상위(부모) 변경 구조가 함께 전달되면 우선 처리 (하위 메뉴의 상위 이동 지원) + $treeJson = (string) $this->request->getPost('menu_tree'); + $structure = $treeJson !== '' ? json_decode($treeJson, true) : null; + + if (is_array($structure) && $structure !== []) { + $orderedIds = array_values(array_filter(array_map( + static fn ($it): int => (int) ($it['idx'] ?? 0), + $structure + ), static fn (int $v): bool => $v > 0)); + if (empty($orderedIds)) { + return $this->menusRedirect($postMtIdx)->with('error', '순서를 적용할 메뉴가 없습니다.'); + } + $firstRow = $this->menuModel->find($orderedIds[0]); + $this->menuModel->setStructure($structure, $lgIdx); + if ($firstRow && (int) $firstRow->lg_idx === $lgIdx) { + $this->menuModel->pruneInventoryManagementMenus((int) $firstRow->mt_idx, $lgIdx); + $this->menuModel->syncTypeToAllLgs((int) $firstRow->mt_idx, $lgIdx); + } + $mtIdx = $firstRow ? (int) $firstRow->mt_idx : $postMtIdx; + return $this->menusRedirect($mtIdx)->with('success', '순서가 적용되었습니다.'); + } + + $ids = $this->request->getPost('mm_idx'); if (! is_array($ids) || empty($ids)) { return $this->menusRedirect($postMtIdx)->with('error', '순서를 적용할 메뉴가 없습니다.'); } diff --git a/app/Models/MenuModel.php b/app/Models/MenuModel.php index 0631e53..00213ca 100644 --- a/app/Models/MenuModel.php +++ b/app/Models/MenuModel.php @@ -82,6 +82,60 @@ class MenuModel extends Model } } + /** + * 순서 + 상위(부모)·깊이 변경을 함께 반영한다. + * 하위 메뉴를 다른 상위 메뉴로 옮기는 드래그를 지원하기 위한 용도. + * + * @param array $items 화면 표시 순서대로 정렬된 구조 배열 + */ + public function setStructure(array $items, int $lgIdx): void + { + $affectedTypes = []; + $i = 0; + foreach ($items as $it) { + $mmIdx = (int) ($it['idx'] ?? 0); + if ($mmIdx <= 0) { + continue; + } + $row = $this->find($mmIdx); + if (! $row || (int) $row->lg_idx !== $lgIdx) { + continue; + } + $this->update($mmIdx, [ + 'mm_num' => $i, + 'mm_pidx' => max(0, (int) ($it['pidx'] ?? 0)), + 'mm_dep' => max(0, (int) ($it['dep'] ?? 0)), + ]); + $affectedTypes[(int) $row->mt_idx] = true; + $i++; + } + // 상위가 바뀌었을 수 있으므로 종류별로 mm_cnode(자식 수)를 재계산 + foreach (array_keys($affectedTypes) as $mtIdx) { + $this->recountChildNodes((int) $mtIdx, $lgIdx); + } + } + + /** + * 특정 메뉴 종류·지자체의 각 메뉴에 대해 mm_cnode(직속 자식 수)를 실제 값으로 재계산한다. + */ + private function recountChildNodes(int $mtIdx, int $lgIdx): void + { + $rows = $this->where('mt_idx', $mtIdx)->where('lg_idx', $lgIdx)->findAll(); + $childCount = []; + foreach ($rows as $r) { + $pid = (int) $r->mm_pidx; + if ($pid > 0) { + $childCount[$pid] = ($childCount[$pid] ?? 0) + 1; + } + } + foreach ($rows as $r) { + $want = (int) ($childCount[(int) $r->mm_idx] ?? 0); + if ((int) $r->mm_cnode !== $want) { + $this->update((int) $r->mm_idx, ['mm_cnode' => $want]); + } + } + } + /** * 추가 시 같은 레벨에서 mm_num 결정 (동일 지자체·메뉴종류·부모·깊이 기준) */ diff --git a/app/Views/admin/menu/index.php b/app/Views/admin/menu/index.php index 6d12812..010e8e1 100644 --- a/app/Views/admin/menu/index.php +++ b/app/Views/admin/menu/index.php @@ -72,6 +72,7 @@ $adminMenuListResolveHref = static function (string $rawLink) use ($menuListReso