Initial project import for team collaboration.
Exclude local docs, MCP, and secrets via gitignore. Made-with: Cursor
This commit is contained in:
192
app/Models/MenuModel.php
Normal file
192
app/Models/MenuModel.php
Normal file
@@ -0,0 +1,192 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use CodeIgniter\Model;
|
||||
|
||||
class MenuModel extends Model
|
||||
{
|
||||
protected $table = 'menu';
|
||||
protected $primaryKey = 'mm_idx';
|
||||
protected $returnType = 'object';
|
||||
protected $useTimestamps = false;
|
||||
protected $allowedFields = [
|
||||
'mt_idx', 'lg_idx', 'mm_name', 'mm_link', 'mm_pidx', 'mm_dep', 'mm_num', 'mm_cnode',
|
||||
'mm_level', 'mm_is_view',
|
||||
];
|
||||
|
||||
/**
|
||||
* 메뉴 종류·지자체별 전체 항목 (정렬: num)
|
||||
*/
|
||||
public function getAllByType(int $mtIdx, int $lgIdx): array
|
||||
{
|
||||
return $this->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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user