2025-08-16 03:25:06 +09:00
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
namespace App\Services;
|
|
|
|
|
|
2025-08-16 04:16:34 +09:00
|
|
|
use App\Models\Commons\Menu;
|
2025-08-16 03:25:06 +09:00
|
|
|
use Illuminate\Support\Arr;
|
2025-08-16 04:16:34 +09:00
|
|
|
use Illuminate\Support\Facades\DB;
|
2025-08-16 03:25:06 +09:00
|
|
|
use Illuminate\Support\Facades\Validator;
|
|
|
|
|
|
|
|
|
|
class MenuService
|
|
|
|
|
{
|
2025-08-16 04:16:34 +09:00
|
|
|
protected static function tenantId(): ?int
|
2025-08-16 03:25:06 +09:00
|
|
|
{
|
2025-08-16 04:16:34 +09:00
|
|
|
return app('tenant_id');
|
2025-08-16 03:25:06 +09:00
|
|
|
}
|
|
|
|
|
|
2025-08-16 04:16:34 +09:00
|
|
|
protected static function actorId(): ?int
|
2025-08-16 03:25:06 +09:00
|
|
|
{
|
2025-08-16 04:16:34 +09:00
|
|
|
$user = app('api_user'); // 컨테이너에 주입된 인증 사용자(객체 or 배열)
|
|
|
|
|
return is_object($user) ? ($user->id ?? null) : ($user['id'] ?? null);
|
2025-08-16 03:25:06 +09:00
|
|
|
}
|
|
|
|
|
|
2025-08-16 04:16:34 +09:00
|
|
|
/**
|
|
|
|
|
* 메뉴 목록 조회
|
|
|
|
|
*/
|
2025-08-16 03:25:06 +09:00
|
|
|
public static function index(array $params)
|
|
|
|
|
{
|
2025-08-16 04:16:34 +09:00
|
|
|
$tenantId = self::tenantId();
|
2025-08-16 03:25:06 +09:00
|
|
|
|
2025-08-16 04:16:34 +09:00
|
|
|
$q = Menu::query()->withShared($tenantId);
|
|
|
|
|
|
|
|
|
|
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']);
|
2025-08-16 03:25:06 +09:00
|
|
|
|
2025-08-16 04:16:34 +09:00
|
|
|
$q->orderBy('parent_id')->orderBy('sort_order');
|
|
|
|
|
|
|
|
|
|
// Builder 그대로 전달해야 쿼리로그/표준응답 형식 유지
|
2025-08-19 12:41:17 +09:00
|
|
|
return $q->get();
|
2025-08-16 03:25:06 +09:00
|
|
|
}
|
|
|
|
|
|
2025-08-16 04:16:34 +09:00
|
|
|
/**
|
|
|
|
|
* 메뉴 단건 조회
|
|
|
|
|
*/
|
2025-08-16 03:25:06 +09:00
|
|
|
public static function show(array $params)
|
|
|
|
|
{
|
2025-08-16 04:16:34 +09:00
|
|
|
$id = (int)($params['id'] ?? 0);
|
|
|
|
|
$tenantId = self::tenantId();
|
|
|
|
|
|
|
|
|
|
if (!$id) {
|
2025-08-19 12:41:17 +09:00
|
|
|
return ['error' => 'id가 필요합니다.', 'code' => 400];
|
2025-08-16 03:25:06 +09:00
|
|
|
}
|
|
|
|
|
|
2025-08-19 12:41:17 +09:00
|
|
|
$res = Menu::withShared($tenantId)->find($id);
|
2025-08-16 04:16:34 +09:00
|
|
|
if (empty($res['data'])) {
|
2025-08-19 12:41:17 +09:00
|
|
|
return ['error' => 'Menu not found', 'code' => 404];
|
2025-08-16 04:16:34 +09:00
|
|
|
}
|
|
|
|
|
return $res;
|
2025-08-16 03:25:06 +09:00
|
|
|
}
|
|
|
|
|
|
2025-08-16 04:16:34 +09:00
|
|
|
/**
|
|
|
|
|
* 메뉴 생성
|
|
|
|
|
*/
|
2025-08-16 03:25:06 +09:00
|
|
|
public static function store(array $params)
|
|
|
|
|
{
|
2025-08-16 04:16:34 +09:00
|
|
|
$tenantId = self::tenantId();
|
|
|
|
|
$userId = self::actorId();
|
2025-08-16 03:25:06 +09:00
|
|
|
|
|
|
|
|
$v = Validator::make($params, [
|
|
|
|
|
'parent_id' => ['nullable','integer'],
|
|
|
|
|
'name' => ['required','string','max:100'],
|
|
|
|
|
'url' => ['nullable','string','max:255'],
|
|
|
|
|
'is_active' => ['nullable','boolean'],
|
|
|
|
|
'sort_order' => ['nullable','integer'],
|
|
|
|
|
'hidden' => ['nullable','boolean'],
|
|
|
|
|
'is_external' => ['nullable','boolean'],
|
|
|
|
|
'external_url' => ['nullable','string','max:255'],
|
|
|
|
|
'icon' => ['nullable','string','max:50'],
|
|
|
|
|
]);
|
|
|
|
|
|
2025-08-16 04:16:34 +09:00
|
|
|
if ($v->fails()) {
|
2025-08-19 12:41:17 +09:00
|
|
|
return ['error' => $v->errors()->first(), 'code' => 422];
|
2025-08-16 03:25:06 +09:00
|
|
|
}
|
2025-08-16 04:16:34 +09:00
|
|
|
$data = $v->validated();
|
2025-08-16 03:25:06 +09:00
|
|
|
|
2025-08-16 04:16:34 +09:00
|
|
|
$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();
|
|
|
|
|
|
|
|
|
|
// 생성 결과를 그대로 전달
|
2025-08-19 12:41:17 +09:00
|
|
|
return $menu->fresh();
|
2025-08-16 03:25:06 +09:00
|
|
|
}
|
|
|
|
|
|
2025-08-16 04:16:34 +09:00
|
|
|
/**
|
|
|
|
|
* 메뉴 수정
|
|
|
|
|
*/
|
2025-08-16 03:25:06 +09:00
|
|
|
public static function update(array $params)
|
|
|
|
|
{
|
|
|
|
|
$id = (int)($params['id'] ?? 0);
|
2025-08-16 04:16:34 +09:00
|
|
|
$tenantId = self::tenantId();
|
|
|
|
|
$userId = self::actorId();
|
|
|
|
|
|
|
|
|
|
if (!$id) {
|
2025-08-19 12:41:17 +09:00
|
|
|
return ['error' => 'id가 필요합니다.', 'code' => 400];
|
2025-08-16 04:16:34 +09:00
|
|
|
}
|
2025-08-16 03:25:06 +09:00
|
|
|
|
|
|
|
|
$v = Validator::make($params, [
|
|
|
|
|
'parent_id' => ['nullable','integer'],
|
|
|
|
|
'name' => ['nullable','string','max:100'],
|
|
|
|
|
'url' => ['nullable','string','max:255'],
|
|
|
|
|
'is_active' => ['nullable','boolean'],
|
|
|
|
|
'sort_order' => ['nullable','integer'],
|
|
|
|
|
'hidden' => ['nullable','boolean'],
|
|
|
|
|
'is_external' => ['nullable','boolean'],
|
|
|
|
|
'external_url' => ['nullable','string','max:255'],
|
|
|
|
|
'icon' => ['nullable','string','max:50'],
|
|
|
|
|
]);
|
2025-08-16 04:16:34 +09:00
|
|
|
|
|
|
|
|
if ($v->fails()) {
|
2025-08-19 12:41:17 +09:00
|
|
|
return ['error' => $v->errors()->first(), 'code' => 422];
|
2025-08-16 04:16:34 +09:00
|
|
|
}
|
2025-08-16 03:25:06 +09:00
|
|
|
$data = $v->validated();
|
|
|
|
|
|
2025-08-16 04:16:34 +09:00
|
|
|
$menu = Menu::withShared($tenantId)->where('id', $id)->first();
|
|
|
|
|
if (!$menu) {
|
2025-08-19 12:41:17 +09:00
|
|
|
return ['error' => 'Menu not found', 'code' => 404];
|
2025-08-16 04:16:34 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$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));
|
|
|
|
|
|
|
|
|
|
if (empty($update)) {
|
2025-08-19 12:41:17 +09:00
|
|
|
return ['error' => '수정할 데이터가 없습니다.', 'code' => 400];
|
2025-08-16 03:25:06 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$update['updated_by'] = $userId;
|
2025-08-16 04:16:34 +09:00
|
|
|
$menu->fill($update)->save();
|
2025-08-16 03:25:06 +09:00
|
|
|
|
2025-08-19 12:41:17 +09:00
|
|
|
return $menu->fresh();
|
2025-08-16 03:25:06 +09:00
|
|
|
}
|
|
|
|
|
|
2025-08-16 04:16:34 +09:00
|
|
|
/**
|
|
|
|
|
* 메뉴 삭제(소프트)
|
|
|
|
|
*/
|
2025-08-16 03:25:06 +09:00
|
|
|
public static function destroy(array $params)
|
|
|
|
|
{
|
|
|
|
|
$id = (int)($params['id'] ?? 0);
|
2025-08-16 04:16:34 +09:00
|
|
|
$tenantId = self::tenantId();
|
|
|
|
|
$userId = self::actorId();
|
|
|
|
|
|
|
|
|
|
if (!$id) {
|
2025-08-19 12:41:17 +09:00
|
|
|
return ['error' => 'id가 필요합니다.', 'code' => 400];
|
2025-08-16 04:16:34 +09:00
|
|
|
}
|
2025-08-16 03:25:06 +09:00
|
|
|
|
2025-08-16 04:16:34 +09:00
|
|
|
$menu = Menu::withShared($tenantId)->where('id', $id)->first();
|
|
|
|
|
if (!$menu) {
|
2025-08-19 12:41:17 +09:00
|
|
|
return ['error' => 'Menu not found', 'code' => 404];
|
2025-08-16 04:16:34 +09:00
|
|
|
}
|
2025-08-16 03:25:06 +09:00
|
|
|
|
2025-08-16 04:16:34 +09:00
|
|
|
$menu->deleted_by = $userId;
|
|
|
|
|
$menu->save();
|
|
|
|
|
$menu->delete();
|
2025-08-16 03:25:06 +09:00
|
|
|
|
2025-08-19 12:41:17 +09:00
|
|
|
return 'success';
|
2025-08-16 03:25:06 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 정렬 일괄 변경
|
|
|
|
|
* $params = [ ['id'=>10, 'sort_order'=>1], ... ]
|
|
|
|
|
*/
|
|
|
|
|
public static function reorder(array $params)
|
|
|
|
|
{
|
|
|
|
|
if (!is_array($params) || empty($params)) {
|
2025-08-19 12:41:17 +09:00
|
|
|
return ['error' => '유효한 정렬 목록이 필요합니다.', 'code' => 422];
|
2025-08-16 03:25:06 +09:00
|
|
|
}
|
2025-08-16 04:16:34 +09:00
|
|
|
$tenantId = self::tenantId();
|
|
|
|
|
|
|
|
|
|
DB::transaction(function () use ($params, $tenantId) {
|
2025-08-16 03:25:06 +09:00
|
|
|
foreach ($params as $it) {
|
|
|
|
|
if (!isset($it['id'], $it['sort_order'])) continue;
|
2025-08-16 04:16:34 +09:00
|
|
|
|
|
|
|
|
$menu = Menu::withShared($tenantId)->find((int)$it['id']);
|
|
|
|
|
if ($menu) {
|
|
|
|
|
$menu->sort_order = (int)$it['sort_order'];
|
|
|
|
|
$menu->save();
|
|
|
|
|
}
|
2025-08-16 03:25:06 +09:00
|
|
|
}
|
|
|
|
|
});
|
2025-08-16 04:16:34 +09:00
|
|
|
|
2025-08-19 12:41:17 +09:00
|
|
|
return 'success';
|
2025-08-16 03:25:06 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2025-08-16 04:16:34 +09:00
|
|
|
* 상태 토글: is_active / hidden / is_external
|
2025-08-16 03:25:06 +09:00
|
|
|
*/
|
|
|
|
|
public static function toggle(array $params)
|
|
|
|
|
{
|
2025-08-16 04:16:34 +09:00
|
|
|
$id = (int)($params['id'] ?? 0);
|
|
|
|
|
$tenantId = self::tenantId();
|
|
|
|
|
$userId = self::actorId();
|
|
|
|
|
|
|
|
|
|
if (!$id) {
|
2025-08-19 12:41:17 +09:00
|
|
|
return ['error' => 'id가 필요합니다.', 'code' => 400];
|
2025-08-16 04:16:34 +09:00
|
|
|
}
|
2025-08-16 03:25:06 +09:00
|
|
|
|
|
|
|
|
$payload = array_filter([
|
2025-08-16 04:16:34 +09:00
|
|
|
'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));
|
2025-08-16 03:25:06 +09:00
|
|
|
|
|
|
|
|
if (empty($payload)) {
|
2025-08-19 12:41:17 +09:00
|
|
|
return ['error' => '변경할 필드가 없습니다.', 'code' => 422];
|
2025-08-16 04:16:34 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$menu = Menu::withShared($tenantId)->find($id);
|
|
|
|
|
if (!$menu) {
|
2025-08-19 12:41:17 +09:00
|
|
|
return ['error' => 'Menu not found', 'code' => 404];
|
2025-08-16 03:25:06 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$payload['updated_by'] = $userId;
|
2025-08-16 04:16:34 +09:00
|
|
|
$menu->fill($payload)->save();
|
2025-08-16 03:25:06 +09:00
|
|
|
|
2025-08-19 12:41:17 +09:00
|
|
|
return $menu->fresh();
|
2025-08-16 03:25:06 +09:00
|
|
|
}
|
|
|
|
|
}
|