diff --git a/app/Config/Constants.php b/app/Config/Constants.php
index 3099215..8837c71 100644
--- a/app/Config/Constants.php
+++ b/app/Config/Constants.php
@@ -384,3 +384,16 @@ define("STATUS", [
'PAID' => 'paid',
'UNPAID' => 'unpaid',
]);
+
+//ROLE
+define("ROLE", [
+ 'USER' => [
+ 'MANAGER' => "manager",
+ 'CLOUDFLARE' => "cloudflare",
+ 'FIREWALL' => "firewall",
+ 'SECURITY' => "security",
+ 'DIRECTOR' => "director",
+ 'MASTER' => "master",
+ ],
+ ['CLIENT'] => []
+]);
diff --git a/app/Controllers/Admin/UserController.php b/app/Controllers/Admin/UserController.php
index f9e7a66..510f6ae 100644
--- a/app/Controllers/Admin/UserController.php
+++ b/app/Controllers/Admin/UserController.php
@@ -37,10 +37,23 @@ class UserController extends AdminController
return parent::getFormRule($action, $field, $rule);
}
//Index,FieldForm관련.
- protected function create_process(): UserEntity
+ protected function create_process(): RedirectResponse
{
//요청 데이터를 DTO 객체로 변환
$dto = new UserDTO($this->request->getPost());
- return $this->service->create($dto);
+ $entity = $this->service->create($dto);
+ $redirect_url = $this->authService->popPreviousUrl() ?? implode(DIRECTORY_SEPARATOR, $this->getActionPaths());
+ return redirect()->to($redirect_url)->with('success', "{$entity->getTitle()} 계정 생성이 완료되었습니다.");
+ }
+ protected function modify_form_process($uid): UserEntity
+ {
+ if (!$uid) {
+ throw new \Exception("계정 번호가 정의 되지 않았습니다.");
+ }
+ $entity = $this->service->getEntity($uid);
+ if (!$entity instanceof UserEntity) {
+ throw new \Exception("{$uid}에 해당하는 계정을 찾을수 없습니다.");
+ }
+ return $entity;
}
}
diff --git a/app/Controllers/CommonController.php b/app/Controllers/CommonController.php
index c4f55f7..e04a820 100644
--- a/app/Controllers/CommonController.php
+++ b/app/Controllers/CommonController.php
@@ -117,7 +117,7 @@ abstract class CommonController extends BaseController
return view($full_path, ['viewDatas' => $view_datas]);
}
protected function create_form_process(): void {}
- final public function create_form(): string|RedirectResponse
+ final public function create_form(): string
{
$action = __FUNCTION__;
try {
@@ -138,19 +138,14 @@ abstract class CommonController extends BaseController
$this->getViewDatas()
);
}
- protected function create_process(): mixed
- {
- return $this->service->create();
- }
+ abstract protected function create_process(): RedirectResponse;
final public function create(): RedirectResponse
{
$action = __FUNCTION__;
try {
//초기화
$this->action_init_process($action);
- $this->create_process();
- $redirect_url = $this->authService->popPreviousUrl() ?? implode(DIRECTORY_SEPARATOR, $this->getActionPaths());
- return redirect()->to($redirect_url)->with('success', static::class . "/{$action}이 완료되었습니다.");
+ return $this->create_process();
} catch (ValidationException $e) {
// 검증 실패 시 폼으로 돌아가서 오류 메시지 표시
log_message('error', $e->getMessage());
@@ -160,7 +155,29 @@ abstract class CommonController extends BaseController
return redirect()->back()->withInput()->with('error', $e->getMessage());
}
}
-
+ abstract protected function modify_form_process($uid): mixed;
+ final public function modify_form($uid): string
+ {
+ $action = __FUNCTION__;
+ try {
+ //초기화
+ $this->action_init_process($action);
+ $entity = $this->modify_form_process($uid);
+ $this->addViewDatas('entity', $entity);
+ $this->action_render_process($action);
+ // dd($this->getViewDatas());
+ } catch (\Exception $e) {
+ log_message('error', $e->getMessage());
+ $this->addViewDatas(self::ACTION_RESULT, 'error');
+ $this->addViewDatas(self::ACTION_MESSAGE, $e->getMessage());
+ //오류발생시 리디렉션 대신 폼 뷰를 다시 렌더링하도록 action_view_process 호출
+ }
+ return $this->action_result_process(
+ $this->getActionPaths(),
+ $action,
+ $this->getViewDatas()
+ );
+ }
//리스트관련
//조건절 처리
// protected function index_condition_process(): void
diff --git a/app/DTOs/UserDTO.php b/app/DTOs/UserDTO.php
index 0a37ab0..fcdf344 100644
--- a/app/DTOs/UserDTO.php
+++ b/app/DTOs/UserDTO.php
@@ -7,10 +7,11 @@ class UserDTO extends CommonDTO
public ?int $uid = null;
public ?string $id = null;
public ?string $passwd = null;
- public ?string $passwd_confirm = null;
+ public ?string $confirmpassword = null;
+ public ?string $name = null;
public ?string $email = null;
public ?string $mobile = null;
- public ?string $role = null;
+ public ?string $role = null;
public ?string $status = null;
public function __construct(array $datas = [])
@@ -18,7 +19,12 @@ class UserDTO extends CommonDTO
parent::__construct();
foreach ($datas as $key => $value) {
if (property_exists($this, $key)) {
- $this->{$key} = $value;
+ if ($key === 'role' && is_array($value)) {
+ // 배열일 경우, 쉼표로 구분된 문자열로 변환하여 저장
+ $this->role = implode(DEFAULTS["DELIMITER_ROLE"], $value);
+ } else {
+ $this->{$key} = $value;
+ }
}
}
}
@@ -28,7 +34,8 @@ class UserDTO extends CommonDTO
'uid' => $this->uid,
'id' => $this->id,
'passwd' => $this->passwd,
- 'passwd_confirm' => $this->passwd_confirm,
+ 'confirmpassword' => $this->confirmpassword,
+ 'name' => $this->name,
'email' => $this->email,
'mobile' => $this->mobile,
'role' => $this->role,
diff --git a/app/Entities/UserEntity.php b/app/Entities/UserEntity.php
index 5fdce70..57403ce 100644
--- a/app/Entities/UserEntity.php
+++ b/app/Entities/UserEntity.php
@@ -9,18 +9,38 @@ class UserEntity extends CommonEntity
{
const PK = Model::PK;
const TITLE = Model::TITLE;
- const DEFAULT_STATUS = STATUS['AVAILABLE'];
+ const DEFAULT_STATUS = STATUS['AVAILABLE'];
+
+ // 💡 1. $casts 속성 추가:
+ // DB에 JSON 형태로 저장된 'role' 컬럼을 PHP에서 배열로 자동 변환합니다.
+ protected $casts = [
+ 'role' => 'json-array',
+ ];
public function getID(): string
{
- return $this->attributes['id'];
+ return (string) $this->attributes['id'];
}
+
public function getPassword(): string
{
return $this->attributes['passwd'];
}
- public function getRole(): string
+ // $formDatas['passwd']에 평문 비밀번호가 들어있으면,
+ //Model->insert시 Entity 생성자($formDatas)가 setPasswd()를 자동으로 호출합니다.
+ public function setPasswd(string $password)
{
- return $this->attributes['role'];
+ $this->attributes['passwd'] = password_hash($password, PASSWORD_BCRYPT);
+ }
+ /**
+ * 사용자의 역할을 배열 형태로 반환합니다.
+ * $casts에 의해 DB에서 읽어올 때 이미 배열로 변환되어 있습니다.
+ * @return array
+ */
+ public function getRole(): array
+ {
+ // 💡 2. 반환 타입을 array로 변경하고,
+ // null일 경우를 대비해 빈 배열을 반환하도록 처리합니다.
+ return $this->attributes['role'] ?? [];
}
}
diff --git a/app/Helpers/UserHelper.php b/app/Helpers/UserHelper.php
index 9604921..b849a02 100644
--- a/app/Helpers/UserHelper.php
+++ b/app/Helpers/UserHelper.php
@@ -15,6 +15,29 @@ class UserHelper extends CommonHelper
case 'confirmpassword':
$form = form_password($field, "", [...$extras]);
break;
+ case 'role':
+ // 💡 $value는 수정 폼 로드시 현재 Entity의 'role' 배열입니다.
+ // (old() 값이 있다면 old() 배열이 됩니다.)
+ $currentRoles = is_array($value) ? $value : [];
+ // 사용 가능한 모든 역할 목록 (Service 등에서 가져온다고 가정)
+ $allRoles = $viewDatas['control']['formOptions']['role'] ?? ROLE['USER'];
+ $form = '';
+ // 2. 각 역할에 대한 체크박스를 순회하며 생성
+ foreach ($allRoles as $roleValue => $roleLabel) {
+
+ // $roleLabel이 배열이 아닌 문자열만 있는 경우를 대비
+ if (is_int($roleValue)) {
+ $roleValue = $roleLabel;
+ $roleLabel = ucfirst($roleValue);
+ }
+ $checked = in_array($roleValue, $currentRoles);
+ // 3. name="role[]" 형태로 배열 데이터 전송 준비
+ $form .= '';
+ }
+ break;
default:
$form = parent::getFieldForm($field, $value, $viewDatas, $extras);
break;
diff --git a/app/Language/en/User.php b/app/Language/en/User.php
index 319c7e3..42a5777 100644
--- a/app/Language/en/User.php
+++ b/app/Language/en/User.php
@@ -16,12 +16,12 @@ return [
'deleted_at' => "삭제일",
],
"ROLE" => [
- "manager" => "관리자",
- "cloudflare" => "Cloudflare관리자",
- "firewall" => "firewall관리자",
- "security" => "보안관리자",
- "director" => "감독자",
- "master" => "마스터",
+ ROLE['USER']["MANAGER"] => "관리자",
+ ROLE['USER']["CLOUDFLARE"] => "Cloudflare관리자",
+ ROLE['USER']["FIREWALL"] => "firewall관리자",
+ ROLE['USER']["SECURITY"] => "보안관리자",
+ ROLE['USER']["DIRECTOR"] => "감독자",
+ ROLE['USER']["MASTER"] => "마스터",
],
"STATUS" => [
STATUS['AVAILABLE'] => "사용중",
diff --git a/app/Models/CommonModel.php b/app/Models/CommonModel.php
index 3835201..bd29a71 100644
--- a/app/Models/CommonModel.php
+++ b/app/Models/CommonModel.php
@@ -64,149 +64,104 @@ abstract class CommonModel extends Model
{
return $this->useAutoIncrement;
}
- //Primary Key로 uuid를 사용시 해당 모델에 아래 변수 반드시 추가 필요
- // protected $useAutoIncrement = false;
- // protected $beforeInsert = ['generateUUID'];
- // allowedFields에는 PK넣으면 않됨, Column Type: CHAR(36)
- //
- final protected function generateUUID(): string
+ /**
+ * Entity 삽입을 처리하고, $useAutoIncrement가 false일 경우 UUID를 자동으로 생성합니다.
+ * @param object|array $data Entity 객체 또는 데이터 배열
+ * @param bool $returnID 삽입 후 ID를 반환할지 여부 (자동 증가 키일 경우 true)
+ * @return bool|int|string
+ */
+ public function insert($data = null, bool $returnID = true)
{
- $data = random_bytes(16);
- // UUID version 4 (random)
- $data[6] = chr(ord($data[6]) & 0x0f | 0x40); // version 0100
- $data[8] = chr(ord($data[8]) & 0x3f | 0x80); // variant 10
- return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
- }
- public function getFormRule(string $action, string $field): string
- {
- if (is_array($field)) {
- throw new \Exception(__FUNCTION__ . "=> field가 array 입니다.\n" . var_export($field, true));
+ // 1. 공통 전처리 로직 (예: 모든 모델에 대한 로그 기록, 공통 필드 설정 등)
+ $data = $this->preProcessData($data);
+
+ // 2. PK 자동 할당 로직
+ if (!$this->useAutoIncrement) {
+ $data = $this->ensurePrimaryKey($data);
}
- switch ($field) {
- case $this->getPKField():
- // 수동입력인 경우
- if (!$this->useAutoIncrement) {
- $rule = "required|regex_match[/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/]";
- $rule .= in_array($action, ["create", "create_form"]) ? "|is_unique[" . $this->getTable() . "." . $field . "]" : "";
- } else {
- $rule = "required|numeric";
- }
- break;
- case $this->getTitleField():
- $rule = "required|trim|string";
- break;
- case "code":
- // a-zA-Z → 영문 대소문자,0-9 → 숫자,가-힣 → 한글 완성형,\- → 하이픈
- $rule = "required|regex_match[/^[a-zA-Z0-9가-힣\-]+$/]|min_length[4]|max_length[20]";
- $rule .= in_array($action, ["create", "create_form"]) ? "|is_unique[" . $this->getTable() . "." . $field . "]" : "";
- break;
- case 'picture':
- $rule = "is_image[{$field}]|mime_in[{$field},image/jpg,image/jpeg,image/gif,image/png,image/webp]|max_size[{$field},300]|max_dims[{$field},2048,768]";
- break;
- case "updated_at":
- case "created_at":
- case "deleted_at":
- $rule = "permit_empty|valid_date";
- break;
- default:
- $rule = "permit_empty|trim|string";
- break;
- }
- return $rule;
- }
- protected function convertFormDatas(string $action, string $field, array $formDatas): mixed
- {
- // 필드 값 존재 여부 확인
- $value = array_key_exists($field, $formDatas) ? $formDatas[$field] : null;
- switch ($field) {
- case $this->getPKField():
- // 수동입력인 경우
- if (!$this->useAutoIncrement) {
- $randomBytes = bin2hex(random_bytes(32));
- $value = sprintf(
- '%08s-%04s-%04x-%04x-%12s',
- substr($randomBytes, 0, 8),
- substr($randomBytes, 8, 4),
- substr($randomBytes, 12, 4),
- substr($randomBytes, 16, 4),
- substr($randomBytes, 20, 12)
- );
- }
- break;
- case "editor":
- case "detail":
- case "content":
- case "discription":
- case "history":
- if ($value !== '' && $value !== null) {
- $value = htmlentities($value, ENT_QUOTES, 'UTF-8');
- }
- break;
- }
- return $value;
- }
- //기본 기능
- public function create(array $formDatas): mixed
- {
- $convertedFormDatas = [];
- foreach ($this->allowedFields as $field) {
- $value = $this->convertFormDatas(
- __FUNCTION__,
- $field,
- $formDatas
- );
- if ($value !== '' && $value !== null) {
- $convertedFormDatas[$field] = $value;
- }
- }
- // 최종 저장 시 오류 발생하면
- // dd($convertedFormDatas);
- if (!$this->save($convertedFormDatas)) {
- $message = sprintf(
- "\n------%s 오류-----\n%s\n%s\n------------------------------\n",
- __METHOD__,
- var_export($this->errors(), true),
- $this->getLastQuery()
- );
- log_message('error', $message);
- throw new \Exception($message);
- }
- //Model별 returntype형의 Entity 호출
- if (!class_exists($this->returnType)) {
- throw new \RuntimeException(__METHOD__ . "에서 returnType: {$this->returnType}이 정의되지 않았습니다.");
- }
- $entity = new $this->returnType($convertedFormDatas);
- // primaryKey가 자동입력이면
+
+ // 3. BaseModel의 insert 호출
+ // 문자열 PK일 경우, $returnID를 false로 넘겨서 ID 반환을 강제하지 않는 것이 좋습니다.
+ $baseReturn = parent::insert($data, $this->useAutoIncrement);
+
+ // 4. 후처리 및 반환
if ($this->useAutoIncrement) {
- $pkField = $this->getPKField();
- $entity->$pkField = $this->getInsertID();
+ // 정수 PK: BaseModel이 반환한 새로운 ID를 그대로 반환
+ return $baseReturn;
+ } else {
+ // 문자열 PK: BaseModel의 insert는 성공 시 true/1을 반환. true로 통일하여 반환
+ return $baseReturn !== false;
}
- return $entity;
}
- public function modify(mixed $entity, array $formDatas): mixed
+
+ /**
+ * Entity나 데이터 배열을 받아서 필요하다면 Primary Key를 생성하여 할당합니다.
+ * @param object|array $data
+ * @return object|array
+ */
+ protected function ensurePrimaryKey($data)
{
- //수정일추가
- $formDatas['updated_at'] = date("Y-m-d H:i:s");
- foreach (array_keys($formDatas) as $field) {
- $value = $this->convertFormDatas(
- __FUNCTION__,
- $field,
- $formDatas
- );
- if ($entity->$field !== $value) {
- $entity->$field = $value;
+ $pkName = $this->primaryKey;
+ $entityData = is_object($data) ? $data->attributes : (array) $data;
+
+ // PK가 설정되어 있지 않은 경우
+ if (empty($entityData[$pkName])) {
+ $newUuid = service('uuid')->uuid4()->toString();
+
+ if (is_object($data)) {
+ // Entity 객체일 경우 __set()을 통해 Setter를 호출하며 할당
+ $data->$pkName = $newUuid;
+ } else {
+ // 배열일 경우 배열에 할당
+ $data[$pkName] = $newUuid;
}
}
- // 최종 저장 시 오류 발생하면
- if (!$this->save($entity)) {
- $message = sprintf(
- "\n------%s 오류-----\n%s\n------------------------------\n",
- __METHOD__,
- var_export($this->errors(), true)
- );
- log_message('error', $message);
- throw new \Exception($message);
- }
- return $entity;
+ return $data;
+ }
+
+ /**
+ * 모든 삽입/수정 작업 전 공통 로직을 처리하는 확장 지점.
+ */
+ protected function preProcessData($data)
+ {
+ // 예: $data['data']['updated_by'] = session('user_id');
+ return $data;
+ }
+
+ // ******************************************************
+ // 2. UPDATE 및 DELETE 확장
+ // ******************************************************
+
+ /**
+ * BaseModel의 update 메서드를 호출하기 전/후에 공통 로직을 적용하는 확장 지점.
+ * @param array|int|string|null $id
+ * @param array|object|null $data
+ * @return bool
+ */
+ public function update($id = null, $data = null): bool
+ {
+ // 1. 업데이트 전 공통 로직 (preUpdate 이벤트)
+ $data = $this->preProcessData($data);
+
+ // 2. BaseModel의 update 호출
+ return parent::update($id, $data);
+
+ // 3. 업데이트 후 공통 로직 (postUpdate 이벤트)
+ }
+
+ /**
+ * BaseModel의 delete 메서드를 호출하기 전/후에 공통 로직을 적용하는 확장 지점.
+ * @param array|int|string|null $id
+ * @param bool $purge
+ * @return bool
+ */
+ public function delete($id = null, bool $purge = false): bool
+ {
+ // 1. 삭제 전 공통 로직 (preDelete 이벤트)
+
+ // 2. BaseModel의 delete 호출
+ return parent::delete($id, $purge);
+
+ // 3. 삭제 후 공통 로직 (postDelete 이벤트)
}
}
diff --git a/app/Models/TrafficModel.php b/app/Models/TrafficModel.php
index eb642ee..b17a35e 100644
--- a/app/Models/TrafficModel.php
+++ b/app/Models/TrafficModel.php
@@ -40,45 +40,6 @@ class TrafficModel extends CommonModel
parent::__construct();
}
public function getFormRule(string $action, string $field): string
- {
- if (is_array($field)) {
- throw new \Exception(__FUNCTION__ . "=> field가 array 입니다.\n" . var_export($field, true));
- }
- switch ($field) {
- case "user_uid":
- case "clientinfo_uid":
- case "serverinfo_uid":
- case "amount":
- case "rack":
- case "line":
- $rule = "required|numeric";
- break;
- case "sale":
- case "payment_uid":
- $rule = "permit_empty|numeric";
- break;
- case "site":
- case "location":
- case "status":
- $rule = "required|trim|string";
- break;
- case "billing_at":
- case "start_at":
- $rule = "required|valid_date";
- break;
- case "end_at":
- $rule = "permit_empty|valid_date";
- break;
- case 'title':
- case "history":
- $rule = "permit_empty|trim|string";
- break;
- default:
- $rule = parent::getFormRule($action, $field);
- break;
- }
- return $rule;
- }
//입력전 코드처리
final public function create(array $formDatas): ServiceEntity
{
diff --git a/app/Models/UserModel.php b/app/Models/UserModel.php
index 99ffc49..c3c623a 100644
--- a/app/Models/UserModel.php
+++ b/app/Models/UserModel.php
@@ -27,50 +27,4 @@ class UserModel extends CommonModel
{
parent::__construct();
}
- public function getFormRule(string $action, string $field): string
- {
- if (is_array($field)) {
- throw new \Exception(__FUNCTION__ . "=> field가 array 입니다.\n" . var_export($field, true));
- }
- switch ($field) {
- case "id":
- $rule = "required|trim|min_length[4]|max_length[20]";
- $rule .= in_array($action, ["create", "create_form"]) ? "|is_unique[{$this->table}.{$field}]" : "";
- break;
- case "passwd":
- $rule = in_array($action, ["create", "create_form"]) ? "required|trim|string" : "permit_empty|trim|string";
- break;
- case "confirmpassword":
- $rule = in_array($action, ["create", "create_form"]) ? "required|trim|string|matches[passwd]" : "permit_empty|trim|string|matches[passwd]";
- break;
- case "email":
- $rule = "required|trim|valid_email";
- $rule .= in_array($action, ["create", "create_form"]) ? "|is_unique[{$this->table}.{$field}]" : "";
- break;
- case "role":
- $rule = "required|trim|string";
- break;
- default:
- $rule = parent::getFormRule($action, $field);
- break;
- }
- return $rule;
- }
- protected function convertFormDatas(string $action, string $field, array $formDatas): mixed
- {
- // 필드 값 존재 여부 확인
- $value = array_key_exists($field, $formDatas) ? $formDatas[$field] : null;
- switch ($field) {
- case "passwd":
- $value = password_hash($value, PASSWORD_DEFAULT);
- break;
- case "confirmpassword":
- $value = password_hash($value, PASSWORD_DEFAULT);
- break;
- default:
- $value = parent::convertFormDatas($action, $field, $formDatas);
- break;
- }
- return $value;
- }
}
diff --git a/app/Services/UserService.php b/app/Services/UserService.php
index 6935a15..414f9b6 100644
--- a/app/Services/UserService.php
+++ b/app/Services/UserService.php
@@ -50,13 +50,31 @@ class UserService extends CommonService
{
// DTO 객체를 배열로 변환하여 검증기에 전달
$formDatas = (array) $dto;
+ // dd($formDatas);
if (!service('validation')->setRules($this->getFormRules(__FUNCTION__))->run($formDatas)) {
- // 검증 실패 시, ValidationException을 던집니다.
throw new ValidationException(implode("\n", service('validation')->getErrors()));
}
- $formDatas['role'] = implode(DEFAULTS["DELIMITER_ROLE"], $formDatas['role']);
- $entity = $this->model->create($formDatas);
- return $entity;
+ $entity = new UserEntity($formDatas);
+ $result = $this->model->insert($entity);
+ if (!$result) {
+ throw new \Exception("{$entity->getTitle()} 등록 중 DB 오류가 발생하였습니다.");
+ }
+ // 💡 PK 타입에 따른 최종 Entity 반환 로직 분기
+ if ($this->model->useAutoIncrement) {
+ // 1. 정수 PK인 경우: $result는 ID 번호이므로, 저장된 레코드를 DB에서 다시 조회합니다.
+ // (자동 설정된 필드(created_at 등)를 포함하기 위해 재조회)
+ $savedEntity = $this->model->find($result);
+ } else {
+ // 2. 문자열 PK인 경우: $result는 true/1 이므로,
+ // 이미 ID가 설정된 $entity 객체를 반환하거나, ID로 재조회합니다.
+ // Entity에 모든 필드(created_at 등)가 설정되지 않았다면 재조회가 안전합니다.
+ $pkValue = $entity->{$this->model->primaryKey};
+ $savedEntity = $this->model->find($pkValue);
+ }
+ if (!$savedEntity) {
+ throw new \Exception("등록된 데이터를 찾을 수 없습니다.");
+ }
+ return $savedEntity;
}
// protected function modify_process(mixed $entity, array $formDatas): UserEntity
// {
diff --git a/app/Views/admin/user/modify_form.php b/app/Views/admin/user/modify_form.php
index 6006c52..e4af212 100644
--- a/app/Views/admin/user/modify_form.php
+++ b/app/Views/admin/user/modify_form.php
@@ -1,22 +1,23 @@
= $this->extend(LAYOUTS[$viewDatas['control']['layout']]['path']) ?>
= $this->section('content') ?>
-getHelper()->alertTrait($error) ?>
+alertTrait(session('error')) ?>
= $this->include("templates/{$viewDatas['control']['layout']}/form_content_top"); ?>
- = form_open(current_url(), ['id' => 'action_form', ...$viewDatas['forms']['attributes']], $viewDatas['forms']['hiddens']) ?>
+ = form_open(current_url(), $viewDatas['forms']['attributes'], $viewDatas['forms']['hiddens']) ?>
= $this->include("templates/{$viewDatas['control']['layout']}/form_content_bottom"); ?>