trafficmonitor/app/Services/CommonService.php
2025-11-12 15:31:20 +09:00

250 lines
10 KiB
PHP

<?php
namespace App\Services;
use App\Entities\CommonEntity;
use App\Models\CommonModel;
use CodeIgniter\Database\Exceptions\DatabaseException;
use CodeIgniter\Validation\Exceptions\ValidationException;
use RuntimeException;
abstract class CommonService
{
private array $_classPaths = [];
protected $title = null;
protected $formServiceInstance = null;
protected $helperInstance = null;
protected function __construct(protected CommonModel $model) {}
abstract public function getFormService(): mixed;
abstract public function getHelper(): mixed;
final protected function addClassPaths(string $path): void
{
$this->_classPaths[] = $path;
}
final public function getClassPaths($isArray = true, $delimeter = DIRECTORY_SEPARATOR): array|string
{
return $isArray ? $this->_classPaths : implode($delimeter, $this->_classPaths);
}
final public function getEntity(string|int|array $where, ?string $message = null): mixed
{
try {
$entity = is_array($where) ? $this->model->where($where)->first() : $this->model->find($where);
if (!$entity) {
return null;
}
if (is_array($entity)) {
throw new \Exception(__METHOD__ . "에서 결과값 Array 오류발생:\n" . var_export($entity, true));
}
return $this->getEntity_process($entity);
} catch (DatabaseException $e) { // 💡 DB 오류를 명시적으로 잡음
$errorMessage = sprintf(
"\n------DB Query 오류 (%s)-----\nQuery: %s\nError: %s\n------------------------------\n",
__FUNCTION__,
$this->model->getLastQuery() ?? "No Query Available",
$e->getMessage()
);
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
{
try {
$entities = $this->getEntities_process($where, $columns);
// echo static::class . DIRECTORY_SEPARATOR . __FUNCTION__ . " Query:" . $this->model->getLastQuery();
return $entities;
} catch (DatabaseException $e) { // 💡 DB 오류를 명시적으로 잡음
$errorMessage = sprintf(
"\n------DB Query 오류 (%s)-----\nQuery: %s\nError: %s\n------------------------------\n",
__FUNCTION__,
$this->model->getLastQuery() ?? "No Query Available",
$e->getMessage()
);
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
{
$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
{
if ($where) {
$this->model->where($where);
}
//출력순서 정의
$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, ?int $uid = null): CommonEntity
{
//최종 PK 값 결정 (insert/update 공통)
$pk = $this->model->useAutoIncrement() && is_numeric($result) && (int)$result > 0 ? (int)$result : $uid;
if (empty($pk)) {
// 모델의 insert/update가 실패했을 경우 에러 메시지를 포함하여 RuntimeException을 던집니다.
$errors = $this->model->errors();
$errorMsg = is_array($errors) && !empty($errors) ? implode(", ", $errors) : "DB 작업 성공 후 PK를 확인할 수 없거나 모델 오류 발생.";
throw new RuntimeException(__METHOD__ . "에서 오류발생: " . $errorMsg);
}
$entity = $this->getEntity($pk);
if (!$entity instanceof CommonEntity) {
throw new RuntimeException(__METHOD__ . "에서 오류발생: {$pk}에 해당하는 정보를 찾을수 없습니다.");
}
return $entity;
}
//생성용
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;
//입력값 검증
if (!$this->getFormService()->validate($formDatas)) {
throw new ValidationException(implode("\n", service('validation')->getErrors()));
}
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}에 해당하는 정보을 찾을수 없습니다.");
}
//PrimaryKey 필드는 수정에서 제외
unset($formDatas[$this->model->primaryKey]);
$result = $this->model->update($uid, $formDatas);
if ($result === false) {
$errors = $this->model->errors();
$errorMsg = is_array($errors) ? implode(", ", $errors) : "업데이트 작업이 실패했습니다.";
throw new RuntimeException(__METHOD__ . "에서 오류발생: " . $errorMsg);
}
log_message('debug', $this->model->getLastQuery());
return $this->handle_save_result($result, $uid);
}
public function modify($uid, object $dto): CommonEntity
{
$formDatas = (array)$dto;
//입력값 검증
if (!$this->getFormService()->validate($formDatas)) {
throw new ValidationException(implode("\n", service('validation')->getErrors()));
}
return $this->modify_process($uid, $formDatas);
}
protected function delete_process($uid): CommonEntity
{
if (!$uid) {
throw new \Exception("삭제에 필요한 PrimaryKey 가 정의 되지 않았습니다.");
}
$entity = $this->getEntity($uid);
if (!$entity instanceof CommonEntity) {
throw new \Exception(__METHOD__ . "에서 오류발생: {$uid}에 해당하는 정보을 찾을수 없습니다.");
}
return $entity;
}
final public function delete($uid): CommonEntity
{
$entity = $this->delete_process($uid);
$result = $this->model->delete($entity->getPK());
if ($result === false) {
$errors = $this->model->errors();
$errorMsg = is_array($errors) ? implode(", ", $errors) : "모델 삭제 작업이 실패했습니다.";
throw new RuntimeException(__METHOD__ . "에서 오류발생: " . $errorMsg);
}
return $entity;
}
//Index용
final public function getTotalCount(): int
{
return $this->model->countAllResults();
}
//Limit처리
final public function setLimit(int $perpage): void
{
$this->model->limit($perpage);
}
//Offset처리
final public function setOffset(int $offset): void
{
$this->model->offset($offset);
}
public function setFilter(string $field, mixed $filter_value): void
{
switch ($field) {
default:
$this->model->where("{$this->model->getTable()}.{$field}", $filter_value);
break;
}
}
//검색어조건절처리
public function setSearchWord(string $word): void
{
$this->model->orLike($this->model->getTable() . "." . $this->model->getTitleField(), $word, 'both');
}
//날자검색
public function setDateFilter(string $start, string $end): void
{
$this->model->where(sprintf("%s.created_at >= '%s 00:00:00'", $this->model->getTable(), $start));
$this->model->where(sprintf("%s.created_at <= '%s 23:59:59'", $this->model->getTable(), $end));
}
//OrderBy 처리
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));
}
}
}