feat: menus 테이블 options JSON 컬럼 추가

- menus 테이블에 options JSON 컬럼 추가 (확장 데이터 저장용)
- Menu 모델에 options 헬퍼 메서드 추가
  - getOption(), setOption()
  - getRouteName(), getSection(), getMenuType()
  - requiresRole(), getBladeComponent(), getCssClass()
  - getMeta(), setMeta()
- JSON 컬럼 기반 스코프 메서드 추가
  - scopeSection(), scopeMenuType(), scopeRequiringRole()
This commit is contained in:
2025-12-16 14:47:47 +09:00
parent ad56e94988
commit 52ba867a3c
2 changed files with 166 additions and 2 deletions

View File

@@ -19,7 +19,7 @@ class Menu extends Model
protected $fillable = [
'tenant_id', 'parent_id', 'global_menu_id', 'name', 'url', 'is_active', 'sort_order',
'hidden', 'is_customized', 'is_external', 'external_url', 'icon',
'hidden', 'is_customized', 'is_external', 'external_url', 'icon', 'options',
'created_by', 'updated_by', 'deleted_by',
];
@@ -35,6 +35,7 @@ class Menu extends Model
'hidden' => 'boolean',
'is_customized' => 'boolean',
'is_external' => 'boolean',
'options' => 'array',
];
/**
@@ -135,6 +136,140 @@ public function scopeRoots($query)
*/
public static function getSyncFields(): array
{
return ['name', 'url', 'icon', 'sort_order', 'is_active', 'hidden', 'is_external', 'external_url'];
return ['name', 'url', 'icon', 'sort_order', 'is_active', 'hidden', 'is_external', 'external_url', 'options'];
}
// ============================================================
// Options JSON 헬퍼 메서드
// ============================================================
/**
* options에서 특정 키 값 조회
*/
public function getOption(string $key, mixed $default = null): mixed
{
return data_get($this->options, $key, $default);
}
/**
* options에 특정 키 값 설정
*/
public function setOption(string $key, mixed $value): static
{
$options = $this->options ?? [];
data_set($options, $key, $value);
$this->options = $options;
return $this;
}
/**
* 라우트명 조회 (mng, api, react 등에서 사용)
*/
public function getRouteName(): ?string
{
return $this->getOption('route_name');
}
/**
* 메뉴 섹션 조회 (main, tools, labs 등)
*/
public function getSection(): string
{
return $this->getOption('section', 'main');
}
/**
* 메뉴 타입 조회 (normal, tool, lab 등)
*/
public function getMenuType(): string
{
return $this->getOption('menu_type', 'normal');
}
/**
* 필요 역할 조회
*/
public function getRequiresRole(): ?string
{
return $this->getOption('requires_role');
}
/**
* 특정 역할이 필요한지 확인
*/
public function requiresRole(?string $role = null): bool
{
$requiredRole = $this->getRequiresRole();
if ($requiredRole === null) {
return false;
}
if ($role === null) {
return true; // 역할이 필요함
}
return $requiredRole === $role;
}
/**
* Blade 컴포넌트명 조회
*/
public function getBladeComponent(): ?string
{
return $this->getOption('blade_component');
}
/**
* CSS 클래스 조회
*/
public function getCssClass(): ?string
{
return $this->getOption('css_class');
}
/**
* meta 데이터 조회 (앱별 커스텀 데이터)
*/
public function getMeta(?string $key = null, mixed $default = null): mixed
{
if ($key === null) {
return $this->getOption('meta', []);
}
return $this->getOption("meta.{$key}", $default);
}
/**
* meta 데이터 설정
*/
public function setMeta(string $key, mixed $value): static
{
return $this->setOption("meta.{$key}", $value);
}
/**
* 특정 섹션 메뉴만 조회
*/
public function scopeSection($query, string $section)
{
return $query->whereJsonContains('options->section', $section);
}
/**
* 특정 메뉴 타입만 조회
*/
public function scopeMenuType($query, string $type)
{
return $query->whereJsonContains('options->menu_type', $type);
}
/**
* 특정 역할이 필요한 메뉴만 조회
*/
public function scopeRequiringRole($query, string $role)
{
return $query->whereJsonContains('options->requires_role', $role);
}
}