fix : 메뉴 모델 및 일부 서비스파일 response 오류 수정

This commit is contained in:
2025-08-16 04:16:34 +09:00
parent 73d06e03b0
commit 6f1842181e
5 changed files with 312 additions and 211 deletions

View File

@@ -2,67 +2,77 @@
namespace App\Services;
use Illuminate\Support\Facades\DB;
use App\Models\Commons\Menu;
use App\Helpers\ApiResponse;
use Illuminate\Support\Arr;
use Illuminate\Validation\ValidationException;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Validator;
class MenuService
{
protected static function tenantId(array $params): ?int
protected static function tenantId(): ?int
{
return $params['tenant_id'] ?? request()->attributes->get('tenant_id');
return app('tenant_id');
}
protected static function actorId(array $params): ?int
protected static function actorId(): ?int
{
return $params['user_id'] ?? (request()->user()->id ?? null);
$user = app('api_user'); // 컨테이너에 주입된 인증 사용자(객체 or 배열)
return is_object($user) ? ($user->id ?? null) : ($user['id'] ?? null);
}
/**
* 메뉴 목록 조회
*/
public static function index(array $params)
{
$tenantId = self::tenantId($params);
$tenantId = self::tenantId();
$q = DB::table('menus')->whereNull('deleted_at');
if (!is_null($tenantId)) {
$q->where(function ($w) use ($tenantId) {
$w->whereNull('tenant_id')->orWhere('tenant_id', $tenantId);
});
}
// 옵션: parent_id / is_active / hidden 필터
if (isset($params['parent_id'])) $q->where('parent_id', $params['parent_id']);
if (isset($params['is_active'])) $q->where('is_active', (int)$params['is_active']);
if (isset($params['hidden'])) $q->where('hidden', (int)$params['hidden']);
$q = Menu::query()->withShared($tenantId);
return $q->orderBy('parent_id')->orderBy('sort_order')->get();
if (array_key_exists('parent_id', $params)) $q->where('parent_id', $params['parent_id']);
if (array_key_exists('is_active', $params)) $q->where('is_active', (int)$params['is_active']);
if (array_key_exists('hidden', $params)) $q->where('hidden', (int)$params['hidden']);
$q->orderBy('parent_id')->orderBy('sort_order');
// Builder 그대로 전달해야 쿼리로그/표준응답 형식 유지
return ApiResponse::response('get', $q);
}
/**
* 메뉴 단건 조회
*/
public static function show(array $params)
{
$id = (int)($params['id'] ?? 0);
$tenantId = self::tenantId($params);
$id = (int)($params['id'] ?? 0);
$tenantId = self::tenantId();
$q = DB::table('menus')->where('id', $id)->whereNull('deleted_at');
if (!is_null($tenantId)) {
$q->where(function ($w) use ($tenantId) {
$w->whereNull('tenant_id')->orWhere('tenant_id', $tenantId);
});
if (!$id) {
return ApiResponse::error('id가 필요합니다.', 400);
}
$row = $q->first();
throw_if(!$row, ValidationException::withMessages(['id' => 'Menu not found']));
return $row;
$q = Menu::withShared($tenantId)->where('id', $id);
// first 쿼리를 ApiResponse에 위임 (존재X면 null 반환)
$res = ApiResponse::response('first', $q);
if (empty($res['data'])) {
return ApiResponse::error('Menu not found', 404);
}
return $res;
}
/**
* 메뉴 생성
*/
public static function store(array $params)
{
$tenantId = self::tenantId($params);
$userId = self::actorId($params);
$tenantId = self::tenantId();
$userId = self::actorId();
$v = Validator::make($params, [
'parent_id' => ['nullable','integer'],
'name' => ['required','string','max:100'],
'slug' => ['nullable','string','max:150'],
'url' => ['nullable','string','max:255'],
'is_active' => ['nullable','boolean'],
'sort_order' => ['nullable','integer'],
@@ -71,52 +81,47 @@ public static function store(array $params)
'external_url' => ['nullable','string','max:255'],
'icon' => ['nullable','string','max:50'],
]);
if ($v->fails()) {
return ApiResponse::error($v->errors()->first(), 422);
}
$data = $v->validated();
// slug 유니크(테넌트 범위) 체크
if (!empty($data['slug'])) {
$exists = DB::table('menus')
->whereNull('deleted_at')
->when(!is_null($tenantId), fn($q)=>$q->where('tenant_id',$tenantId), fn($q)=>$q->whereNull('tenant_id'))
->where('slug',$data['slug'])
->exists();
if ($exists) {
throw ValidationException::withMessages(['slug'=>'이미 사용 중인 슬러그입니다.']);
}
}
$menu = new Menu();
$menu->tenant_id = $tenantId;
$menu->parent_id = $data['parent_id'] ?? null;
$menu->name = $data['name'];
$menu->url = $data['url'] ?? null;
$menu->is_active = (int)($data['is_active'] ?? 1);
$menu->sort_order = (int)($data['sort_order'] ?? 0);
$menu->hidden = (int)($data['hidden'] ?? 0);
$menu->is_external = (int)($data['is_external'] ?? 0);
$menu->external_url = $data['external_url'] ?? null;
$menu->icon = $data['icon'] ?? null;
$menu->created_by = $userId;
$menu->updated_by = $userId;
$menu->save();
$now = now();
$id = DB::table('menus')->insertGetId([
'tenant_id' => $tenantId,
'parent_id' => $data['parent_id'] ?? null,
'name' => $data['name'],
'slug' => $data['slug'] ?? null,
'url' => $data['url'] ?? null,
'is_active' => (int)($data['is_active'] ?? 1),
'sort_order' => (int)($data['sort_order'] ?? 0),
'hidden' => (int)($data['hidden'] ?? 0),
'is_external' => (int)($data['is_external'] ?? 0),
'external_url' => $data['external_url'] ?? null,
'icon' => $data['icon'] ?? null,
'created_at' => $now,
'updated_at' => $now,
'created_by' => $userId,
'updated_by' => $userId,
]);
return ['id' => $id];
// 생성 결과를 그대로 전달
return ApiResponse::response('result', $menu->fresh());
}
/**
* 메뉴 수정
*/
public static function update(array $params)
{
$id = (int)($params['id'] ?? 0);
$tenantId = self::tenantId($params);
$userId = self::actorId($params);
$tenantId = self::tenantId();
$userId = self::actorId();
if (!$id) {
return ApiResponse::error('id가 필요합니다.', 400);
}
$v = Validator::make($params, [
'parent_id' => ['nullable','integer'],
'name' => ['nullable','string','max:100'],
'slug' => ['nullable','string','max:150'],
'url' => ['nullable','string','max:255'],
'is_active' => ['nullable','boolean'],
'sort_order' => ['nullable','integer'],
@@ -125,50 +130,55 @@ public static function update(array $params)
'external_url' => ['nullable','string','max:255'],
'icon' => ['nullable','string','max:50'],
]);
if ($v->fails()) {
return ApiResponse::error($v->errors()->first(), 422);
}
$data = $v->validated();
// 대상 존재 확인 & 테넌트 범위
$exists = DB::table('menus')->where('id',$id)->whereNull('deleted_at')
->when(!is_null($tenantId), fn($q)=>$q->where('tenant_id',$tenantId), fn($q)=>$q->whereNull('tenant_id'))
->exists();
if (!$exists) throw ValidationException::withMessages(['id'=>'Menu not found']);
// slug 유니크(테넌트 범위) 체크
if (!empty($data['slug'])) {
$dup = DB::table('menus')->whereNull('deleted_at')
->when(!is_null($tenantId), fn($q)=>$q->where('tenant_id',$tenantId), fn($q)=>$q->whereNull('tenant_id'))
->where('slug',$data['slug'])->where('id','<>',$id)->exists();
if ($dup) throw ValidationException::withMessages(['slug'=>'이미 사용 중인 슬러그입니다.']);
$menu = Menu::withShared($tenantId)->where('id', $id)->first();
if (!$menu) {
return ApiResponse::error('Menu not found', 404);
}
$update = Arr::only($data, ['parent_id','name','slug','url','is_active','sort_order','hidden','is_external','external_url','icon']);
$update = array_filter($update, fn($v)=>!is_null($v));
$update['updated_at'] = now();
$update['updated_by'] = $userId;
$update = Arr::only($data, [
'parent_id','name','url','is_active','sort_order','hidden','is_external','external_url','icon'
]);
$update = array_filter($update, fn($v) => !is_null($v));
DB::table('menus')->where('id',$id)->update($update);
return ['id' => $id];
if (empty($update)) {
return ApiResponse::error('수정할 데이터가 없습니다.', 400);
}
$update['updated_by'] = $userId;
$menu->fill($update)->save();
return ApiResponse::response('result', $menu->fresh());
}
/**
* 메뉴 삭제(소프트)
*/
public static function destroy(array $params)
{
$id = (int)($params['id'] ?? 0);
$tenantId = self::tenantId($params);
$userId = self::actorId($params);
$tenantId = self::tenantId();
$userId = self::actorId();
$q = DB::table('menus')->where('id',$id)->whereNull('deleted_at');
$q = !is_null($tenantId)
? $q->where('tenant_id',$tenantId)
: $q->whereNull('tenant_id');
if (!$id) {
return ApiResponse::error('id가 필요합니다.', 400);
}
$row = $q->first();
if (!$row) throw ValidationException::withMessages(['id'=>'Menu not found']);
$menu = Menu::withShared($tenantId)->where('id', $id)->first();
if (!$menu) {
return ApiResponse::error('Menu not found', 404);
}
DB::table('menus')->where('id',$id)->update([
'deleted_at' => now(),
'deleted_by' => $userId,
]);
return ['id' => $id, 'deleted' => true];
$menu->deleted_by = $userId;
$menu->save();
$menu->delete();
return ApiResponse::response('success');
}
/**
@@ -178,43 +188,56 @@ public static function destroy(array $params)
public static function reorder(array $params)
{
if (!is_array($params) || empty($params)) {
throw ValidationException::withMessages(['items'=>'유효한 정렬 목록이 필요합니다.']);
return ApiResponse::error('유효한 정렬 목록이 필요합니다.', 422);
}
DB::transaction(function () use ($params) {
$tenantId = self::tenantId();
DB::transaction(function () use ($params, $tenantId) {
foreach ($params as $it) {
if (!isset($it['id'], $it['sort_order'])) continue;
DB::table('menus')->where('id',(int)$it['id'])->update([
'sort_order' => (int)$it['sort_order'],
'updated_at' => now(),
]);
$menu = Menu::withShared($tenantId)->find((int)$it['id']);
if ($menu) {
$menu->sort_order = (int)$it['sort_order'];
$menu->save();
}
}
});
return true;
return ApiResponse::response('success');
}
/**
* 상태 토글
* 허용 필드: is_active / hidden / is_external
* 상태 토글: is_active / hidden / is_external
*/
public static function toggle(array $params)
{
$id = (int)($params['id'] ?? 0);
$userId = self::actorId($params);
$id = (int)($params['id'] ?? 0);
$tenantId = self::tenantId();
$userId = self::actorId();
$payload = array_filter([
'is_active' => isset($params['is_active']) ? (int)$params['is_active'] : null,
'hidden' => isset($params['hidden']) ? (int)$params['hidden'] : null,
'is_external' => isset($params['is_external']) ? (int)$params['is_external'] : null,
], fn($v)=>!is_null($v));
if (empty($payload)) {
throw ValidationException::withMessages(['toggle'=>'변경할 필드가 없습니다.']);
if (!$id) {
return ApiResponse::error('id가 필요합니다.', 400);
}
$payload['updated_at'] = now();
$payload['updated_by'] = $userId;
$payload = array_filter([
'is_active' => array_key_exists('is_active', $params) ? (int)$params['is_active'] : null,
'hidden' => array_key_exists('hidden', $params) ? (int)$params['hidden'] : null,
'is_external' => array_key_exists('is_external', $params) ? (int)$params['is_external'] : null,
], fn($v) => !is_null($v));
DB::table('menus')->where('id',$id)->update($payload);
return ['id' => $id];
if (empty($payload)) {
return ApiResponse::error('변경할 필드가 없습니다.', 422);
}
$menu = Menu::withShared($tenantId)->find($id);
if (!$menu) {
return ApiResponse::error('Menu not found', 404);
}
$payload['updated_by'] = $userId;
$menu->fill($payload)->save();
return ApiResponse::response('result', $menu->fresh());
}
}