parentId ?? null; $group = $request->group ?? 'category'; // 재귀적으로 트리 구성 $list = $this->fetchCategoryTree($parentId, $group); return $list; } /** * 내부 재귀 함수 (하위 카테고리 트리 구조로 구성) */ protected function fetchCategoryTree(?int $parentId = null) { $tenantId = $this->tenantId(); // Base Service에서 상속받은 메서드 $query = Category::query() ->when($tenantId, fn($q) => $q->where('tenant_id', $tenantId)) ->when( is_null($parentId), fn($q) => $q->whereNull('parent_id'), fn($q) => $q->where('parent_id', $parentId) ) ->where('is_active', 1) ->orderBy('sort_order'); $categories = $query->get(); foreach ($categories as $category) { $children = $this->fetchCategoryTree($category->id); $category->setRelation('children', $children); } return $categories; } /** * (예시) 기존의 flat 리스트 조회 */ public static function getCategoryFlat($group = 'category') { $query = CommonCode::where('code_group',$group)->whereNull('parent_id'); return $query->get(); } // 목록/검색 public function index(array $params) { $tenantId = $this->tenantId(); $size = (int)($params['size'] ?? 20); $q = trim((string)($params['q'] ?? '')); $categoryId = $params['category_id'] ?? null; $productType = $params['product_type'] ?? null; // PRODUCT|PART|SUBASSEMBLY... $active = $params['active'] ?? null; // 1/0 $query = Product::query() ->with('category:id,name') // 필요한 컬럼만 가져오기 ->where('tenant_id', $tenantId); if ($q !== '') { $query->where(function ($w) use ($q) { $w->where('name', 'like', "%{$q}%") ->orWhere('code', 'like', "%{$q}%") ->orWhere('description', 'like', "%{$q}%"); }); } if ($categoryId) $query->where('category_id', (int)$categoryId); if ($productType) $query->where('product_type', $productType); if ($active !== null && $active !== '') $query->where('is_active', (int)$active); $paginator = $query->orderBy('id')->paginate($size); // 날짜 형식을 위해 분리 $paginator->setCollection( $paginator->getCollection()->transform(function ($item) { $arr = $item->toArray(); $arr['created_at'] = $item->created_at ? $item->created_at->format('Y-m-d') : null; return $arr; }) ); return $paginator; } // 생성 public function store(array $data) { $tenantId = $this->tenantId(); $userId = $this->apiUserId(); $v = Validator::make($data, [ 'code' => 'required|string|max:30', 'name' => 'required|string|max:100', 'category_id' => 'required|integer', 'product_type' => 'required|string|max:30', 'attributes' => 'nullable|array', 'description' => 'nullable|string|max:255', 'is_sellable' => 'nullable|in:0,1', 'is_purchasable' => 'nullable|in:0,1', 'is_producible' => 'nullable|in:0,1', 'is_active' => 'nullable|in:0,1', ]); $payload = $v->validate(); // tenant별 code 유니크 수동 체크(운영 전 DB 유니크 구성도 권장) $dup = Product::query() ->where('tenant_id', $tenantId) ->where('code', $payload['code']) ->exists(); if ($dup) throw new BadRequestHttpException(__('error.duplicate_key')); $payload['tenant_id'] = $tenantId; $payload['created_by'] = $userId; $payload['is_sellable'] = $payload['is_sellable'] ?? 1; $payload['is_purchasable'] = $payload['is_purchasable'] ?? 0; $payload['is_producible'] = $payload['is_producible'] ?? 1; $payload['is_active'] = $payload['is_active'] ?? 1; // attributes array → json 저장 (Eloquent casts가 array면 그대로 가능) return Product::create($payload); } // 단건 public function show(int $id) { $tenantId = $this->tenantId(); $p = Product::query()->where('tenant_id', $tenantId)->find($id); if (!$p) throw new BadRequestHttpException(__('error.not_found')); return $p; } // 수정 public function update(int $id, array $data) { $tenantId = $this->tenantId(); $userId = $this->apiUserId(); $p = Product::query()->where('tenant_id', $tenantId)->find($id); if (!$p) throw new BadRequestHttpException(__('error.not_found')); $v = Validator::make($data, [ 'code' => 'sometimes|string|max:30', 'name' => 'sometimes|string|max:100', 'category_id' => 'sometimes|integer', 'product_type' => 'sometimes|string|max:30', 'attributes' => 'nullable|array', 'description' => 'nullable|string|max:255', 'is_sellable' => 'nullable|in:0,1', 'is_purchasable' => 'nullable|in:0,1', 'is_producible' => 'nullable|in:0,1', 'is_active' => 'nullable|in:0,1', ]); $payload = $v->validate(); if (isset($payload['code']) && $payload['code'] !== $p->code) { $dup = Product::query() ->where('tenant_id', $tenantId) ->where('code', $payload['code']) ->exists(); if ($dup) throw new BadRequestHttpException(__('error.duplicate_key')); } $payload['updated_by'] = $userId; $p->update($payload); return $p->refresh(); } // 삭제(soft) public function destroy(int $id): void { $tenantId = $this->tenantId(); $p = Product::query()->where('tenant_id', $tenantId)->find($id); if (!$p) throw new BadRequestHttpException(__('error.not_found')); $p->delete(); } // 간편 검색(모달/드롭다운) public function search(array $params) { $tenantId = $this->tenantId(); $q = trim((string)($params['q'] ?? '')); $lim = (int)($params['limit'] ?? 20); $qr = Product::query()->where('tenant_id', $tenantId); if ($q !== '') { $qr->where(function ($w) use ($q) { $w->where('name', 'like', "%{$q}%") ->orWhere('code', 'like', "%{$q}%"); }); } return $qr->orderBy('name')->limit($lim)->get(['id','code','name','product_type','category_id','is_active']); } // 활성 토글 public function toggle(int $id) { $tenantId = $this->tenantId(); $userId = $this->apiUserId(); $p = Product::query()->where('tenant_id', $tenantId)->find($id); if (!$p) throw new BadRequestHttpException(__('error.not_found')); $p->is_active = $p->is_active ? 0 : 1; $p->updated_by = $userId; $p->save(); return ['id' => $p->id, 'is_active' => (int)$p->is_active]; } }