request->getVar(); return $formDatas; } protected function create_form_result_process(string $action): string|RedirectResponse { return $this->action_render_process($action, $this->getViewDatas(), $this->request->getVar('ActionTemplate')); } public function create_form(): string|RedirectResponse { try { $action = __FUNCTION__; $formDatas = $this->create_form_process(); $this->action_init_process($action, $formDatas); $this->addViewDatas('formDatas', $formDatas); return $this->create_form_result_process($action); } catch (\Throwable $e) { return $this->action_redirect_process('error', static::class . '->' . __FUNCTION__ . "에서 {$this->getTitle()} 생성폼 오류:" . $e->getMessage()); } } protected function create_process(array $formDatas): CommonEntity { // POST 데이터를 DTO 객체로 변환 $dto = $this->service->createDTO($formDatas); //DTO 타입 체크 로직을 일반화 $dtoClass = $this->service->getDTOClass(); if (!$dto instanceof $dtoClass) { throw new RuntimeException(static::class . '->' . __FUNCTION__ . "에서 오류발생: " . get_class($dto) . "는 사용할 수 없습니다. ({$dtoClass} 필요)"); } // 💡 여기서 service->create() 내부에서 CommonForm::validate()가 실행되고 // 실패 시 FormValidationException이 throw 된다고 가정(권장) return $this->service->create($dto->toArray()); } protected function create_result_process(CommonEntity $entity, ?string $redirect_url = null): string|RedirectResponse { return $this->action_redirect_process( 'info', "{$this->getTitle()}에서 {$entity->getTitle()} 생성이 완료되었습니다.", $redirect_url ?? '/' . implode('/', [...$this->getActionPaths(), 'view']) . '/' . $entity->getPK() ); } final public function create(): string|RedirectResponse|ResponseInterface { try { $action = __FUNCTION__; $this->action_init_process($action); $entity = $this->create_process($this->request->getPost()); // 💡 동적으로 가져온 Entity 클래스 이름으로 instanceof 검사 $entityClass = $this->service->getEntityClass(); if (!$entity instanceof $entityClass) { throw new RuntimeException(static::class . '->' . __FUNCTION__ . "에서 오류발생:Return Type은 {$entityClass}만 가능"); } // ✅ AJAX 요청이면 JSON 성공 응답 if ($this->request->isAJAX()) { return $this->response->setJSON([ 'ok' => true, 'message' => "{$this->getTitle()}에서 {$entity->getTitle()} 생성이 완료되었습니다.", 'id' => $entity->getPK(), ]); } return $this->create_result_process($entity); } catch (FormValidationException $e) { // ✅ AJAX 요청이면 422 + 필드별 오류 if ($this->request->isAJAX()) { return $this->response ->setStatusCode(422) ->setJSON([ 'ok' => false, 'errors' => $e->errors, ]); } // 기존 redirect 방식 유지 return $this->action_redirect_process( 'error', static::class . '->' . __FUNCTION__ . "에서 {$this->getTitle()} 생성 오류:\n" . implode("\n", $e->errors) ); } catch (\Throwable $e) { log_message('error', 'EXCEPTION_CLASS=' . get_class($e)); // ✅ AJAX면 500 JSON if ($this->request->isAJAX()) { return $this->response ->setStatusCode(500) ->setJSON([ 'ok' => false, 'message' => static::class . '->' . __FUNCTION__ . "에서 오류:" . $e->getMessage(), ]); } return $this->action_redirect_process('error', static::class . '->' . __FUNCTION__ . "에서 {$this->getTitle()} 생성 오류:" . $e->getMessage()); } } // --- 수정 (Modify) --- protected function modify_form_process($uid): CommonEntity { return $this->service->getEntity($uid); } protected function modify_form_result_process(string $action): string|RedirectResponse { return $this->action_render_process($action, $this->getViewDatas(), $this->request->getVar('ActionTemplate')); } final public function modify_form($uid): string|RedirectResponse { try { if (!$uid) { throw new RuntimeException(static::class . '->' . __FUNCTION__ . "에서 {$this->getTitle()}에 번호가 정의 되지 않았습니다."); } $entity = $this->modify_form_process($uid); $this->addViewDatas('entity', $entity); $action = __FUNCTION__; $this->action_init_process($action, $entity->toArray()); return $this->modify_form_result_process($action); } catch (\Throwable $e) { return $this->action_redirect_process('error', static::class . '->' . __FUNCTION__ . "에서 {$this->getTitle()} 수정폼 오류:" . $e->getMessage()); } } protected function modify_process($uid, array $formDatas): CommonEntity { $formDatas[$this->service->getPKField()] = $uid; $dto = $this->service->createDTO($formDatas); //DTO 타입 체크 로직을 일반화 $dtoClass = $this->service->getDTOClass(); if (!$dto instanceof $dtoClass) { throw new RuntimeException(static::class . '->' . __FUNCTION__ . "에서 오류발생: " . get_class($dto) . "는 사용할 수 없습니다. ({$dtoClass} 필요)"); } return $this->service->modify($uid, $dto->toArray()); } final public function modify($uid): string|RedirectResponse|ResponseInterface { try { if (!$uid) { throw new RuntimeException(static::class . '->' . __FUNCTION__ . "에서 {$this->getTitle()}에 번호가 정의 되지 않았습니다."); } $action = __FUNCTION__; $this->action_init_process($action); $entity = $this->modify_process($uid, $this->request->getPost()); // 💡 동적으로 가져온 Entity 클래스 이름으로 instanceof 검사 $entityClass = $this->service->getEntityClass(); if (!$entity instanceof $entityClass) { throw new RuntimeException(static::class . '->' . __FUNCTION__ . "에서 오류발생:Return Type은 {$entityClass}만 가능"); } $this->addViewDatas('entity', $entity); // ✅ AJAX 요청이면 JSON 성공 응답 if ($this->request->isAJAX()) { return $this->response->setJSON([ 'ok' => true, 'message' => "{$this->getTitle()}에서 {$entity->getTitle()} 수정이 완료되었습니다.", 'id' => $entity->getPK(), ]); } return $this->modify_result_process($entity); } catch (FormValidationException $e) { // ✅ AJAX 요청이면 422 + 필드별 오류 if ($this->request->isAJAX()) { return $this->response ->setStatusCode(422) ->setJSON([ 'ok' => false, 'errors' => $e->errors, ]); } // 기존 redirect 방식 유지 return $this->action_redirect_process( 'error', static::class . '->' . __FUNCTION__ . "에서 {$this->getTitle()} 수정 오류:\n" . implode("\n", $e->errors) ); } catch (\Throwable $e) { log_message('error', 'EXCEPTION_CLASS=' . get_class($e)); // ✅ AJAX면 500 JSON if ($this->request->isAJAX()) { return $this->response ->setStatusCode(500) ->setJSON([ 'ok' => false, 'message' => static::class . '->' . __FUNCTION__ . "에서 오류:" . $e->getMessage(), ]); } return $this->action_redirect_process( 'error', static::class . '->' . __FUNCTION__ . "에서 {$this->getTitle()} 수정 오류:" . $e->getMessage() ); } } protected function modify_result_process(CommonEntity $entity, ?string $redirect_url = null): string|RedirectResponse { return $this->action_redirect_process( 'info', "{$this->getTitle()}에서 {$entity->getTitle()} 수정이 완료되었습니다.", $redirect_url ?? '/' . implode('/', [...$this->getActionPaths(), 'view']) . '/' . $entity->getPK() ); } // --- 삭제 (Delete) --- protected function delete_process($uid): CommonEntity { return $this->service->delete($uid); } protected function delete_result_process($entity, ?string $redirect_url = null): string|RedirectResponse { return $this->action_redirect_process('info', "{$this->getTitle()}에서 {$entity->getTitle()} 삭제가 완료되었습니다.", $redirect_url); } final public function delete($uid): RedirectResponse { try { if (!$uid) { throw new RuntimeException(static::class . '->' . __FUNCTION__ . "에서 {$this->getTitle()}에 번호가 정의 되지 않았습니다."); } $entity = $this->service->getEntity($uid); $entity = $this->delete_process($uid); return $this->delete_result_process($entity); } catch (\Throwable $e) { return $this->action_redirect_process('error', static::class . '->' . __FUNCTION__ . "에서 {$this->getTitle()} 삭제 오류:" . $e->getMessage()); } } // --- 상세보기 (View) --- protected function view_process($uid): CommonEntity { return $this->service->getEntity($uid); } protected function view_result_process(string $action): string { return $this->action_render_process($action, $this->getViewDatas(), $this->request->getVar('ActionTemplate')); } final public function view($uid): string|RedirectResponse { try { if (!$uid) { throw new RuntimeException(static::class . '->' . __FUNCTION__ . "에서 {$this->getTitle()}에 번호가 정의 되지 않았습니다."); } $entity = $this->view_process($uid); $action = __FUNCTION__; $this->action_init_process($action, $entity->toArray()); $this->addViewDatas('entity', $entity); return $this->view_result_process($action); } catch (\Throwable $e) { return $this->action_redirect_process('error', static::class . '->' . __FUNCTION__ . "에서 {$this->getTitle()} 상세보기 오류:" . $e->getMessage()); } } }