daemon-idc init
This commit is contained in:
parent
10fce3bd2e
commit
fcebaa0ae0
@ -25,6 +25,7 @@ class Validation extends BaseConfig
|
||||
FormatRules::class,
|
||||
FileRules::class,
|
||||
CreditCardRules::class,
|
||||
\App\Validation\CustomRules::class
|
||||
];
|
||||
|
||||
/**
|
||||
@ -34,7 +35,7 @@ class Validation extends BaseConfig
|
||||
* @var array<string, string>
|
||||
*/
|
||||
public array $templates = [
|
||||
'list' => 'CodeIgniter\Validation\Views\list',
|
||||
'list' => 'CodeIgniter\Validation\Views\list',
|
||||
'single' => 'CodeIgniter\Validation\Views\single',
|
||||
];
|
||||
|
||||
|
||||
@ -1,12 +1,19 @@
|
||||
<?php
|
||||
// =========================================================
|
||||
// AbstractCRUDController.php (FINAL)
|
||||
// - 모든 "결과/리다이렉트"는 RedirectResponse|ResponseInterface로 엄격 정리
|
||||
// - create/modify/delete/view/create_form/modify_form 모두 runAction 적용
|
||||
// - delete_result_process도 정책에 맞게 RedirectResponse|ResponseInterface로 정리
|
||||
// =========================================================
|
||||
|
||||
namespace App\Controllers;
|
||||
|
||||
use RuntimeException;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use App\Entities\CommonEntity;
|
||||
use CodeIgniter\HTTP\RedirectResponse;
|
||||
use CodeIgniter\HTTP\RequestInterface;
|
||||
use CodeIgniter\HTTP\ResponseInterface;
|
||||
use App\Exceptions\FormValidationException;
|
||||
|
||||
/**
|
||||
* AbstractCRUDController
|
||||
@ -14,259 +21,187 @@ use App\Exceptions\FormValidationException;
|
||||
*/
|
||||
abstract class AbstractCRUDController extends AbstractWebController
|
||||
{
|
||||
// --- 생성 (Create) ---
|
||||
public function initController(RequestInterface $request, ResponseInterface $response, LoggerInterface $logger)
|
||||
{
|
||||
parent::initController($request, $response, $logger);
|
||||
}
|
||||
|
||||
// =========================================================
|
||||
// CRUD 공통 보조
|
||||
// =========================================================
|
||||
|
||||
protected function assertUid($uid, string $action): void
|
||||
{
|
||||
if (!$uid) {
|
||||
throw new RuntimeException(static::class . '->' . $action . "에서 {$this->getTitle()}에 번호가 정의 되지 않았습니다.");
|
||||
}
|
||||
}
|
||||
|
||||
protected function assertEntityClass(CommonEntity $entity, string $action): void
|
||||
{
|
||||
$entityClass = $this->service->getEntityClass();
|
||||
if (!$entity instanceof $entityClass) {
|
||||
throw new RuntimeException(static::class . '->' . $action . "에서 오류발생:Return Type은 {$entityClass}만 가능");
|
||||
}
|
||||
}
|
||||
|
||||
protected function buildViewRedirectUrl(CommonEntity $entity): string
|
||||
{
|
||||
return '/' . implode('/', [...$this->getActionPaths(), 'view']) . '/' . $entity->getPK();
|
||||
}
|
||||
|
||||
protected function buildListRedirectUrl(): string
|
||||
{
|
||||
return implode(DIRECTORY_SEPARATOR, $this->getActionPaths());
|
||||
}
|
||||
|
||||
// =========================================================
|
||||
// Create Form
|
||||
// =========================================================
|
||||
|
||||
protected function create_form_process(array $formDatas = []): array
|
||||
{
|
||||
//초기 기본 Default값 지정
|
||||
$formDatas = $this->request->getVar();
|
||||
return $formDatas;
|
||||
return $this->request->getVar();
|
||||
}
|
||||
|
||||
protected function create_form_result_process(string $action): string|RedirectResponse
|
||||
public function create_form(): string|RedirectResponse|ResponseInterface
|
||||
{
|
||||
return $this->action_render_process($action, $this->getViewDatas(), $this->request->getVar('ActionTemplate'));
|
||||
}
|
||||
$action = __FUNCTION__;
|
||||
|
||||
public function create_form(): string|RedirectResponse
|
||||
{
|
||||
try {
|
||||
$action = __FUNCTION__;
|
||||
return $this->runAction($action, function () use ($action) {
|
||||
$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());
|
||||
}
|
||||
return $this->action_render_process($action, $this->getViewDatas(), $this->request->getVar('ActionTemplate'));
|
||||
});
|
||||
}
|
||||
|
||||
// =========================================================
|
||||
// Create
|
||||
// =========================================================
|
||||
|
||||
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());
|
||||
return $this->service->create($formDatas);
|
||||
}
|
||||
|
||||
protected function create_result_process(CommonEntity $entity, ?string $redirect_url = null): string|RedirectResponse
|
||||
final public function create(): RedirectResponse|ResponseInterface|string
|
||||
{
|
||||
return $this->action_redirect_process(
|
||||
'info',
|
||||
"{$this->getTitle()}에서 {$entity->getTitle()} 생성이 완료되었습니다.",
|
||||
$redirect_url ?? '/' . implode('/', [...$this->getActionPaths(), 'view']) . '/' . $entity->getPK()
|
||||
);
|
||||
}
|
||||
$action = __FUNCTION__;
|
||||
|
||||
final public function create(): string|RedirectResponse|ResponseInterface
|
||||
{
|
||||
try {
|
||||
$action = __FUNCTION__;
|
||||
return $this->runAction($action, function () use ($action) {
|
||||
$this->action_init_process($action);
|
||||
|
||||
$entity = $this->create_process($this->request->getPost());
|
||||
$this->assertEntityClass($entity, $action);
|
||||
|
||||
// 💡 동적으로 가져온 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());
|
||||
}
|
||||
$message = "{$this->getTitle()}에서 {$entity->getTitle()} 생성이 완료되었습니다.";
|
||||
return $this->okResponse($message, ['id' => $entity->getPK()], $this->buildViewRedirectUrl($entity));
|
||||
});
|
||||
}
|
||||
|
||||
// --- 수정 (Modify) ---
|
||||
// =========================================================
|
||||
// Modify Form
|
||||
// =========================================================
|
||||
|
||||
protected function modify_form_process($uid): CommonEntity
|
||||
{
|
||||
return $this->service->getEntity($uid);
|
||||
}
|
||||
|
||||
protected function modify_form_result_process(string $action): string|RedirectResponse
|
||||
protected function modify_form_result_process(string $action): string
|
||||
{
|
||||
return $this->action_render_process($action, $this->getViewDatas(), $this->request->getVar('ActionTemplate'));
|
||||
}
|
||||
|
||||
final public function modify_form($uid): string|RedirectResponse
|
||||
final public function modify_form($uid): string|RedirectResponse|ResponseInterface
|
||||
{
|
||||
try {
|
||||
if (!$uid) {
|
||||
throw new RuntimeException(static::class . '->' . __FUNCTION__ . "에서 {$this->getTitle()}에 번호가 정의 되지 않았습니다.");
|
||||
}
|
||||
$action = __FUNCTION__;
|
||||
|
||||
return $this->runAction($action, function () use ($action, $uid) {
|
||||
$this->assertUid($uid, $action);
|
||||
|
||||
$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());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// =========================================================
|
||||
// Modify
|
||||
// =========================================================
|
||||
|
||||
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());
|
||||
return $this->service->modify($uid, $formDatas);
|
||||
}
|
||||
|
||||
/**
|
||||
* 기본 결과 처리 (필요시 자식에서 override)
|
||||
*/
|
||||
protected function result_process(CommonEntity $entity, string $message): RedirectResponse|ResponseInterface
|
||||
{
|
||||
return $this->okResponse($message, ['id' => $entity->getPK()], $this->buildViewRedirectUrl($entity));
|
||||
}
|
||||
|
||||
final public function modify($uid): string|RedirectResponse|ResponseInterface
|
||||
{
|
||||
try {
|
||||
if (!$uid) {
|
||||
throw new RuntimeException(static::class . '->' . __FUNCTION__ . "에서 {$this->getTitle()}에 번호가 정의 되지 않았습니다.");
|
||||
}
|
||||
$action = __FUNCTION__;
|
||||
|
||||
return $this->runAction($action, function () use ($action, $uid) {
|
||||
$this->assertUid($uid, $action);
|
||||
|
||||
$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->assertEntityClass($entity, $action);
|
||||
|
||||
$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()
|
||||
);
|
||||
}
|
||||
return $this->result_process($entity, "{$this->getTitle()}에서 {$entity->getTitle()} 수정이 완료되었습니다.");
|
||||
});
|
||||
}
|
||||
|
||||
// =========================================================
|
||||
// Delete
|
||||
// =========================================================
|
||||
|
||||
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
|
||||
protected function delete_result_process(CommonEntity $entity, ?string $redirect_url = null): RedirectResponse|ResponseInterface
|
||||
{
|
||||
// AJAX면 action_redirect_process가 JSON으로 자동 변환
|
||||
$redirect_url = $redirect_url ?? $this->buildListRedirectUrl();
|
||||
return $this->action_redirect_process('info', "{$this->getTitle()}에서 {$entity->getTitle()} 삭제가 완료되었습니다.", $redirect_url);
|
||||
}
|
||||
|
||||
final public function delete($uid): RedirectResponse
|
||||
final public function delete($uid): RedirectResponse|ResponseInterface|string
|
||||
{
|
||||
try {
|
||||
if (!$uid) {
|
||||
throw new RuntimeException(static::class . '->' . __FUNCTION__ . "에서 {$this->getTitle()}에 번호가 정의 되지 않았습니다.");
|
||||
}
|
||||
$entity = $this->service->getEntity($uid);
|
||||
$action = __FUNCTION__;
|
||||
|
||||
return $this->runAction($action, function () use ($action, $uid) {
|
||||
$this->assertUid($uid, $action);
|
||||
|
||||
$this->action_init_process($action);
|
||||
|
||||
// 삭제 전 존재 확인(기존 로직 유지)
|
||||
$this->service->getEntity($uid);
|
||||
|
||||
$entity = $this->delete_process($uid);
|
||||
$this->assertEntityClass($entity, $action);
|
||||
|
||||
return $this->delete_result_process($entity);
|
||||
} catch (\Throwable $e) {
|
||||
return $this->action_redirect_process('error', static::class . '->' . __FUNCTION__ . "에서 {$this->getTitle()} 삭제 오류:" . $e->getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// --- 상세보기 (View) ---
|
||||
// =========================================================
|
||||
// View
|
||||
// =========================================================
|
||||
|
||||
protected function view_process($uid): CommonEntity
|
||||
{
|
||||
return $this->service->getEntity($uid);
|
||||
@ -277,19 +212,18 @@ abstract class AbstractCRUDController extends AbstractWebController
|
||||
return $this->action_render_process($action, $this->getViewDatas(), $this->request->getVar('ActionTemplate'));
|
||||
}
|
||||
|
||||
final public function view($uid): string|RedirectResponse
|
||||
final public function view($uid): string|RedirectResponse|ResponseInterface
|
||||
{
|
||||
try {
|
||||
if (!$uid) {
|
||||
throw new RuntimeException(static::class . '->' . __FUNCTION__ . "에서 {$this->getTitle()}에 번호가 정의 되지 않았습니다.");
|
||||
}
|
||||
$action = __FUNCTION__;
|
||||
|
||||
return $this->runAction($action, function () use ($action, $uid) {
|
||||
$this->assertUid($uid, $action);
|
||||
|
||||
$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());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,13 @@
|
||||
<?php
|
||||
// =========================================================
|
||||
// AbstractWebController.php (FINAL)
|
||||
// - runAction / okResponse / failResponse 내장
|
||||
// - action_redirect_process: AJAX 방어 + 상태코드 정책 고정
|
||||
// * warning/error => 400
|
||||
// * critical/alert/emergency => 500
|
||||
// * info/notice/debug/default => 200
|
||||
// - RedirectResponse|ResponseInterface로 엄격 정리
|
||||
// =========================================================
|
||||
|
||||
namespace App\Controllers;
|
||||
|
||||
@ -9,28 +18,26 @@ use CodeIgniter\HTTP\RedirectResponse;
|
||||
use CodeIgniter\HTTP\RequestInterface;
|
||||
use CodeIgniter\HTTP\ResponseInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use App\Exceptions\FormValidationException;
|
||||
|
||||
/**
|
||||
* CommonController
|
||||
*
|
||||
* 이 클래스를 상속받는 모든 자식 클래스(UserController 등)는
|
||||
* 반드시 'PATH' 상수를 가지고 있음을 IDE에 알려줍니다.
|
||||
* * @property-read string PATH // ⭐ 이 부분이 핵심입니다.
|
||||
*/
|
||||
abstract class AbstractWebController extends Controller
|
||||
{
|
||||
use LogTrait;
|
||||
|
||||
private array $_action_paths = [];
|
||||
private array $_viewDatas = [];
|
||||
private ?string $_title = null;
|
||||
|
||||
protected $layouts = [];
|
||||
protected $service = null;
|
||||
// --- 초기화 및 DI ---
|
||||
|
||||
// --- 초기화 및 DI ---
|
||||
public function initController(RequestInterface $request, ResponseInterface $response, LoggerInterface $logger)
|
||||
{
|
||||
parent::initController($request, $response, $logger);
|
||||
helper('util');
|
||||
}
|
||||
|
||||
final protected function getAuthContext(): AuthContext
|
||||
{
|
||||
return service('myauth')->getAuthContext();
|
||||
@ -39,15 +46,13 @@ abstract class AbstractWebController extends Controller
|
||||
protected function getTitle(): string
|
||||
{
|
||||
if ($this->_title === null) {
|
||||
// 이 로직은 하위 클래스에서 service가 초기화되었다고 가정합니다.
|
||||
$this->_title = lang("{$this->service->getClassPaths(false)}.title");
|
||||
}
|
||||
return $this->_title;
|
||||
}
|
||||
|
||||
// --- 경로 및 뷰 데이터 관리 ---
|
||||
|
||||
final protected function addActionPaths(string $path)
|
||||
final protected function addActionPaths(string $path): void
|
||||
{
|
||||
$this->_action_paths[] = $path;
|
||||
}
|
||||
@ -57,24 +62,19 @@ abstract class AbstractWebController extends Controller
|
||||
return $isArray ? $this->_action_paths : implode($delimeter, $this->_action_paths);
|
||||
}
|
||||
|
||||
final protected function addViewDatas(string $key, mixed $value)
|
||||
final protected function addViewDatas(string $key, mixed $value): void
|
||||
{
|
||||
$this->_viewDatas[$key] = $value;
|
||||
}
|
||||
|
||||
final protected function getViewDatas(?string $key = null): mixed
|
||||
{
|
||||
if ($key === null) {
|
||||
if ($key === null)
|
||||
return $this->_viewDatas;
|
||||
}
|
||||
return $this->_viewDatas[$key] ?? null;
|
||||
}
|
||||
|
||||
// --- 공통 처리 로직 (Override 가능) ---
|
||||
|
||||
/**
|
||||
* 모든 액션 실행 전 공통 초기화 작업
|
||||
*/
|
||||
protected function action_init_process(string $action, array $formDatas = []): void
|
||||
{
|
||||
$this->addViewDatas('action', $action);
|
||||
@ -84,10 +84,49 @@ abstract class AbstractWebController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* 액션 성공 후 모달을 닫고 부모 창을 리로드하는 스크립트를 반환합니다.
|
||||
* action_redirect_process
|
||||
* ✅ AJAX 요청이면 RedirectResponse 대신 JSON으로 변환(방어)
|
||||
*
|
||||
* 상태코드 정책(고정):
|
||||
* - warning/error => 400
|
||||
* - critical/alert/emergency => 500
|
||||
* - info/notice/debug/default => 200
|
||||
*/
|
||||
protected function action_redirect_process(string $type, string $message, ?string $redirect_url = null): RedirectResponse
|
||||
protected function action_redirect_process(string $type, string $message, ?string $redirect_url = null): RedirectResponse|ResponseInterface
|
||||
{
|
||||
$resolvedRedirect = $redirect_url
|
||||
?? $this->getAuthContext()->popPreviousUrl()
|
||||
?? implode(DIRECTORY_SEPARATOR, $this->getActionPaths());
|
||||
|
||||
if ($this->request->isAJAX()) {
|
||||
$error400 = ['warning', 'error'];
|
||||
$error500 = ['critical', 'alert', 'emergency'];
|
||||
|
||||
if (in_array($type, $error400, true)) {
|
||||
log_message($type, $message);
|
||||
return $this->response->setStatusCode(400)->setJSON([
|
||||
'ok' => false,
|
||||
'message' => $message,
|
||||
'redirect' => $resolvedRedirect,
|
||||
]);
|
||||
}
|
||||
|
||||
if (in_array($type, $error500, true)) {
|
||||
log_message($type, $message);
|
||||
return $this->response->setStatusCode(500)->setJSON([
|
||||
'ok' => false,
|
||||
'message' => $message,
|
||||
'redirect' => $resolvedRedirect,
|
||||
]);
|
||||
}
|
||||
|
||||
return $this->response->setStatusCode(200)->setJSON([
|
||||
'ok' => true,
|
||||
'message' => $message,
|
||||
'redirect' => $resolvedRedirect,
|
||||
]);
|
||||
}
|
||||
|
||||
switch ($type) {
|
||||
case 'warning':
|
||||
case 'error':
|
||||
@ -95,37 +134,26 @@ abstract class AbstractWebController extends Controller
|
||||
case 'alert':
|
||||
case 'emergency':
|
||||
log_message($type, $message);
|
||||
$result = redirect()->back()->withInput()->with('message', $message);
|
||||
break;
|
||||
return redirect()->back()->withInput()->with('message', $message);
|
||||
|
||||
case 'debug':
|
||||
case 'info':
|
||||
case 'notice':
|
||||
default:
|
||||
$redirect_url = $redirect_url ?? $this->getAuthContext()->popPreviousUrl() ?? implode(DIRECTORY_SEPARATOR, $this->getActionPaths());
|
||||
$result = redirect()->to($redirect_url)->with('message', $message);
|
||||
break;
|
||||
return redirect()->to($resolvedRedirect)->with('message', $message);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 뷰 경로와 데이터를 이용하여 최종 HTML을 렌더링합니다.
|
||||
* 뷰 렌더링
|
||||
*/
|
||||
protected function action_render_process(
|
||||
string $view_file,
|
||||
array $viewDatas,
|
||||
?string $template_path = null
|
||||
): string {
|
||||
protected function action_render_process(string $view_file, array $viewDatas, ?string $template_path = null): string
|
||||
{
|
||||
helper(['form', 'utility']);
|
||||
/**
|
||||
* 🔥 핵심:
|
||||
* View name은 항상 슬래시 기반
|
||||
*/
|
||||
$baseViewPath = trim($viewDatas['layout']['path'], '/');
|
||||
|
||||
if ($template_path) {
|
||||
$baseViewPath = trim($viewDatas['layout']['path'], '/');
|
||||
if ($template_path)
|
||||
$baseViewPath .= '/' . trim($template_path, '/');
|
||||
}
|
||||
|
||||
$viewName = $baseViewPath . '/' . ltrim($view_file, '/');
|
||||
|
||||
@ -139,4 +167,53 @@ abstract class AbstractWebController extends Controller
|
||||
],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// =========================================================
|
||||
// 공통화: runAction / okResponse / failResponse
|
||||
// =========================================================
|
||||
|
||||
protected function runAction(string $action, callable $core): mixed
|
||||
{
|
||||
try {
|
||||
return $core();
|
||||
} catch (FormValidationException $e) {
|
||||
return $this->failResponse($action, $e);
|
||||
} catch (\Throwable $e) {
|
||||
return $this->failResponse($action, $e);
|
||||
}
|
||||
}
|
||||
|
||||
protected function okResponse(string $message, array $payload = [], ?string $redirectUrl = null): RedirectResponse|ResponseInterface
|
||||
{
|
||||
if ($this->request->isAJAX()) {
|
||||
return $this->response->setStatusCode(200)->setJSON(array_merge(
|
||||
['ok' => true, 'message' => $message],
|
||||
$payload
|
||||
));
|
||||
}
|
||||
return $this->action_redirect_process('info', $message, $redirectUrl);
|
||||
}
|
||||
|
||||
protected function failResponse(string $action, \Throwable $e, ?string $humanPrefix = null): RedirectResponse|ResponseInterface
|
||||
{
|
||||
if ($e instanceof FormValidationException) {
|
||||
if ($this->request->isAJAX()) {
|
||||
return $this->response->setStatusCode(422)->setJSON([
|
||||
'ok' => false,
|
||||
'errors' => $e->errors,
|
||||
]);
|
||||
}
|
||||
return $this->action_redirect_process('error', dev_exception($e->getMessage()));
|
||||
}
|
||||
|
||||
if ($this->request->isAJAX()) {
|
||||
return $this->response->setStatusCode(500)->setJSON([
|
||||
'ok' => false,
|
||||
'message' => static::class . '->' . $action . "에서 오류:" . $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
|
||||
$msg = $humanPrefix ? ($humanPrefix . $e->getMessage()) : $e->getMessage();
|
||||
return $this->action_redirect_process('error', dev_exception($msg));
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
|
||||
namespace App\Controllers\Auth;
|
||||
|
||||
use App\DTOs\Auth\GoogleDTO;
|
||||
use App\Entities\UserEntity;
|
||||
use CodeIgniter\HTTP\RequestInterface;
|
||||
use CodeIgniter\HTTP\ResponseInterface;
|
||||
@ -25,8 +24,7 @@ class GoogleController extends AuthController
|
||||
//로그인처리
|
||||
protected function login_process(): UserEntity
|
||||
{
|
||||
//요청 데이터를 DTO 객체로 변환
|
||||
return $this->service->login(new GoogleDTO($this->request->getPost()));
|
||||
return $this->service->login($this->request->getPost());
|
||||
}
|
||||
protected function logout_process(): void
|
||||
{
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
|
||||
namespace App\Controllers\Auth;
|
||||
|
||||
use App\DTOs\Auth\LocalDTO;
|
||||
use App\Entities\UserEntity;
|
||||
use App\Services\Auth\LocalService;
|
||||
use CodeIgniter\HTTP\RequestInterface;
|
||||
@ -25,7 +24,7 @@ class LocalController extends AuthController
|
||||
//로그인처리
|
||||
protected function login_process(): UserEntity
|
||||
{
|
||||
return $this->service->login(new LocalDTO($this->request->getPost()));
|
||||
return $this->service->login($this->request->getPost());
|
||||
}
|
||||
protected function logout_process(): void
|
||||
{
|
||||
|
||||
@ -1,45 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controllers;
|
||||
|
||||
use CodeIgniter\Controller;
|
||||
use CodeIgniter\HTTP\RequestInterface;
|
||||
use CodeIgniter\HTTP\ResponseInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
/**
|
||||
* BaseController provides a convenient place for loading components
|
||||
* and performing functions that are needed by all your controllers.
|
||||
*
|
||||
* Extend this class in any new controllers:
|
||||
* ```
|
||||
* class Home extends BaseController
|
||||
* ```
|
||||
*
|
||||
* For security, be sure to declare any new methods as protected or private.
|
||||
*/
|
||||
abstract class BaseController extends Controller
|
||||
{
|
||||
/**
|
||||
* Be sure to declare properties for any property fetch you initialized.
|
||||
* The creation of dynamic property is deprecated in PHP 8.2.
|
||||
*/
|
||||
|
||||
// protected $session;
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function initController(RequestInterface $request, ResponseInterface $response, LoggerInterface $logger)
|
||||
{
|
||||
// Load here all helpers you want to be available in your controllers that extend BaseController.
|
||||
// Caution: Do not put the this below the parent::initController() call below.
|
||||
// $this->helpers = ['form', 'url'];
|
||||
|
||||
// Caution: Do not edit this line.
|
||||
parent::initController($request, $response, $logger);
|
||||
|
||||
// Preload any models, libraries, etc, here.
|
||||
// $this->session = service('session');
|
||||
}
|
||||
}
|
||||
@ -1,13 +1,22 @@
|
||||
<?php
|
||||
// =========================================================
|
||||
// CommonController.php (FINAL)
|
||||
// - index() runAction 적용 (렌더링 예외도 동일 처리)
|
||||
// - batchjob/batchjob_delete/download runAction 적용
|
||||
// - "결과/리다이렉트"는 RedirectResponse|ResponseInterface로 엄격 정리
|
||||
// =========================================================
|
||||
|
||||
namespace App\Controllers;
|
||||
|
||||
use RuntimeException;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use CodeIgniter\HTTP\DownloadResponse;
|
||||
use CodeIgniter\HTTP\RedirectResponse;
|
||||
use CodeIgniter\HTTP\RequestInterface;
|
||||
use CodeIgniter\HTTP\ResponseInterface;
|
||||
use PhpOffice\PhpSpreadsheet\IOFactory;
|
||||
use PhpOffice\PhpSpreadsheet\Reader\Html;
|
||||
use PhpOffice\PhpSpreadsheet\Writer\Pdf\Mpdf;
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* CommonController
|
||||
@ -15,22 +24,29 @@ use RuntimeException;
|
||||
*/
|
||||
abstract class CommonController extends AbstractCRUDController
|
||||
{
|
||||
// --- 일괄 작업 (Batch Job) ---
|
||||
public function initController(RequestInterface $request, ResponseInterface $response, LoggerInterface $logger)
|
||||
{
|
||||
parent::initController($request, $response, $logger);
|
||||
}
|
||||
|
||||
// =========================================================
|
||||
// Batch Job
|
||||
// =========================================================
|
||||
|
||||
protected function batchjob_pre_process(array $postDatas): array
|
||||
{
|
||||
// 1. postDatas에서 선택된 uids 정보 추출
|
||||
$uids = $postDatas['batchjob_uids'] ?? [];
|
||||
if (empty($uids)) {
|
||||
throw new RuntimeException("{$this->getTitle()}에서 일괄작업에 적용할 리스트을 선택하셔야합니다.");
|
||||
}
|
||||
// 2. 변경할 데이터 추출 및 정리
|
||||
unset($postDatas['batchjob_uids'], $postDatas['batchjob_submit']); //formDatas에 포함되지 않게하기위함
|
||||
|
||||
unset($postDatas['batchjob_uids'], $postDatas['batchjob_submit']);
|
||||
$formDatas = array_filter($postDatas, fn($value) => $value !== "" && $value !== null);
|
||||
if (empty($formDatas)) {
|
||||
throw new RuntimeException(message: "{$this->getTitle()}에서 일괄작업에 변경할 조건항목을 선택하셔야합니다.");
|
||||
throw new RuntimeException("{$this->getTitle()}에서 일괄작업에 변경할 조건항목을 선택하셔야합니다.");
|
||||
}
|
||||
// 3. 데이터가 있는 필드 추출
|
||||
return array($uids, $formDatas);
|
||||
|
||||
return [$uids, $formDatas];
|
||||
}
|
||||
|
||||
protected function batchjob_process(array $uids, array $formDatas): array
|
||||
@ -38,7 +54,7 @@ abstract class CommonController extends AbstractCRUDController
|
||||
return $this->service->batchjob($uids, $formDatas);
|
||||
}
|
||||
|
||||
protected function batchjob_result_process(array $uids, array $entities): string|RedirectResponse
|
||||
protected function batchjob_result_process(array $uids, array $entities): RedirectResponse|ResponseInterface
|
||||
{
|
||||
return $this->action_redirect_process('info', sprintf(
|
||||
"%s에서 %s개 처리완료 총:%s개 수정이 완료되었습니다.",
|
||||
@ -47,19 +63,21 @@ abstract class CommonController extends AbstractCRUDController
|
||||
count($uids)
|
||||
));
|
||||
}
|
||||
final public function batchjob(): string|RedirectResponse
|
||||
|
||||
final public function batchjob(): RedirectResponse|ResponseInterface|string
|
||||
{
|
||||
try {
|
||||
$action = __FUNCTION__;
|
||||
// 사전작업 및 데이터 추출 초기화
|
||||
list($uids, $formDatas) = $this->batchjob_pre_process($this->request->getPost());
|
||||
$action = __FUNCTION__;
|
||||
|
||||
return $this->runAction($action, function () use ($action) {
|
||||
[$uids, $formDatas] = $this->batchjob_pre_process($this->request->getPost());
|
||||
$entities = $this->batchjob_process($uids, $formDatas);
|
||||
return $this->batchjob_result_process($uids, $entities);
|
||||
} catch (\Throwable $e) {
|
||||
return $this->action_redirect_process('error', static::class . '->' . __FUNCTION__ . "에서 {$this->getTitle()} 일괄수정 오류:" . $e->getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
// --- 일괄 삭제 (Batch Job Delete) ---
|
||||
|
||||
// =========================================================
|
||||
// Batch Job Delete
|
||||
// =========================================================
|
||||
|
||||
protected function batchjob_delete_pre_process(array $postDatas): array
|
||||
{
|
||||
@ -70,7 +88,12 @@ abstract class CommonController extends AbstractCRUDController
|
||||
return $uids;
|
||||
}
|
||||
|
||||
protected function batchjob_delete_result_process(array $uids, array $entities): string|RedirectResponse
|
||||
protected function batchjob_delete_process(array $uids): array
|
||||
{
|
||||
return $this->service->batchjob_delete($uids);
|
||||
}
|
||||
|
||||
protected function batchjob_delete_result_process(array $uids, array $entities): RedirectResponse|ResponseInterface
|
||||
{
|
||||
return $this->action_redirect_process('info', sprintf(
|
||||
"%s에서 %s개 처리완료, 총:%s개 일괄삭제가 완료되었습니다.",
|
||||
@ -80,51 +103,38 @@ abstract class CommonController extends AbstractCRUDController
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* 단일 삭제 로직을 재사용 (Override 가능)
|
||||
*/
|
||||
protected function batchjob_delete_process(array $uids): array
|
||||
final public function batchjob_delete(): RedirectResponse|ResponseInterface|string
|
||||
{
|
||||
return $this->service->batchjob_delete($uids);
|
||||
}
|
||||
final public function batchjob_delete(): string|RedirectResponse
|
||||
{
|
||||
try {
|
||||
$action = __FUNCTION__;
|
||||
|
||||
return $this->runAction($action, function () use ($action) {
|
||||
$uids = $this->batchjob_delete_pre_process($this->request->getPost());
|
||||
$entities = $this->batchjob_delete_process($uids);
|
||||
return $this->batchjob_delete_result_process($uids, $entities);
|
||||
} catch (\Throwable $e) {
|
||||
return $this->action_redirect_process('error', static::class . '->' . __FUNCTION__ . "에서 {$this->getTitle()} 일괄삭제 오류:" . $e->getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// --- 목록 (Index / List) 관련 ---
|
||||
// =========================================================
|
||||
// Index / List
|
||||
// =========================================================
|
||||
|
||||
/**
|
||||
* 조건절(필터, 검색어, 날짜, 정렬)을 처리합니다. (Override 가능)
|
||||
*/
|
||||
protected function index_condition_process(string $action): void
|
||||
{
|
||||
// Filter조건절 처리
|
||||
$index_filters = [];
|
||||
// dd($this->service->getActionForm()->getIndexFilters($action));
|
||||
foreach ($this->service->getActionForm()->getIndexFilters($action) as $field) {
|
||||
$value = $this->request->getVar($field) ?? null;
|
||||
if ($value) {
|
||||
if ($value)
|
||||
$this->service->setFilter($field, $value);
|
||||
}
|
||||
$index_filters[$field] = $value;
|
||||
}
|
||||
$this->addViewDatas('index_filters', $index_filters);
|
||||
|
||||
// 검색어조건절 처리
|
||||
$index_word = $this->request->getVar('index_word');
|
||||
if ($index_word !== null && $index_word !== '') {
|
||||
$this->service->setSearchWord($index_word);
|
||||
}
|
||||
$this->addViewDatas('index_word', $index_word);
|
||||
|
||||
// 날자검색
|
||||
$index_start = $this->request->getVar('index_start');
|
||||
$index_end = $this->request->getVar('index_end');
|
||||
if ($index_start !== null && $index_start !== '' && $index_end !== null && $index_end !== '') {
|
||||
@ -133,7 +143,6 @@ abstract class CommonController extends AbstractCRUDController
|
||||
$this->addViewDatas('index_start', $index_start);
|
||||
$this->addViewDatas('index_end', $index_end);
|
||||
|
||||
// OrderBy처리
|
||||
$order_field = $this->request->getVar('order_field');
|
||||
$order_value = $this->request->getVar('order_value');
|
||||
$this->service->setOrderBy($order_field, $order_value);
|
||||
@ -141,23 +150,15 @@ abstract class CommonController extends AbstractCRUDController
|
||||
$this->addViewDatas('order_value', $order_value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pagenation Select Box 옵션을 생성합니다. (Override 가능)
|
||||
*/
|
||||
protected function pagenation_options_process(int $index_totalcount, int $perpage): array
|
||||
{
|
||||
$page_options = ["" => "줄수선택"];
|
||||
// 기존 로직 유지
|
||||
for ($i = $perpage; $i <= $index_totalcount; $i += $perpage) {
|
||||
for ($i = $perpage; $i <= $index_totalcount; $i += $perpage)
|
||||
$page_options[$i] = $i;
|
||||
}
|
||||
$page_options[$index_totalcount] = $index_totalcount;
|
||||
return $page_options;
|
||||
}
|
||||
|
||||
/**
|
||||
* PageNation 링크를 생성하고 뷰 데이터에 추가합니다. (Override 가능)
|
||||
*/
|
||||
protected function pagenation_process(int $index_totalcount, int $page, int $perpage, $pager_group = 'default', int $segment = 0, $template = 'bootstrap_full'): mixed
|
||||
{
|
||||
$pager = service("pager");
|
||||
@ -166,14 +167,10 @@ abstract class CommonController extends AbstractCRUDController
|
||||
return $pager->links($pager_group, $template);
|
||||
}
|
||||
|
||||
/**
|
||||
* Service에서 엔티티 목록을 가져와 처리합니다. (Override 가능)
|
||||
*/
|
||||
protected function index_entities_process(array $entities = []): array
|
||||
{
|
||||
foreach ($this->service->getEntities() as $entity) {
|
||||
foreach ($this->service->getEntities() as $entity)
|
||||
$entities[] = $entity;
|
||||
}
|
||||
return $entities;
|
||||
}
|
||||
|
||||
@ -182,63 +179,71 @@ abstract class CommonController extends AbstractCRUDController
|
||||
return $this->action_render_process($action, $this->getViewDatas(), $this->request->getVar('ActionTemplate'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 인덱스(목록) 페이지의 메인 로직입니다.
|
||||
*/
|
||||
protected function index_process(string $action): void
|
||||
{
|
||||
// 현재 URL을 이전 URL 스택에 저장
|
||||
$this->getAuthContext()->pushCurrentUrl($this->request->getUri()->getPath() . ($this->request->getUri()->getQuery() ? "?" . $this->request->getUri()->getQuery() : ""));
|
||||
$this->getAuthContext()->pushCurrentUrl(
|
||||
$this->request->getUri()->getPath() . ($this->request->getUri()->getQuery() ? "?" . $this->request->getUri()->getQuery() : "")
|
||||
);
|
||||
$this->addViewDatas('uri', $this->request->getUri());
|
||||
// Paging 설정
|
||||
|
||||
$page = (int) $this->request->getVar('page') ?: 1;
|
||||
$perpage = (int) $this->request->getVar('perpage') ?: intval(DEFAULTS['INDEX_PERPAGE'] ?? 10);
|
||||
$this->addViewDatas('page', $page);
|
||||
$this->addViewDatas('perpage', $perpage);
|
||||
// 1. Total Count 계산을 위한 조건절 처리 (오버라이드 가능)
|
||||
|
||||
$this->index_condition_process($action);
|
||||
$index_totalcount = $this->service->getTotalCount();
|
||||
$this->addViewDatas('index_totalcount', $index_totalcount);
|
||||
// Pagination 설정
|
||||
|
||||
$this->addViewDatas('index_pagination', $this->pagenation_process($index_totalcount, $page, $perpage));
|
||||
$this->addViewDatas('index_pagination_options', $this->pagenation_options_process($index_totalcount, $perpage));
|
||||
// 2. 실제 리스트를 위한 조건절, LIMIT, OFFSET 처리 (오버라이드 가능)
|
||||
$this->index_condition_process($action); // 조건절을 다시 호출하여 필터/검색어 유지
|
||||
|
||||
$this->index_condition_process($action);
|
||||
$this->service->setLimit($perpage);
|
||||
$this->service->setOffset(($page - 1) * $perpage);
|
||||
// Entities 처리
|
||||
|
||||
$this->addViewDatas('entities', $this->index_entities_process());
|
||||
helper(['form']);
|
||||
$this->addViewDatas('formDatas', $this->request->getVar() ?? []);
|
||||
}
|
||||
|
||||
public function index(): string
|
||||
public function index(): string|RedirectResponse|ResponseInterface
|
||||
{
|
||||
$action = __FUNCTION__;
|
||||
$this->action_init_process($action);
|
||||
$this->index_process($action);
|
||||
return $this->index_result_process($action);
|
||||
|
||||
return $this->runAction($action, function () use ($action) {
|
||||
$this->action_init_process($action);
|
||||
$this->index_process($action);
|
||||
return $this->index_result_process($action);
|
||||
});
|
||||
}
|
||||
|
||||
// --- 문서 다운로드 (Download) ---
|
||||
// =========================================================
|
||||
// Download
|
||||
// =========================================================
|
||||
|
||||
protected function downloadByDocumentType(string $document_type, mixed $loaded_data): array
|
||||
{
|
||||
$full_path = WRITEPATH . DIRECTORY_SEPARATOR . "download";
|
||||
|
||||
switch ($document_type) {
|
||||
case 'excel':
|
||||
$file_name = sprintf("%s_%s.xlsx", $this->service->getClassPaths(false, "_"), date('Y-m-d_Hm'));
|
||||
$writer = IOFactory::createWriter($loaded_data, 'Xlsx');
|
||||
$writer->save($full_path . DIRECTORY_SEPARATOR . $file_name);
|
||||
break;
|
||||
|
||||
case 'pdf':
|
||||
$file_name = sprintf("%s_%s.pdf", $this->service->getClassPaths(false, "_"), date('Y-m-d_Hm'));
|
||||
$writer = new Mpdf($loaded_data);
|
||||
$writer->save($full_path . DIRECTORY_SEPARATOR . $file_name);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new RuntimeException(static::class . '->' . __FUNCTION__ . "에서 지원하지 않는 다운로드 타입입니다: {$document_type}");
|
||||
}
|
||||
return array($full_path, $file_name);
|
||||
|
||||
return [$full_path, $file_name];
|
||||
}
|
||||
|
||||
protected function download_process(string $action, string $output_type, mixed $uid = null): DownloadResponse|RedirectResponse|string
|
||||
@ -247,45 +252,45 @@ abstract class CommonController extends AbstractCRUDController
|
||||
case 'excel':
|
||||
case 'pdf':
|
||||
helper(['form']);
|
||||
// 전체 목록을 다운로드하므로, 목록 조건절을 처리합니다.
|
||||
|
||||
$this->index_condition_process($action);
|
||||
$this->addViewDatas('entities', $this->index_entities_process());
|
||||
|
||||
// HTML로 렌더링된 내용을 가져옵니다.
|
||||
$html = $this->action_render_process($action, $this->getViewDatas(), $this->request->getVar('ActionTemplate'));
|
||||
|
||||
// HTML을 PhpSpreadsheet 객체로 로드합니다.
|
||||
$reader = new Html();
|
||||
$loaded_data = $reader->loadFromString($html);
|
||||
|
||||
// 파일 저장 및 정보 가져오기
|
||||
list($full_path, $file_name) = $this->downloadByDocumentType($output_type, $loaded_data);
|
||||
[$full_path, $file_name] = $this->downloadByDocumentType($output_type, $loaded_data);
|
||||
$full_path .= DIRECTORY_SEPARATOR . $file_name;
|
||||
break;
|
||||
|
||||
default:
|
||||
// 개별 파일 다운로드 로직
|
||||
if (!$uid) {
|
||||
throw new RuntimeException(static::class . '->' . __FUNCTION__ . "에서 {$output_type}은 반드시 uid의 값이 필요합니다.");
|
||||
}
|
||||
|
||||
$entity = $this->service->getEntity($uid);
|
||||
if (!$entity) {
|
||||
throw new RuntimeException(static::class . '->' . __FUNCTION__ . "에서 {$uid}에 대한 정보를 찾을수 없습니다.");
|
||||
}
|
||||
|
||||
$this->addViewDatas('entity', $entity);
|
||||
list($file_name, $uploaded_filename) = $entity->getDownlaodFile();
|
||||
[$file_name, $uploaded_filename] = $entity->getDownlaodFile();
|
||||
$full_path = WRITEPATH . DIRECTORY_SEPARATOR . "uploads" . DIRECTORY_SEPARATOR . $uploaded_filename;
|
||||
break;
|
||||
}
|
||||
|
||||
return $this->response->download($full_path, null)->setFileName($file_name);
|
||||
}
|
||||
final public function download(string $output_type, mixed $uid = false): DownloadResponse|RedirectResponse|string
|
||||
|
||||
final public function download(string $output_type, mixed $uid = false): DownloadResponse|RedirectResponse|string|ResponseInterface
|
||||
{
|
||||
try {
|
||||
$action = __FUNCTION__;
|
||||
$action = __FUNCTION__;
|
||||
|
||||
return $this->runAction($action, function () use ($action, $output_type, $uid) {
|
||||
$this->action_init_process($action);
|
||||
return $this->download_process($action, $output_type, $uid);
|
||||
} catch (\Throwable $e) {
|
||||
return $this->action_redirect_process('error', static::class . '->' . __FUNCTION__ . "에서 {$this->getTitle()} 다운로드 오류:" . $e->getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,7 +15,6 @@ abstract class FrontController extends CommonController
|
||||
parent::initController($request, $response, $logger);
|
||||
$this->addActionPaths($this->_layout);
|
||||
$this->layouts = LAYOUTS[$this->_layout];
|
||||
helper('util');
|
||||
}
|
||||
protected function action_init_process(string $action, array $formDatas = []): void
|
||||
{
|
||||
|
||||
@ -1,13 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\DTOs\Auth;
|
||||
|
||||
use App\DTOs\CommonDTO;
|
||||
|
||||
abstract class AuthDTO extends CommonDTO
|
||||
{
|
||||
public function __construct(array $datas = [])
|
||||
{
|
||||
parent::__construct($datas);
|
||||
}
|
||||
}
|
||||
@ -1,13 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\DTOs\Auth;
|
||||
|
||||
class GoogleDTO extends AuthDTO
|
||||
{
|
||||
|
||||
public $access_code = null;
|
||||
public function __construct(array $datas = [])
|
||||
{
|
||||
parent::__construct($datas);
|
||||
}
|
||||
}
|
||||
@ -1,14 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\DTOs\Auth;
|
||||
|
||||
class LocalDTO extends AuthDTO
|
||||
{
|
||||
public ?string $id = null;
|
||||
public ?string $passwd = null;
|
||||
|
||||
public function __construct(array $datas = [])
|
||||
{
|
||||
parent::__construct($datas);
|
||||
}
|
||||
}
|
||||
@ -1,19 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\DTOs;
|
||||
|
||||
class BoardDTO extends CommonDTO
|
||||
{
|
||||
public ?int $uid = null;
|
||||
public ?int $user_uid = null;
|
||||
public ?int $worker_uid = null;
|
||||
public string $category = '';
|
||||
public string $title = '';
|
||||
public string $status = '';
|
||||
public string $content = '';
|
||||
|
||||
public function __construct(array $datas = [])
|
||||
{
|
||||
parent::__construct($datas);
|
||||
}
|
||||
}
|
||||
@ -1,72 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\DTOs;
|
||||
|
||||
use ReflectionClass;
|
||||
use ReflectionNamedType;
|
||||
|
||||
abstract class CommonDTO
|
||||
{
|
||||
protected function __construct(array $datas = [])
|
||||
{
|
||||
if (empty($datas))
|
||||
return;
|
||||
|
||||
$reflection = new ReflectionClass($this);
|
||||
|
||||
foreach ($datas as $key => $value) {
|
||||
if (!$reflection->hasProperty($key))
|
||||
continue;
|
||||
|
||||
$property = $reflection->getProperty($key);
|
||||
$type = $property->getType();
|
||||
$assignValue = $value;
|
||||
|
||||
// *_uid 규칙 처리
|
||||
if ($value === '' && preg_match('/_uid$/', $key)) {
|
||||
if ($type instanceof ReflectionNamedType && $type->allowsNull()) {
|
||||
$this->{$key} = null;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// 1) 기존: 빈 문자열('') 처리
|
||||
if ($value === '') {
|
||||
if ($type instanceof ReflectionNamedType && $type->allowsNull()) {
|
||||
$assignValue = null;
|
||||
} else {
|
||||
$typeName = ($type instanceof ReflectionNamedType) ? $type->getName() : '';
|
||||
$assignValue = ($typeName === 'int' || $typeName === 'float') ? 0 : '';
|
||||
}
|
||||
}
|
||||
// 2) 기존: 타입별 캐스팅
|
||||
elseif ($type instanceof ReflectionNamedType) {
|
||||
$typeName = $type->getName();
|
||||
|
||||
if ($typeName === 'array' && is_string($value)) {
|
||||
$assignValue = explode(DEFAULTS["DELIMITER_COMMA"], $value);
|
||||
} elseif ($typeName === 'int' && is_numeric($value)) {
|
||||
$assignValue = (int) $value;
|
||||
} elseif ($typeName === 'float' && is_numeric($value)) {
|
||||
$assignValue = (float) $value;
|
||||
}
|
||||
}
|
||||
|
||||
$this->{$key} = $assignValue;
|
||||
}
|
||||
}
|
||||
|
||||
public function toArray(): array
|
||||
{
|
||||
$reflection = new ReflectionClass($this);
|
||||
$properties = $reflection->getProperties();
|
||||
$result = [];
|
||||
|
||||
foreach ($properties as $property) {
|
||||
$name = $property->getName();
|
||||
$result[$name] = $this->{$name};
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
@ -1,17 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\DTOs;
|
||||
|
||||
class InquiryDTO extends CommonDTO
|
||||
{
|
||||
public ?int $uid = null;
|
||||
public string $title = '';
|
||||
public string $email = '';
|
||||
public string $content = '';
|
||||
public string $status = '';
|
||||
|
||||
public function __construct(array $datas = [])
|
||||
{
|
||||
parent::__construct($datas);
|
||||
}
|
||||
}
|
||||
@ -1,40 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\DTOs;
|
||||
|
||||
class UserDTO extends CommonDTO
|
||||
{
|
||||
public ?int $uid = null;
|
||||
public string $id = '';
|
||||
public string $passwd = '';
|
||||
public string $confirmpassword = '';
|
||||
public string $name = '';
|
||||
public string $email = '';
|
||||
public string $mobile = '';
|
||||
public array $role = [];
|
||||
public string $status = '';
|
||||
|
||||
public function __construct(array $datas = [])
|
||||
{
|
||||
// 1. [전처리] 입력값이 문자열(CSV)로 들어왔다면 배열로 변환
|
||||
if (isset($datas['role']) && is_string($datas['role'])) {
|
||||
$datas['role'] = explode(DEFAULTS["DELIMITER_COMMA"], $datas['role']);
|
||||
}
|
||||
|
||||
// 2. 만약 데이터가 없다면 빈 배열로 초기화
|
||||
if (!isset($datas['role'])) {
|
||||
$datas['role'] = [];
|
||||
}
|
||||
|
||||
// 3. 부모 생성자 호출
|
||||
parent::__construct($datas);
|
||||
}
|
||||
|
||||
/**
|
||||
* DB 저장용(Entity 전달용) CSV 문자열이 필요할 때 사용합니다.
|
||||
*/
|
||||
public function getRoleToString(): string
|
||||
{
|
||||
return implode(DEFAULTS["DELIMITER_COMMA"], $this->role);
|
||||
}
|
||||
}
|
||||
@ -20,3 +20,25 @@ if (!function_exists('alertTrait')) {
|
||||
return $util->alertTrait($msg, $url);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('dev_exception')) {
|
||||
function dev_exception(string $message): RuntimeException
|
||||
{
|
||||
if (ENVIRONMENT === 'development') {
|
||||
$trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
|
||||
$caller = $trace[1] ?? null;
|
||||
|
||||
if ($caller) {
|
||||
$message = sprintf(
|
||||
'%s->%s에서 오류발생: %s',
|
||||
$caller['class'] ?? '',
|
||||
$caller['function'] ?? '',
|
||||
$message
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return new RuntimeException($message);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,9 @@
|
||||
<?php
|
||||
// app/Language/kr/Validation.php
|
||||
|
||||
// public array $ruleSets = [
|
||||
// ...
|
||||
// \App\Validation\CustomRules::class, // ✅ 추가
|
||||
// ];
|
||||
return [
|
||||
// 여기서부터 각 Validation rule에 대한 메시지를 정의합니다.
|
||||
// {field}나 {param} 같은 플레이스홀더는 그대로 유지해야 합니다.
|
||||
|
||||
@ -2,8 +2,6 @@
|
||||
|
||||
namespace App\Services\Auth;
|
||||
|
||||
use App\DTOs\Auth\GoogleDTO;
|
||||
use App\DTOs\Auth\LocalDTO;
|
||||
use App\Entities\UserEntity;
|
||||
use App\Helpers\AuthHelper;
|
||||
use App\Models\CommonModel;
|
||||
@ -24,9 +22,9 @@ abstract class AuthService extends CommonService
|
||||
}
|
||||
//로그인
|
||||
abstract protected function login_process(array $formDatas): UserEntity;
|
||||
final public function login(LocalDTO|GoogleDTO $dto): UserEntity
|
||||
final public function login(array $formDatas): UserEntity
|
||||
{
|
||||
$entity = $this->login_process($dto->toArray());
|
||||
$entity = $this->login_process($formDatas);
|
||||
//인증 세션처리
|
||||
$this->getAuthContext()->setAuthSession($entity);
|
||||
return $entity;
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
|
||||
namespace App\Services\Auth;
|
||||
|
||||
use App\DTOs\Auth\GoogleDTO;
|
||||
use App\Entities\UserEntity;
|
||||
use App\Forms\Auth\GoogleForm;
|
||||
use App\Libraries\MySocket\GoogleSocket\CURL;
|
||||
@ -18,14 +17,6 @@ class GoogleService extends AuthService
|
||||
parent::__construct($model);
|
||||
$this->addClassPaths('Google');
|
||||
}
|
||||
public function createDTO(array $formDatas): GoogleDTO
|
||||
{
|
||||
return new GoogleDTO($formDatas);
|
||||
}
|
||||
public function getDTOClass(): string
|
||||
{
|
||||
return GoogleDTO::class;
|
||||
}
|
||||
protected function getEntity_process(mixed $entity): UserEntity
|
||||
{
|
||||
return $entity;
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
|
||||
namespace App\Services\Auth;
|
||||
|
||||
use App\DTOs\Auth\LocalDTO;
|
||||
use App\Entities\UserEntity;
|
||||
use App\Forms\Auth\LocalForm;
|
||||
use App\Models\UserModel;
|
||||
@ -17,14 +16,6 @@ class LocalService extends AuthService
|
||||
parent::__construct($model);
|
||||
$this->addClassPaths('Local');
|
||||
}
|
||||
public function createDTO(array $formDatas): LocalDTO
|
||||
{
|
||||
return new LocalDTO($formDatas);
|
||||
}
|
||||
public function getDTOClass(): string
|
||||
{
|
||||
return LocalDTO::class;
|
||||
}
|
||||
protected function getEntity_process(mixed $entity): UserEntity
|
||||
{
|
||||
return $entity;
|
||||
|
||||
@ -6,7 +6,6 @@ use App\Models\BoardModel;
|
||||
use App\Helpers\BoardHelper;
|
||||
use App\Forms\BoardForm;
|
||||
use App\Entities\BoardEntity;
|
||||
use App\DTOs\BoardDTO;
|
||||
|
||||
class BoardService extends CommonService
|
||||
{
|
||||
@ -18,14 +17,6 @@ class BoardService extends CommonService
|
||||
parent::__construct($model);
|
||||
$this->addClassPaths('Board');
|
||||
}
|
||||
public function getDTOClass(): string
|
||||
{
|
||||
return BoardDTO::class;
|
||||
}
|
||||
public function createDTO(array $formDatas): BoardDTO
|
||||
{
|
||||
return new BoardDTO($formDatas);
|
||||
}
|
||||
public function getEntityClass(): string
|
||||
{
|
||||
return BoardEntity::class;
|
||||
|
||||
@ -3,7 +3,6 @@
|
||||
namespace App\Services;
|
||||
|
||||
use App\Forms\CommonForm;
|
||||
use App\DTOs\CommonDTO;
|
||||
use App\Entities\CommonEntity;
|
||||
use App\Models\CommonModel;
|
||||
use App\Libraries\AuthContext;
|
||||
@ -26,9 +25,6 @@ abstract class CommonService
|
||||
protected function __construct(protected CommonModel $model)
|
||||
{
|
||||
}
|
||||
|
||||
abstract public function getDTOClass(): string;
|
||||
abstract public function createDTO(array $formDatas): CommonDTO;
|
||||
abstract public function getEntityClass(): string;
|
||||
|
||||
/**
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\DTOs\InquiryDTO;
|
||||
use App\Forms\InquiryForm;
|
||||
use App\Models\InquiryModel;
|
||||
use App\Entities\CommonEntity;
|
||||
@ -19,14 +18,6 @@ class InquiryService extends CommonService
|
||||
parent::__construct($model);
|
||||
$this->addClassPaths('Inquiry');
|
||||
}
|
||||
public function getDTOClass(): string
|
||||
{
|
||||
return InquiryDTO::class;
|
||||
}
|
||||
public function createDTO(array $formDatas): InquiryDTO
|
||||
{
|
||||
return new InquiryDTO($formDatas);
|
||||
}
|
||||
public function getEntityClass(): string
|
||||
{
|
||||
return InquiryEntity::class;
|
||||
|
||||
@ -8,7 +8,6 @@ use App\Helpers\UserHelper;
|
||||
use App\Forms\UserForm;
|
||||
use App\Entities\UserEntity;
|
||||
use App\Entities\CommonEntity;
|
||||
use App\DTOs\UserDTO;
|
||||
|
||||
class UserService extends CommonService
|
||||
{
|
||||
@ -20,14 +19,6 @@ class UserService extends CommonService
|
||||
parent::__construct($model);
|
||||
$this->addClassPaths('User');
|
||||
}
|
||||
public function getDTOClass(): string
|
||||
{
|
||||
return UserDTO::class;
|
||||
}
|
||||
public function createDTO(array $formDatas): UserDTO
|
||||
{
|
||||
return new UserDTO($formDatas);
|
||||
}
|
||||
public function getEntityClass(): string
|
||||
{
|
||||
return UserEntity::class;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user