FishingFieldController.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. <?php
  2. namespace App\Controllers\Api;
  3. use CodeIgniter\HTTP\ResponseInterface;
  4. class FishingFieldController extends BaseApiController
  5. {
  6. protected $format = 'json';
  7. protected $table = 'fishing_field';
  8. /**
  9. * 낚시분야 목록
  10. * GET /api/field/list
  11. */
  12. public function index()
  13. {
  14. $auth = $this->requireAuth();
  15. if ($auth instanceof ResponseInterface) {
  16. return $auth;
  17. }
  18. try {
  19. $page = (int) ($this->request->getGet('page') ?? 1);
  20. $perPage = (int) ($this->request->getGet('per_page') ?? 10);
  21. if ($page < 1) $page = 1;
  22. if ($perPage < 1) $perPage = 10;
  23. $offset = ($page - 1) * $perPage;
  24. $search = trim((string) $this->request->getGet('search'));
  25. $status = trim((string) $this->request->getGet('status')); // 'Y' or 'N' 필터 (선택)
  26. $db = $this->getDB();
  27. $builder = $db->table($this->table);
  28. // soft delete 제외
  29. $builder->where('deleted_YN', 'N');
  30. if ($search !== '') {
  31. $builder->like('name', $search);
  32. }
  33. if ($status === 'Y' || $status === 'N') {
  34. $builder->where('status_YN', $status);
  35. }
  36. $total = $builder->countAllResults(false);
  37. $items = $builder
  38. ->select('id, name, weight, status_YN, created_at, updated_at')
  39. ->orderBy('id', 'DESC')
  40. ->limit($perPage, $offset)
  41. ->get()
  42. ->getResult();
  43. return $this->respondSuccess([
  44. 'items' => $items,
  45. 'total' => $total,
  46. 'page' => $page,
  47. 'per_page' => $perPage,
  48. 'total_pages' => (int) ceil($total / $perPage),
  49. ]);
  50. } catch (\Exception $e) {
  51. log_message('error', 'FishingFieldController index error: ' . $e->getMessage());
  52. return $this->respondError('목록 조회 중 오류가 발생했습니다: ' . $e->getMessage(), ResponseInterface::HTTP_INTERNAL_SERVER_ERROR);
  53. }
  54. }
  55. /**
  56. * 낚시분야 등록
  57. * POST /api/field
  58. */
  59. public function create()
  60. {
  61. $auth = $this->requireAuth();
  62. if ($auth instanceof ResponseInterface) {
  63. return $auth;
  64. }
  65. try {
  66. // JSON 요청 우선, fallback 으로 form-encoded 도 허용
  67. $payload = $this->request->getJSON(true);
  68. if (!is_array($payload) || empty($payload)) {
  69. $payload = $this->request->getPost() ?? [];
  70. }
  71. $name = trim((string) ($payload['name'] ?? ''));
  72. $weightRaw = trim((string) ($payload['weight'] ?? ''));
  73. // 분야명 검증: 1~30자, 필수
  74. if ($name === '') {
  75. return $this->respondError('분야명을 입력하세요.', ResponseInterface::HTTP_BAD_REQUEST);
  76. }
  77. if (mb_strlen($name) > 30) {
  78. return $this->respondError('분야명은 30자 이내로 입력하세요.', ResponseInterface::HTTP_BAD_REQUEST);
  79. }
  80. // 가중치 검증: 0.0 ~ 1.0, 소수점 1자리
  81. if ($weightRaw === '' || !preg_match('/^(0(\.\d)?|1(\.0)?)$/', $weightRaw)) {
  82. return $this->respondError('가중치는 0.0 ~ 1.0 사이 소수점 1자리로 입력하세요.', ResponseInterface::HTTP_BAD_REQUEST);
  83. }
  84. // 소수점 1자리로 정규화 (예: "1" → "1.0", ".5" → "0.5")
  85. $weight = number_format((float) $weightRaw, 1, '.', '');
  86. $db = $this->getDB();
  87. $builder = $db->table($this->table);
  88. // 중복 검사
  89. $exists = $builder->where('name', $name)->countAllResults();
  90. if ($exists > 0) {
  91. return $this->respondError('이미 등록된 분야명입니다.', ResponseInterface::HTTP_CONFLICT);
  92. }
  93. $now = date('Y-m-d H:i:s');
  94. $insertData = [
  95. 'name' => $name,
  96. 'weight' => $weight,
  97. 'created_at' => $now,
  98. ];
  99. if (!$db->table($this->table)->insert($insertData)) {
  100. return $this->respondError('등록에 실패했습니다.', ResponseInterface::HTTP_INTERNAL_SERVER_ERROR);
  101. }
  102. $newId = $db->insertID();
  103. $row = $db->table($this->table)->where('id', $newId)->get()->getRow();
  104. return $this->respondSuccess($row, '낚시분야가 등록되었습니다.', ResponseInterface::HTTP_CREATED);
  105. } catch (\Exception $e) {
  106. log_message('error', 'FishingFieldController create error: ' . $e->getMessage());
  107. return $this->respondError('등록 중 오류가 발생했습니다: ' . $e->getMessage(), ResponseInterface::HTTP_INTERNAL_SERVER_ERROR);
  108. }
  109. }
  110. /**
  111. * 낚시분야 상세 조회
  112. * GET /api/field/:id
  113. */
  114. public function show($id = null)
  115. {
  116. $auth = $this->requireAuth();
  117. if ($auth instanceof ResponseInterface) {
  118. return $auth;
  119. }
  120. if (empty($id)) {
  121. return $this->respondError('ID가 필요합니다.', ResponseInterface::HTTP_BAD_REQUEST);
  122. }
  123. try {
  124. $row = $this->getDB()->table($this->table)
  125. ->select('id, name, weight, status_YN, created_at, updated_at')
  126. ->where('id', (int) $id)
  127. ->where('deleted_YN', 'N')
  128. ->get()
  129. ->getRow();
  130. if (!$row) {
  131. return $this->respondError('해당 낚시분야를 찾을 수 없습니다.', ResponseInterface::HTTP_NOT_FOUND);
  132. }
  133. return $this->respondSuccess($row);
  134. } catch (\Exception $e) {
  135. log_message('error', 'FishingFieldController show error: ' . $e->getMessage());
  136. return $this->respondError('조회 중 오류가 발생했습니다: ' . $e->getMessage(), ResponseInterface::HTTP_INTERNAL_SERVER_ERROR);
  137. }
  138. }
  139. /**
  140. * 낚시분야 수정
  141. * PUT /api/field/:id
  142. */
  143. public function update($id = null)
  144. {
  145. $auth = $this->requireAuth();
  146. if ($auth instanceof ResponseInterface) {
  147. return $auth;
  148. }
  149. if (empty($id)) {
  150. return $this->respondError('ID가 필요합니다.', ResponseInterface::HTTP_BAD_REQUEST);
  151. }
  152. try {
  153. $payload = $this->request->getJSON(true);
  154. if (!is_array($payload) || empty($payload)) {
  155. $payload = $this->request->getRawInput() ?? [];
  156. }
  157. $name = trim((string) ($payload['name'] ?? ''));
  158. $weightRaw = trim((string) ($payload['weight'] ?? ''));
  159. $status = trim((string) ($payload['status_YN'] ?? 'Y'));
  160. // 분야명 검증
  161. if ($name === '') {
  162. return $this->respondError('분야명을 입력하세요.', ResponseInterface::HTTP_BAD_REQUEST);
  163. }
  164. if (mb_strlen($name) > 30) {
  165. return $this->respondError('분야명은 30자 이내로 입력하세요.', ResponseInterface::HTTP_BAD_REQUEST);
  166. }
  167. // 가중치 검증
  168. if ($weightRaw === '' || !preg_match('/^(0(\.\d)?|1(\.0)?)$/', $weightRaw)) {
  169. return $this->respondError('가중치는 0.0 ~ 1.0 사이 소수점 1자리로 입력하세요.', ResponseInterface::HTTP_BAD_REQUEST);
  170. }
  171. $weight = number_format((float) $weightRaw, 1, '.', '');
  172. // 상태 검증
  173. if ($status !== 'Y' && $status !== 'N') {
  174. return $this->respondError('상태값이 올바르지 않습니다.', ResponseInterface::HTTP_BAD_REQUEST);
  175. }
  176. $db = $this->getDB();
  177. $builder = $db->table($this->table);
  178. // 대상 행 존재 확인
  179. $exists = $builder->where('id', (int) $id)->where('deleted_YN', 'N')->countAllResults(false);
  180. if ($exists === 0) {
  181. return $this->respondError('해당 낚시분야를 찾을 수 없습니다.', ResponseInterface::HTTP_NOT_FOUND);
  182. }
  183. $builder->resetQuery();
  184. // 중복 검사 (자기 자신 제외)
  185. $dupe = $db->table($this->table)
  186. ->where('name', $name)
  187. ->where('id !=', (int) $id)
  188. ->where('deleted_YN', 'N')
  189. ->countAllResults();
  190. if ($dupe > 0) {
  191. return $this->respondError('이미 등록된 분야명입니다.', ResponseInterface::HTTP_CONFLICT);
  192. }
  193. $updateData = [
  194. 'name' => $name,
  195. 'weight' => $weight,
  196. 'status_YN' => $status,
  197. 'updated_at' => date('Y-m-d H:i:s'),
  198. ];
  199. $db->table($this->table)->where('id', (int) $id)->update($updateData);
  200. $row = $db->table($this->table)->where('id', (int) $id)->get()->getRow();
  201. return $this->respondSuccess($row, '낚시분야가 수정되었습니다.');
  202. } catch (\Exception $e) {
  203. log_message('error', 'FishingFieldController update error: ' . $e->getMessage());
  204. return $this->respondError('수정 중 오류가 발생했습니다: ' . $e->getMessage(), ResponseInterface::HTTP_INTERNAL_SERVER_ERROR);
  205. }
  206. }
  207. /**
  208. * 낚시분야 삭제 (soft delete)
  209. * DELETE /api/field/:id
  210. */
  211. public function delete($id = null)
  212. {
  213. $auth = $this->requireAuth();
  214. if ($auth instanceof ResponseInterface) {
  215. return $auth;
  216. }
  217. if (empty($id)) {
  218. return $this->respondError('ID가 필요합니다.', ResponseInterface::HTTP_BAD_REQUEST);
  219. }
  220. try {
  221. $db = $this->getDB();
  222. $exists = $db->table($this->table)
  223. ->where('id', (int) $id)
  224. ->where('deleted_YN', 'N')
  225. ->countAllResults();
  226. if ($exists === 0) {
  227. return $this->respondError('해당 낚시분야를 찾을 수 없습니다.', ResponseInterface::HTTP_NOT_FOUND);
  228. }
  229. $db->table($this->table)
  230. ->where('id', (int) $id)
  231. ->update([
  232. 'deleted_YN' => 'Y',
  233. 'updated_at' => date('Y-m-d H:i:s'),
  234. ]);
  235. return $this->respondSuccess(null, '낚시분야가 삭제되었습니다.');
  236. } catch (\Exception $e) {
  237. log_message('error', 'FishingFieldController delete error: ' . $e->getMessage());
  238. return $this->respondError('삭제 중 오류가 발생했습니다: ' . $e->getMessage(), ResponseInterface::HTTP_INTERNAL_SERVER_ERROR);
  239. }
  240. }
  241. }