diff --git a/app/Services/CollectorService.php b/app/Services/CollectorService.php index 4f77dde..80a027a 100644 --- a/app/Services/CollectorService.php +++ b/app/Services/CollectorService.php @@ -62,21 +62,6 @@ class CollectorService extends CommonService } return parent::create($dto); } - protected function modify_process($uid, array $formDatas): CollectorEntity - { - if (!$uid) { - throw new \Exception("수집 번호가 정의 되지 않았습니다."); - } - $entity = $this->getEntity($uid); - if (!$entity instanceof CollectorEntity) { - throw new \Exception("{$uid}에 해당하는 수집정보을 찾을수 없습니다."); - } - // 변경 사항을 Entity에 적용합니다. (Dirty Tracking 활성화) - $formDatas[$this->model->getPKField()] = $uid; - $entity->fill($formDatas); - // 💡 부모 호출 제거: 변경된 Entity 객체를 반환합니다. - return $entity; - } public function modify($uid, object $dto): CollectorEntity { if (!$dto instanceof CollectorDTO) { diff --git a/app/Services/CommonService.php b/app/Services/CommonService.php index dff27c1..fc1f25b 100644 --- a/app/Services/CommonService.php +++ b/app/Services/CommonService.php @@ -4,8 +4,8 @@ namespace App\Services; use App\Entities\CommonEntity; use App\Models\CommonModel; +use CodeIgniter\Database\Exceptions\DatabaseException; use CodeIgniter\Validation\Exceptions\ValidationException; -use PhpParser\Node\Scalar\MagicConst\Dir; use RuntimeException; abstract class CommonService @@ -25,6 +25,7 @@ abstract class CommonService { return $isArray ? $this->_classPaths : implode($delimeter, $this->_classPaths); } + final public function getEntity(string|int|array $where, ?string $message = null): mixed { try { @@ -36,14 +37,22 @@ abstract class CommonService throw new \Exception(__METHOD__ . "에서 결과값 Array 오류발생:\n" . var_export($entity, true)); } return $this->getEntity_process($entity); - } catch (\Exception $e) { - $message = sprintf( - "\n------%s SQL오류-----
\n%s\n%s\n------------------------------\n", + } catch (DatabaseException $e) { // 💡 DB 오류를 명시적으로 잡음 + $errorMessage = sprintf( + "\n------DB Query 오류 (%s)-----\nQuery: %s\nError: %s\n------------------------------\n", __FUNCTION__, - $this->model->getLastQuery(), + $this->model->getLastQuery() ?? "No Query Available", $e->getMessage() ); - throw new \Exception($message); + log_message('error', $errorMessage); + throw new RuntimeException($errorMessage, $e->getCode(), $e); + } catch (\Exception $e) { // 기타 일반적인 예외 처리 + $errorMessage = sprintf( + "\n------일반 오류 (%s)-----\nError: %s\n------------------------------\n", + __FUNCTION__, + $e->getMessage() + ); + throw new \Exception($errorMessage, $e->getCode(), $e); } } final public function getEntities(mixed $where = null, array $columns = ['*']): array @@ -52,26 +61,40 @@ abstract class CommonService $entities = $this->getEntities_process($where, $columns); // echo static::class . DIRECTORY_SEPARATOR . __FUNCTION__ . " Query:" . $this->model->getLastQuery(); return $entities; - } catch (\Exception $e) { - $message = sprintf( - "\n------%s SQL오류-----
\n%s\n%s\n------------------------------\n", + } catch (DatabaseException $e) { // 💡 DB 오류를 명시적으로 잡음 + $errorMessage = sprintf( + "\n------DB Query 오류 (%s)-----\nQuery: %s\nError: %s\n------------------------------\n", __FUNCTION__, - $this->model->getLastQuery(), + $this->model->getLastQuery() ?? "No Query Available", $e->getMessage() ); - throw new \Exception($message); + log_message('error', $errorMessage); + throw new RuntimeException($errorMessage, $e->getCode(), $e); + } catch (\Exception $e) { // 기타 일반적인 예외 처리 + $errorMessage = sprintf( + "\n------일반 오류 (%s)-----\nError: %s\n------------------------------\n", + __FUNCTION__, + $e->getMessage() + ); + throw new \Exception($errorMessage, $e->getCode(), $e); } } // + final public function getLatestPK(): int { - $row = $this->model->selectMax($this->model->getPKField())->get()->getRow(); - return isset($row->uid) ? ((int)$row->uid + 1) : 1; + $pkField = $this->model->getPKField(); + // $this->model->selectMax($pkField)->get()->getRow() 대신 row() 사용 권장 + $row = $this->model->selectMax($pkField)->get()->getRow(); + // uid 대신 PK 필드명을 동적으로 사용 + return isset($row->{$pkField}) ? ((int)$row->{$pkField} + 1) : 1; } + //Entity관련 protected function getEntity_process(mixed $entity): mixed { return $entity; } + //entities를 가져오는 조건 protected function getEntities_process(mixed $where = null, array $columns = ['*']): array { @@ -81,35 +104,43 @@ abstract class CommonService //출력순서 정의 $this->setOrderBy(); $entities = []; + + // findAll()이 DB 오류 없이 실행되었다면 문제 없음 foreach ($this->model->select(implode(',', $columns))->findAll() as $entity) { $entities[$entity->getPK()] = $this->getEntity_process($entity); } return $entities; } + //CURD 결과처리용 //DB 결과 처리 로직 통합 및 개선 - protected function handle_save_result(mixed $result, CommonEntity $entity): CommonEntity + protected function handle_save_result(mixed $result, ?CommonEntity $entity = null): CommonEntity { - if (!$result) { - throw new RuntimeException(static::class . "에서 " . __FUNCTION__ . "오류발생:" . $this->model->getLastQuery()); - } - // 2. 최종 PK 값 결정 (insert/update 공통) + //최종 PK 값 결정 (insert/update 공통) $pk = $this->model->useAutoIncrement() && is_numeric($result) && (int)$result > 0 ? (int)$result - : $entity->{$this->model->primaryKey}; - + : ($entity->{$this->model->primaryKey} ?? null); // PK가 없는 경우 null 처리 if (empty($pk)) { - throw new RuntimeException("{$entity->getTitle()} 저장 후 Primary Key를 확인할 수 없습니다."); + // 모델의 insert/update가 실패했을 경우 에러 메시지를 포함하여 RuntimeException을 던집니다. + $errors = $this->model->errors(); + $errorMsg = is_array($errors) && !empty($errors) ? implode(", ", $errors) : "DB 작업 성공 후 PK를 확인할 수 없거나 모델 오류 발생."; + throw new RuntimeException(__METHOD__ . "에서 오류발생: " . $errorMsg); } - // 3. Entity 재조회 (수정 및 생성 모두 최신 DB 상태 반영) - $savedEntity = $this->model->find($pk); - if (!$savedEntity) { - throw new RuntimeException("등록/수정된 데이터를 찾을 수 없습니다. (PK: {$pk})"); - } - return $savedEntity; + return $this->getEntity($pk); } + //생성용 - abstract protected function create_process(array $formDatas): CommonEntity; + protected function create_process(array $formDatas): CommonEntity + { + $result = $this->model->insert($formDatas, $this->model->useAutoIncrement()); + if ($result === false) { + $errors = $this->model->errors(); + $errorMsg = is_array($errors) ? implode(", ", $errors) : "삽입 작업이 실패했습니다."; + throw new RuntimeException(__METHOD__ . "에서 오류발생: " . $errorMsg); + } + return $this->handle_save_result($result); + } + public function create(object $dto): CommonEntity { $formDatas = (array)$dto; @@ -117,12 +148,28 @@ abstract class CommonService if (!$this->getFormService()->validate($formDatas)) { throw new ValidationException(implode("\n", service('validation')->getErrors())); } - $entity = $this->create_process($formDatas); - $result = $this->model->insert($entity, $this->model->useAutoIncrement()); + return $this->create_process($formDatas); + } + + //수정용 + protected function modify_process($uid, array $formDatas): CommonEntity + { + if (!$uid) { + throw new \Exception("수정에 필요한 PrimaryKey 가 정의 되지 않았습니다."); + } + $entity = $this->getEntity($uid); + if (!$entity instanceof CommonEntity) { + throw new \Exception(__METHOD__ . "에서 오류발생: {$uid}에 해당하는 정보을 찾을수 없습니다."); + } + $result = $this->model->update($uid, $formDatas); + if ($result === false) { + $errors = $this->model->errors(); + $errorMsg = is_array($errors) ? implode(", ", $errors) : "업데이트 작업이 실패했습니다."; + throw new RuntimeException(__METHOD__ . "에서 오류발생: " . $errorMsg); + } return $this->handle_save_result($result, $entity); } - //수정용 - abstract protected function modify_process($uid, array $formDatas): CommonEntity; + public function modify($uid, object $dto): CommonEntity { $formDatas = (array)$dto; @@ -130,17 +177,28 @@ abstract class CommonService if (!$this->getFormService()->validate($formDatas)) { throw new ValidationException(implode("\n", service('validation')->getErrors())); } - $updatedEntity = $this->modify_process($uid, $formDatas); - $result = $this->model->save($updatedEntity); - if (!$result) { - throw new RuntimeException(static::class . "에서 " . __FUNCTION__ . "오류발생:" . $this->model->getLastQuery()); - } - return $this->handle_save_result($result, $updatedEntity); + return $this->modify_process($uid, $formDatas); } + protected function delete_process($uid): bool { - return $this->model->delete($uid); + if (!$uid) { + throw new \Exception("삭제에 필요한 PrimaryKey 가 정의 되지 않았습니다."); + } + $entity = $this->getEntity($uid); + if (!$entity instanceof CommonEntity) { + throw new \Exception(__METHOD__ . "에서 오류발생: {$uid}에 해당하는 정보을 찾을수 없습니다."); + } + $result = $this->model->delete($uid); + if ($result === false) { + $errors = $this->model->errors(); + $errorMsg = is_array($errors) ? implode(", ", $errors) : "모델 삭제 작업이 실패했습니다."; + throw new RuntimeException(__METHOD__ . "에서 오류발생: " . $errorMsg); + } + + return $result; } + final public function delete($uid): bool { return $this->delete_process($uid); @@ -183,92 +241,7 @@ abstract class CommonService public function setOrderBy(mixed $field = null, mixed $value = null): void { if ($field !== null && $value !== null) { - $this->model->orderBy(sprintf("%s.%s %s", $this->model->getTable(), $field, $value)); + $this->model->orderBy(sprintf(" %s.%s %s", $this->model->getTable(), $field, $value)); } } - - - // //단일작업 - // protected function toggle_process(mixed $entity, array $formDatas): mixed - // { - // return $this->model->modify($entity, $formDatas); - // } - // public function toggle(mixed $entity, array $formDatas): mixed - // { - // $db = \Config\Database::connect(); - // try { - // //트랜잭션 도중 DB 오류가 발생하면 DatabaseException을 던지도록 설정 - // $db->transException(true)->transStart(); - // $entity = $this->toggle_process($entity, $formDatas); - // $db->transComplete(); - // return $entity; - // } catch (DatabaseException $e) { //DB 오류시 발생 - // throw new RuntimeException(sprintf( - // "\n----[%s]에서 트랜잭션 실패: DB 오류----\n%s\n%s\n------------------------------\n", - // __METHOD__, - // $this->model->getLastQuery(), - // $e->getMessage() - // ), $e->getCode(), $e); - // } catch (\Throwable $e) { // 그 외 다른 종류의 예외 처리 - // $db->transRollback(); // 예외 발생 시 수동으로 롤백 - // throw new RuntimeException($e->getMessage(), 0, $e); - // } - // } - // //일괄처리작업 - // protected function batchjob_process(mixed $entity, array $formDatas): mixed - // { - // return $this->model->modify($entity, $formDatas); - // } - // public function batchjob(mixed $entity, array $formDatas): mixed - // { - // $db = \Config\Database::connect(); - // try { - // //트랜잭션 도중 DB 오류가 발생하면 DatabaseException을 던지도록 설정 - // $db->transException(true)->transStart(); - // $entity = $this->batchjob_process($entity, $formDatas); - // $db->transComplete(); - // return $entity; - // } catch (DatabaseException $e) { //DB 오류시 발생 - // throw new RuntimeException(sprintf( - // "\n----[%s]에서 트랜잭션 실패: DB 오류----\n%s\n%s\n------------------------------\n", - // __METHOD__, - // $this->model->getLastQuery(), - // $e->getMessage() - // ), $e->getCode(), $e); - // } catch (\Throwable $e) { // 그 외 다른 종류의 예외 처리 - // $db->transRollback(); // 예외 발생 시 수동으로 롤백 - // throw new RuntimeException($e->getMessage(), 0, $e); - // } - // } - // //삭제 - // protected function delete_process(string $uid): void - // { - // if (!$this->model->delete($uid)) { - // // delete() 메서드 실패 시 모델의 errors()를 통해 상세 정보 확인 - // $errors = $this->model->errors(); - // throw new RuntimeException("모델 삭제 실패: " . var_export($errors, true)); - // } - // } - // public function delete(mixed $entity): mixed - // { - // $db = \Config\Database::connect(); - // $db->transStart(); - // try { - // //트랜잭션 도중 DB 오류가 발생하면 DatabaseException을 던지도록 설정 - // $db->transException(true)->transStart(); - // $this->delete_process($entity->getPK()); - // $db->transComplete(); - // return $entity; - // } catch (DatabaseException $e) { //DB 오류시 발생 - // throw new RuntimeException(sprintf( - // "\n----[%s]에서 트랜잭션 실패: DB 오류----\n%s\n%s\n------------------------------\n", - // __METHOD__, - // $this->model->getLastQuery(), - // $e->getMessage() - // ), $e->getCode(), $e); - // } catch (\Throwable $e) { // 그 외 다른 종류의 예외 처리 - // $db->transRollback(); // 예외 발생 시 수동으로 롤백 - // throw new RuntimeException($e->getMessage(), 0, $e); - // } - // } } diff --git a/app/Services/MylogService.php b/app/Services/MylogService.php index 30bd4c1..d99862b 100644 --- a/app/Services/MylogService.php +++ b/app/Services/MylogService.php @@ -45,11 +45,6 @@ class MylogService extends CommonService return $this->helperInstance; } //기본 기능부분 - protected function create_process(array $formDatas): MylogEntity - { - //MylogEntity를 생성하면 Setter가 자동 호출됩니다. - return new MylogEntity($formDatas); - } public function create(object $dto): MylogEntity { if (!$dto instanceof MylogDTO) { @@ -57,26 +52,11 @@ class MylogService extends CommonService } return parent::create($dto); } - protected function modify_process($uid, array $formDatas): MyLogEntity - { - if (!$uid) { - throw new \Exception("로그 번호가 정의 되지 않았습니다."); - } - $entity = $this->getEntity($uid); - if (!$entity instanceof MyLogEntity) { - throw new \Exception("{$uid}에 해당하는 계정을 찾을수 없습니다."); - } - // 변경 사항을 Entity에 적용합니다. (Dirty Tracking 활성화) - $formDatas[$this->model->getPKField()] = $uid; - $entity->fill($formDatas); - // 💡 부모 호출 제거: 변경된 Entity 객체를 반환합니다. - return $entity; - } public function modify($uid, object $dto): MyLogEntity { if (!$dto instanceof MylogDTO) { throw new RuntimeException(__METHOD__ . "에서 오류발생:" . get_class($dto) . "는 사용할수 없습니다."); } - return parent::create($dto); + return parent::modify($uid, $dto); } } diff --git a/app/Services/TrafficService.php b/app/Services/TrafficService.php index 68db92e..d65f2d7 100644 --- a/app/Services/TrafficService.php +++ b/app/Services/TrafficService.php @@ -45,11 +45,6 @@ class TrafficService extends CommonService return $this->helperInstance; } //기본 기능부분 - protected function create_process(array $formDatas): TrafficEntity - { - //TrafficEntity를 생성하면 Setter가 자동 호출됩니다. - return new TrafficEntity($formDatas); - } public function create(object $dto): TrafficEntity { if (!$dto instanceof TrafficDTO) { @@ -57,21 +52,6 @@ class TrafficService extends CommonService } return parent::create($dto); } - protected function modify_process($uid, array $formDatas): TrafficEntity - { - if (!$uid) { - throw new \Exception("트래픽 번호가 정의 되지 않았습니다."); - } - $entity = $this->getEntity($uid); - if (!$entity instanceof TrafficEntity) { - throw new \Exception("{$uid}에 해당하는 트래픽정보을 찾을수 없습니다."); - } - // 변경 사항을 Entity에 적용합니다. (Dirty Tracking 활성화) - $formDatas[$this->model->getPKField()] = $uid; - $entity->fill($formDatas); - // 💡 부모 호출 제거: 변경된 Entity 객체를 반환합니다. - return $entity; - } public function modify($uid, object $dto): TrafficEntity { if (!$dto instanceof TrafficDTO) { @@ -80,11 +60,4 @@ class TrafficService extends CommonService return parent::modify($uid, $dto); } //List 검색용 - //FormFilter 조건절 처리 - //검색어조건절처리 - public function setSearchWord(string $word): void - { - $this->model->orLike($this->model->getTable() . "." . $this->model->getTitleField(), $word, 'both'); - parent::setSearchWord($word); - } } diff --git a/app/Services/UserService.php b/app/Services/UserService.php index a76f3b3..6746921 100644 --- a/app/Services/UserService.php +++ b/app/Services/UserService.php @@ -47,11 +47,12 @@ class UserService extends CommonService //기본 기능부분 protected function create_process(array $formDatas): UserEntity { + //confirmpassword 필드는 Entity에 필요없으므로 제거 if (isset($formDatas['confirmpassword'])) { unset($formDatas['confirmpassword']); } - //UserEntity를 생성하면 Setter가 자동 호출됩니다. - return new UserEntity($formDatas); + $entity = parent::create_process($formDatas); + return $entity; } public function create(object $dto): UserEntity { @@ -62,20 +63,11 @@ class UserService extends CommonService } protected function modify_process($uid, array $formDatas): UserEntity { - if (!$uid) { - throw new \Exception("계정 번호가 정의 되지 않았습니다."); - } - $entity = $this->getEntity($uid); - if (!$entity instanceof UserEntity) { - throw new \Exception("{$uid}에 해당하는 계정을 찾을수 없습니다."); - } + //confirmpassword 필드는 Entity에 필요없으므로 제거 if (isset($formDatas['confirmpassword'])) { unset($formDatas['confirmpassword']); } - // 변경 사항을 Entity에 적용합니다. (Dirty Tracking 활성화) - $formDatas[$this->model->getPKField()] = $uid; - $entity->fill($formDatas); - // 💡 부모 호출 제거: 변경된 Entity 객체를 반환합니다. + $entity = parent::modify_process($uid, $formDatas); return $entity; } public function modify($uid, object $dto): UserEntity @@ -91,8 +83,9 @@ class UserService extends CommonService { switch ($field) { case 'role': + //FIND_IN_SET()은 MySQL 함수이므로 CodeIgniter가 이를 일반 컬럼명으로 착각하고 escape하게 되면 오류가 발생 + // 따라서 ->where($sql, null, false)로 명시하여 escape를 꺼줘야 정상 작동 $where = "FIND_IN_SET(" . $this->model->escape($filter_value) . ", {$this->model->getTable()}.{$field}) > 0"; - //FIND_IN_SET()은 MySQL 함수이므로 CodeIgniter가 이를 일반 컬럼명으로 착각하고 escape하게 되면 오류가 발생합니다. 따라서 ->where($sql, null, false)로 명시하여 escape를 꺼줘야 정상 작동 $this->model->where($where, null, false); break; default: @@ -104,7 +97,6 @@ class UserService extends CommonService public function setSearchWord(string $word): void { $this->model->orLike($this->model->getTable() . '.id', $word, 'both'); - $this->model->orLike($this->model->getTable() . "." . $this->model->getTitleField(), $word, 'both'); $this->model->orLike($this->model->getTable() . '.email', $word, 'both'); parent::setSearchWord($word); }