requireAuth(); if ($auth instanceof ResponseInterface) { return $auth; } try { $page = (int) ($this->request->getGet('page') ?? 1); $perPage = (int) ($this->request->getGet('per_page') ?? 10); if ($page < 1) $page = 1; if ($perPage < 1) $perPage = 10; $offset = ($page - 1) * $perPage; $search = trim((string) $this->request->getGet('search')); $db = $this->getDB(); $builder = $db->table($this->table); // soft delete 제외 $builder->where('deleted_YN', 'N'); if ($search !== '') { $builder->like('name', $search); } $total = $builder->countAllResults(false); $items = $builder ->select('id, name, created_at, updated_at') ->orderBy('id', 'DESC') ->limit($perPage, $offset) ->get() ->getResult(); return $this->respondSuccess([ 'items' => $items, 'total' => $total, 'page' => $page, 'per_page' => $perPage, 'total_pages' => (int) ceil($total / $perPage), ]); } catch (\Exception $e) { log_message('error', 'FishingAreaController index error: ' . $e->getMessage()); return $this->respondError('목록 조회 중 오류가 발생했습니다: ' . $e->getMessage(), ResponseInterface::HTTP_INTERNAL_SERVER_ERROR); } } /** * 낚시지역 등록 * POST /api/area */ public function create() { $auth = $this->requireAuth(); if ($auth instanceof ResponseInterface) { return $auth; } try { $payload = $this->request->getJSON(true); if (!is_array($payload) || empty($payload)) { $payload = $this->request->getPost() ?? []; } $name = trim((string) ($payload['name'] ?? '')); // 지역명 검증: 1~20자, 필수 if ($name === '') { return $this->respondError('지역명을 입력하세요.', ResponseInterface::HTTP_BAD_REQUEST); } if (mb_strlen($name) > 20) { return $this->respondError('지역명은 20자 이내로 입력하세요.', ResponseInterface::HTTP_BAD_REQUEST); } $db = $this->getDB(); // 중복 검사 (soft delete 제외) $exists = $db->table($this->table) ->where('name', $name) ->where('deleted_YN', 'N') ->countAllResults(); if ($exists > 0) { return $this->respondError('이미 등록된 지역명입니다.', ResponseInterface::HTTP_CONFLICT); } $insertData = [ 'name' => $name, 'created_at' => date('Y-m-d H:i:s'), ]; if (!$db->table($this->table)->insert($insertData)) { return $this->respondError('등록에 실패했습니다.', ResponseInterface::HTTP_INTERNAL_SERVER_ERROR); } $newId = $db->insertID(); $row = $db->table($this->table)->where('id', $newId)->get()->getRow(); return $this->respondSuccess($row, '낚시지역이 등록되었습니다.', ResponseInterface::HTTP_CREATED); } catch (\Exception $e) { log_message('error', 'FishingAreaController create error: ' . $e->getMessage()); return $this->respondError('등록 중 오류가 발생했습니다: ' . $e->getMessage(), ResponseInterface::HTTP_INTERNAL_SERVER_ERROR); } } /** * 낚시지역 상세 조회 * GET /api/area/:id */ public function show($id = null) { $auth = $this->requireAuth(); if ($auth instanceof ResponseInterface) { return $auth; } if (empty($id)) { return $this->respondError('ID가 필요합니다.', ResponseInterface::HTTP_BAD_REQUEST); } try { $row = $this->getDB()->table($this->table) ->select('id, name, created_at, updated_at') ->where('id', (int) $id) ->where('deleted_YN', 'N') ->get() ->getRow(); if (!$row) { return $this->respondError('해당 낚시지역을 찾을 수 없습니다.', ResponseInterface::HTTP_NOT_FOUND); } return $this->respondSuccess($row); } catch (\Exception $e) { log_message('error', 'FishingAreaController show error: ' . $e->getMessage()); return $this->respondError('조회 중 오류가 발생했습니다: ' . $e->getMessage(), ResponseInterface::HTTP_INTERNAL_SERVER_ERROR); } } /** * 낚시지역 수정 * PUT /api/area/:id */ public function update($id = null) { $auth = $this->requireAuth(); if ($auth instanceof ResponseInterface) { return $auth; } if (empty($id)) { return $this->respondError('ID가 필요합니다.', ResponseInterface::HTTP_BAD_REQUEST); } try { $payload = $this->request->getJSON(true); if (!is_array($payload) || empty($payload)) { $payload = $this->request->getRawInput() ?? []; } $name = trim((string) ($payload['name'] ?? '')); // 지역명 검증 if ($name === '') { return $this->respondError('지역명을 입력하세요.', ResponseInterface::HTTP_BAD_REQUEST); } if (mb_strlen($name) > 20) { return $this->respondError('지역명은 20자 이내로 입력하세요.', ResponseInterface::HTTP_BAD_REQUEST); } $db = $this->getDB(); // 대상 행 존재 확인 $exists = $db->table($this->table) ->where('id', (int) $id) ->where('deleted_YN', 'N') ->countAllResults(); if ($exists === 0) { return $this->respondError('해당 낚시지역을 찾을 수 없습니다.', ResponseInterface::HTTP_NOT_FOUND); } // 중복 검사 (자기 자신 제외) $dupe = $db->table($this->table) ->where('name', $name) ->where('id !=', (int) $id) ->where('deleted_YN', 'N') ->countAllResults(); if ($dupe > 0) { return $this->respondError('이미 등록된 지역명입니다.', ResponseInterface::HTTP_CONFLICT); } $updateData = [ 'name' => $name, 'updated_at' => date('Y-m-d H:i:s'), ]; $db->table($this->table)->where('id', (int) $id)->update($updateData); $row = $db->table($this->table)->where('id', (int) $id)->get()->getRow(); return $this->respondSuccess($row, '낚시지역이 수정되었습니다.'); } catch (\Exception $e) { log_message('error', 'FishingAreaController update error: ' . $e->getMessage()); return $this->respondError('수정 중 오류가 발생했습니다: ' . $e->getMessage(), ResponseInterface::HTTP_INTERNAL_SERVER_ERROR); } } /** * 낚시지역 삭제 (soft delete) * DELETE /api/area/:id */ public function delete($id = null) { $auth = $this->requireAuth(); if ($auth instanceof ResponseInterface) { return $auth; } if (empty($id)) { return $this->respondError('ID가 필요합니다.', ResponseInterface::HTTP_BAD_REQUEST); } try { $db = $this->getDB(); $exists = $db->table($this->table) ->where('id', (int) $id) ->where('deleted_YN', 'N') ->countAllResults(); if ($exists === 0) { return $this->respondError('해당 낚시지역을 찾을 수 없습니다.', ResponseInterface::HTTP_NOT_FOUND); } // 해당 지역에 등록된 낚시어선/낚시터가 있으면 삭제 차단 $onboardCount = $db->table('onboard') ->where('area_id', (int) $id)->where('deleted_YN', 'N')->countAllResults(); $fishingCount = $db->table('fishing') ->where('area_id', (int) $id)->where('deleted_YN', 'N')->countAllResults(); if (($onboardCount + $fishingCount) > 0) { return $this->respondError( "해당 지역에 등록된 낚시어선/낚시터가 있어 삭제할 수 없습니다. (낚시어선 {$onboardCount} / 낚시터 {$fishingCount})", ResponseInterface::HTTP_CONFLICT ); } $db->table($this->table) ->where('id', (int) $id) ->update([ 'deleted_YN' => 'Y', 'updated_at' => date('Y-m-d H:i:s'), ]); return $this->respondSuccess(null, '낚시지역이 삭제되었습니다.'); } catch (\Exception $e) { log_message('error', 'FishingAreaController delete error: ' . $e->getMessage()); return $this->respondError('삭제 중 오류가 발생했습니다: ' . $e->getMessage(), ResponseInterface::HTTP_INTERNAL_SERVER_ERROR); } } /** * 해당 지역에 속한 낚시어선 / 낚시터 통합 목록 * GET /api/area/:id/places?limit=8 */ public function places($id = null) { $auth = $this->requireAuth(); if ($auth instanceof ResponseInterface) { return $auth; } if (empty($id)) { return $this->respondError('지역 ID가 필요합니다.', ResponseInterface::HTTP_BAD_REQUEST); } try { $areaId = (int) $id; $limit = (int) ($this->request->getGet('limit') ?? 0); // 지정되면 단순 LIMIT 모드 $page = (int) ($this->request->getGet('page') ?? 1); $perPage = (int) ($this->request->getGet('per_page') ?? 10); if ($page < 1) $page = 1; if ($perPage < 1) $perPage = 10; $offset = ($page - 1) * $perPage; $db = $this->getDB(); // 지역명 $area = $db->table($this->table) ->select('name') ->where('id', $areaId)->where('deleted_YN', 'N') ->get()->getRow(); $areaName = $area ? $area->name : null; // 카운트 $onboardCount = $db->table('onboard') ->where('area_id', $areaId) ->where('deleted_YN', 'N') ->countAllResults(); $fishingCount = $db->table('fishing') ->where('area_id', $areaId) ->where('deleted_YN', 'N') ->countAllResults(); $total = $onboardCount + $fishingCount; // UNION ALL — 통합 정렬 if ($limit > 0) { // 단순 LIMIT 모드 (detail 페이지의 8개 미리보기) if ($limit > 1000) $limit = 1000; $tail = "LIMIT {$limit}"; $totalPages = 1; } else { // 페이지네이션 모드 (전체보기 페이지) $tail = "LIMIT {$perPage} OFFSET {$offset}"; $totalPages = (int) ceil($total / $perPage); } $sql = "(SELECT o.id, 'onboard' AS place_type, o.name, o.address, o.status_YN, o.partnership_YN, ff.name AS field_name, o.created_at FROM onboard o LEFT JOIN fishing_field ff ON ff.id = o.field_id AND ff.deleted_YN = 'N' WHERE o.area_id = ? AND o.deleted_YN = 'N') UNION ALL (SELECT fi.id, 'fishing' AS place_type, fi.name, fi.address, fi.status_YN, fi.partnership_YN, ff2.name AS field_name, fi.created_at FROM fishing fi LEFT JOIN fishing_field ff2 ON ff2.id = fi.field_id AND ff2.deleted_YN = 'N' WHERE fi.area_id = ? AND fi.deleted_YN = 'N') ORDER BY created_at DESC {$tail}"; $items = $db->query($sql, [$areaId, $areaId])->getResult(); return $this->respondSuccess([ 'items' => $items, 'onboard_count' => $onboardCount, 'fishing_count' => $fishingCount, 'area_name' => $areaName, 'total' => $total, 'page' => $page, 'per_page' => $perPage, 'total_pages' => $totalPages, ]); } catch (\Exception $e) { log_message('error', 'FishingAreaController places error: ' . $e->getMessage()); return $this->respondError('조회 중 오류: ' . $e->getMessage(), ResponseInterface::HTTP_INTERNAL_SERVER_ERROR); } } }