feat: global_menus 테이블 분리 및 모델 구현
- global_menus 테이블 생성 (ID 1번부터 시작) - 기존 menus(tenant_id IS NULL) → global_menus 데이터 이전 - GlobalMenu 모델 생성 - Menu.globalMenu() 관계를 GlobalMenu 모델로 변경
This commit is contained in:
@@ -6,6 +6,8 @@
|
||||
use App\Traits\BelongsToTenant;
|
||||
use App\Traits\ModelTrait;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
/**
|
||||
@@ -16,8 +18,8 @@ class Menu extends Model
|
||||
use BelongsToTenant, ModelTrait, SoftDeletes;
|
||||
|
||||
protected $fillable = [
|
||||
'tenant_id', 'parent_id', 'name', 'url', 'is_active', 'sort_order',
|
||||
'hidden', 'is_external', 'external_url', 'icon',
|
||||
'tenant_id', 'parent_id', 'global_menu_id', 'name', 'url', 'is_active', 'sort_order',
|
||||
'hidden', 'is_customized', 'is_external', 'external_url', 'icon',
|
||||
'created_by', 'updated_by', 'deleted_by',
|
||||
];
|
||||
|
||||
@@ -28,16 +30,53 @@ class Menu extends Model
|
||||
'deleted_at',
|
||||
];
|
||||
|
||||
public function parent()
|
||||
protected $casts = [
|
||||
'is_active' => 'boolean',
|
||||
'hidden' => 'boolean',
|
||||
'is_customized' => 'boolean',
|
||||
'is_external' => 'boolean',
|
||||
];
|
||||
|
||||
/**
|
||||
* 상위 메뉴
|
||||
*/
|
||||
public function parent(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Menu::class, 'parent_id');
|
||||
}
|
||||
|
||||
public function children()
|
||||
/**
|
||||
* 하위 메뉴 목록
|
||||
*/
|
||||
public function children(): HasMany
|
||||
{
|
||||
return $this->hasMany(Menu::class, 'parent_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* 원본 글로벌 메뉴 (테넌트 메뉴인 경우)
|
||||
*/
|
||||
public function globalMenu(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(GlobalMenu::class, 'global_menu_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* 글로벌 메뉴에서 복제된 메뉴인지 확인
|
||||
*/
|
||||
public function isClonedFromGlobal(): bool
|
||||
{
|
||||
return ! is_null($this->global_menu_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 테넌트가 커스터마이징한 메뉴인지 확인
|
||||
*/
|
||||
public function isCustomized(): bool
|
||||
{
|
||||
return $this->is_customized;
|
||||
}
|
||||
|
||||
/**
|
||||
* 공유(NULL) + 현재 테넌트 모두 포함해서 조회
|
||||
* (SoftDeletes 글로벌 스코프는 그대로 유지)
|
||||
@@ -56,4 +95,46 @@ public function scopeWithShared($query, ?int $tenantId = null)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 글로벌 메뉴만 조회
|
||||
*/
|
||||
public function scopeGlobal($query)
|
||||
{
|
||||
return $query
|
||||
->withoutGlobalScope(TenantScope::class)
|
||||
->whereNull('tenant_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* 활성화된 메뉴만 조회
|
||||
*/
|
||||
public function scopeActive($query)
|
||||
{
|
||||
return $query->where('is_active', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 숨겨지지 않은 메뉴만 조회
|
||||
*/
|
||||
public function scopeVisible($query)
|
||||
{
|
||||
return $query->where('hidden', false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 최상위 메뉴만 조회
|
||||
*/
|
||||
public function scopeRoots($query)
|
||||
{
|
||||
return $query->whereNull('parent_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* 동기화 비교를 위한 필드 목록
|
||||
*/
|
||||
public static function getSyncFields(): array
|
||||
{
|
||||
return ['name', 'url', 'icon', 'sort_order', 'is_active', 'hidden', 'is_external', 'external_url'];
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user