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); } // Note: is_active 필드는 하이브리드 구조로 전환하면서 제거됨 // 필요시 attributes JSON이나 별도 필드로 관리 // 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(); // FormRequest에서 이미 검증됨 $payload = $data; // tenant별 code 유니크 수동 체크 $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'] ?? true; $payload['is_purchasable'] = $payload['is_purchasable'] ?? false; $payload['is_producible'] = $payload['is_producible'] ?? true; 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')); } // FormRequest에서 이미 검증됨 $payload = $data; // code 변경 시 중복 체크 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']); } // Note: toggle 메서드는 is_active 필드 제거로 인해 비활성화됨 // 필요시 attributes JSON이나 별도 필드로 구현 // 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]; // } }