Files
sam-manage/app/Models/Commons/Menu.php
hskwon 28b4ec8afd feat: [메뉴] 통합메뉴관리 - 글로벌에서 가져오기 기능 구현
- PULL 방식 메뉴 가져오기 (테넌트가 글로벌에서 선택적으로 가져옴)
- 모드 전환 UI (내 메뉴 / 글로벌에서 가져오기)
- 체크박스 선택으로 다중 메뉴 가져오기 지원
- 가져오기 모드에서 읽기 전용 상태 배지 표시
- hidden input으로 HTMX mode 파라미터 전달 수정
2025-12-02 19:16:23 +09:00

153 lines
3.6 KiB
PHP

<?php
namespace App\Models\Commons;
use App\Models\Scopes\TenantScope;
use App\Traits\BelongsToTenant;
use App\Traits\ModelTrait;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
/**
* @mixin IdeHelperMenu
*/
class Menu extends Model
{
use BelongsToTenant, ModelTrait, SoftDeletes;
protected $fillable = [
'tenant_id', 'parent_id', 'global_menu_id', 'name', 'url', 'is_active', 'sort_order',
'hidden', 'is_external', 'external_url', 'icon', 'is_customized',
'created_by', 'updated_by', 'deleted_by',
];
protected $hidden = [
'created_by',
'updated_by',
'deleted_by',
'deleted_at',
];
protected $casts = [
'is_active' => 'boolean',
'hidden' => 'boolean',
'is_external' => 'boolean',
'is_customized' => 'boolean',
];
/**
* 동기화 비교 대상 필드
*/
public static function getSyncFields(): array
{
return ['name', 'url', 'icon', 'sort_order', 'is_active', 'hidden', 'is_external', 'external_url'];
}
public function parent()
{
return $this->belongsTo(Menu::class, 'parent_id');
}
public function children()
{
return $this->hasMany(Menu::class, 'parent_id');
}
public function tenant()
{
return $this->belongsTo(\App\Models\Tenants\Tenant::class, 'tenant_id');
}
/**
* 원본 글로벌 메뉴 (복제된 메뉴인 경우)
*/
public function globalMenu()
{
return $this->belongsTo(Menu::class, 'global_menu_id');
}
/**
* 이 글로벌 메뉴에서 복제된 테넌트 메뉴들
*/
public function tenantMenus()
{
return $this->hasMany(Menu::class, 'global_menu_id');
}
/**
* 글로벌 메뉴인지 확인
*/
public function isGlobal(): bool
{
return is_null($this->tenant_id);
}
/**
* 글로벌 메뉴에서 복제된 메뉴인지 확인
*/
public function isClonedFromGlobal(): bool
{
return ! is_null($this->global_menu_id);
}
/**
* 테넌트가 커스터마이징 했는지 확인
*/
public function isCustomized(): bool
{
return (bool) $this->is_customized;
}
/**
* 글로벌 메뉴 스코프 (tenant_id가 NULL인 것)
*/
public function scopeGlobal($query)
{
return $query->withoutGlobalScope(TenantScope::class)
->whereNull('tenant_id');
}
/**
* 활성 메뉴 스코프
*/
public function scopeActive($query)
{
return $query->where('is_active', true);
}
/**
* 표시되는 메뉴 스코프 (hidden=false)
*/
public function scopeVisible($query)
{
return $query->where('hidden', false);
}
/**
* 최상위 메뉴 스코프 (parent_id가 NULL인 것)
*/
public function scopeRoots($query)
{
return $query->whereNull('parent_id');
}
/**
* 공유(NULL) + 현재 테넌트 모두 포함해서 조회
* (SoftDeletes 글로벌 스코프는 그대로 유지)
*/
public function scopeWithShared($query, ?int $tenantId = null)
{
$tenantId = $tenantId ?? app('tenant_id');
return $query
->withoutGlobalScope(TenantScope::class)
->where(function ($w) use ($tenantId) {
if (is_null($tenantId)) {
$w->whereNull('tenant_id');
} else {
$w->whereNull('tenant_id')->orWhere('tenant_id', $tenantId);
}
});
}
}