fix : 메뉴 모델 및 일부 서비스파일 response 오류 수정
This commit is contained in:
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user