dbmsv4 init...5

This commit is contained in:
최준흠 2026-02-26 16:57:21 +09:00
parent 8cab3c4e94
commit 9b805c755d
6 changed files with 67 additions and 276 deletions

View File

@ -28,14 +28,19 @@ class ClientEntity extends CustomerEntity
return sprintf("%s/%s", $this->getSite(), $title ? $title : $this->getTitle());
}
public function getSite(): string
{
return $this->site;
}
public function getName(): string
{
return $this->name;
}
public function getSite(): string
public function getRole(): string
{
return $this->site;
return $this->role;
}
public function getAccountBalance(): int
@ -57,69 +62,4 @@ class ClientEntity extends CustomerEntity
{
return $this->history;
}
/**
* role을 배열로 반환
*/
public function getRole(): array
{
$role = $this->attributes['role'] ?? null;
if (is_array($role)) {
return array_values(array_filter($role, fn($v) => (string) $v !== ''));
}
if (is_string($role) && $role !== '') {
$decoded = json_decode($role, true);
if (json_last_error() === JSON_ERROR_NONE && is_array($decoded)) {
$clean = array_map(
fn($item) => trim((string) ($item ?? ''), " \t\n\r\0\x0B\""),
$decoded
);
return array_values(array_filter($clean, fn($v) => $v !== ''));
}
$parts = explode(DEFAULTS["DELIMITER_COMMA"], $role);
$clean = array_map(
fn($item) => trim((string) ($item ?? ''), " \t\n\r\0\x0B\""),
$parts
);
return array_values(array_filter($clean, fn($v) => $v !== ''));
}
return [];
}
/**
* role은 DB 저장용 CSV 문자열로 반환
*/
public function setRole($role): string
{
$roleArray = [];
if (is_string($role)) {
$clean = trim($role, " \t\n\r\0\x0B\"");
if ($clean !== '') {
$decoded = json_decode($clean, true);
if (json_last_error() === JSON_ERROR_NONE && is_array($decoded)) {
$roleArray = $decoded;
} else {
$roleArray = explode(DEFAULTS["DELIMITER_COMMA"], $clean);
}
}
} elseif (is_array($role)) {
$roleArray = $role;
} else {
$roleArray = [];
}
$cleaned = array_map(
fn($item) => trim((string) ($item ?? ''), " \t\n\r\0\x0B\""),
$roleArray
);
$roleArray = array_values(array_filter($cleaned, fn($v) => $v !== ''));
return implode(DEFAULTS["DELIMITER_COMMA"], $roleArray);
}
}

View File

@ -28,7 +28,10 @@ class UserEntity extends CommonEntity
{
return $this->passwd;
}
public function getRole(): string
{
return $this->role;
}
public function getEmail(): string
{
return $this->email;
@ -37,87 +40,4 @@ class UserEntity extends CommonEntity
{
return $this->mobile;
}
/**
* role을 "배열" 반환 (DB에는 CSV/JSON/배열 무엇이든 복구)
*/
public function getRole(): array
{
$role = $this->attributes['role'] ?? null;
if (is_array($role)) {
return array_values(array_filter($role, fn($v) => (string) $v !== ''));
}
if (is_string($role) && $role !== '') {
// JSON 시도
$decoded = json_decode($role, true);
if (json_last_error() === JSON_ERROR_NONE && is_array($decoded)) {
$clean = array_map(
fn($item) => trim((string) ($item ?? ''), " \t\n\r\0\x0B\""),
$decoded
);
return array_values(array_filter($clean, fn($v) => $v !== ''));
}
// CSV fallback
$parts = explode(DEFAULTS["DELIMITER_COMMA"], $role);
$clean = array_map(
fn($item) => trim((string) ($item ?? ''), " \t\n\r\0\x0B\""),
$parts
);
return array_values(array_filter($clean, fn($v) => $v !== ''));
}
return [];
}
/**
* CI4 뮤테이터: "return 값" attributes에 저장됨
* - 빈값이면 기존값 유지 (create에서 required면 validate에서 걸러짐)
*/
public function setPasswd($password): string
{
// null/'' 이면 기존값 유지
if (!is_string($password) || $password === '') {
return (string) ($this->attributes['passwd'] ?? '');
}
return password_hash($password, PASSWORD_BCRYPT);
}
/**
* role은 최종적으로 "CSV 문자열" 저장 (DB 안전)
*/
public function setRole($role): string
{
$roleArray = [];
if (is_string($role)) {
$clean = trim($role, " \t\n\r\0\x0B\"");
if ($clean !== '') {
// JSON 문자열 가능성도 있어서 먼저 JSON 시도
$decoded = json_decode($clean, true);
if (json_last_error() === JSON_ERROR_NONE && is_array($decoded)) {
$roleArray = $decoded;
} else {
$roleArray = explode(DEFAULTS["DELIMITER_COMMA"], $clean);
}
}
} elseif (is_array($role)) {
$roleArray = $role;
} else {
// 그 외 타입은 안전하게 빈값 처리
$roleArray = [];
}
$cleaned = array_map(
fn($item) => trim((string) ($item ?? ''), " \t\n\r\0\x0B\""),
$roleArray
);
$roleArray = array_values(array_filter($cleaned, fn($v) => $v !== ''));
// ✅ 무조건 문자열 반환 (빈 배열이면 '')
return implode(DEFAULTS["DELIMITER_COMMA"], $roleArray);
}
}

View File

@ -14,12 +14,10 @@ abstract class CommonModel extends Model
protected $useSoftDeletes = false;
protected $protectFields = true;
protected $allowedFields = [];
// $allowEmptyInserts = false (기본값): 삽입할 데이터가 전혀 없는 경우, CI4는 오류를 발생시키며 쿼리 실행을 막습니다. (보안 및 데이터 무결성 목적)
// $allowEmptyInserts = true: 삽입할 데이터가 없어도 INSERT INTO table_name () VALUES () 같은 빈 쿼리 실행을 허용합니다 (극히 드문 경우에 사용).
protected bool $allowEmptyInserts = false;
protected bool $updateOnlyChanged = true;
// protected $useEmptyStringIfNull = true; (기본값)
// 이 기본 설정 때문에 PHP의 null 값이 데이터베이스로 전달될 때 실제 SQL의 NULL 키워드가 아닌 **빈 문자열 ('')**로 변환되고 있습니다.
// 그리고 데이터베이스(MySQL 등)의 설정에 따라 빈 문자열이 업데이트 쿼리에서 무시되거나, 해당 컬럼의 기존 값이 유지되는 현상이 발생합니다.
@ -43,16 +41,15 @@ abstract class CommonModel extends Model
// Callbacks
protected $allowCallbacks = true;
protected $beforeInsert = ['emptyStringToNull']; //Field 값이 NULL일 경우 DB Default값 적용용
protected $beforeInsert = []; //Field 값이 NULL일 경우 DB Default값 적용용
protected $afterInsert = [];
protected $beforeUpdate = ['emptyStringToNull']; //Field 값이 NULL일 경우 DB Default값 적용용
protected $beforeUpdate = []; //Field 값이 NULL일 경우 DB Default값 적용용
protected $afterUpdate = [];
protected $beforeFind = [];
protected $afterFind = [];
protected $beforeDelete = [];
protected $afterDelete = [];
protected array $nullableFields = []; // 모델별로 override
protected function __construct()
{
parent::__construct();
@ -77,35 +74,4 @@ abstract class CommonModel extends Model
{
return $this->allowedFields;
}
protected function emptyStringToNull(array $data): array
{
if (!isset($data['data']) || !is_array($data['data'])) {
return $data;
}
// 공통 모델에서는 아무 필드도 강제하지 않음 (안전)
if (empty($this->nullableFields)) {
return $data;
}
foreach ($this->nullableFields as $field) {
if (array_key_exists($field, $data['data'])) {
$v = $data['data'][$field];
// 문자열이면 trim 후, 빈문자면 null
if (is_string($v)) {
$v = trim($v);
$data['data'][$field] = ($v === '') ? null : $v;
} else {
// 문자열이 아닌데도 '' 같은 케이스 방어 (거의 없음)
if ($v === '')
$data['data'][$field] = null;
}
}
}
return $data;
}
}

View File

@ -246,7 +246,7 @@ abstract class CommonService
}
//Action 작업시 field에따른 Hook처리(각 Service에서 override);
protected function validation_fieldhook(string $field, $value, array $formDatas): array
protected function fieldhook_process(string $field, $value, array $formDatas): array
{
return $formDatas;
}
@ -256,21 +256,21 @@ abstract class CommonService
{
try {
$actionForm = $this->getActionForm();
if ($actionForm instanceof CommonForm) {
$actionForm->action_init_process('create', $formDatas);
// log_message('debug', 'BEFORE hook CREATE FORMDATA:' . print_r($formDatas ?? null, true));
foreach ($formDatas as $field => $value) {
$formDatas = $this->validation_fieldhook($field, $value, $formDatas);
}
// log_message('debug', 'AFTER hook CREATE FORMDATA:' . print_r($formDatas ?? null, true));
$actionForm->validate($formDatas); // ✅ 여기서 검증
if (!$actionForm instanceof CommonForm) {
throw new RuntimeException(static::class . '->' . __FUNCTION__ . "에서 오류발생: actionForm이 정의되지 않았습니다.");
}
$actionForm->action_init_process('create', $formDatas);
$actionForm->validate($formDatas); // ✅ 여기서 검증
// 검증 통과 후 엔티티 반영용
foreach ($formDatas as $field => $value) {
$formDatas = $this->fieldhook_process($field, $value, $formDatas);
}
$entityClass = $this->getEntityClass();
$entity = new $entityClass($formDatas);
if (!$entity instanceof $entityClass) {
throw new RuntimeException("Return Type은 {$entityClass}만 가능");
}
$entity->fill($formDatas);
return $this->save_process($entity);
} catch (FormValidationException $e) {
throw $e; // ✅ 감싸지 말고 그대로
@ -288,10 +288,6 @@ abstract class CommonService
}
//수정용
protected function modify_process_fieldhook(array $formDatas): array
{
return $formDatas;
}
protected function modify_process($entity, array $formDatas): CommonEntity
{
try {
@ -300,17 +296,12 @@ abstract class CommonService
throw new RuntimeException(static::class . '->' . __FUNCTION__ . "에서 오류발생: actionForm이 정의되지 않았습니다.");
}
$actionForm->action_init_process('modify', $formDatas);
log_message('debug', 'BEFORE hook MODIFY FORMDATA:' . print_r($formDatas ?? null, true));
foreach ($formDatas as $field => $value) {
$formDatas = $this->validation_fieldhook($field, $value, $formDatas);
}
log_message('debug', 'AFTER hook MODIFY FORMDATA:' . print_r($formDatas ?? null, true));
$actionForm->validate($formDatas); // ✅ 여기서 검증
// 검증 통과 후 엔티티 반영
$formDatas = $this->modify_process_fieldhook($formDatas);
log_message('debug', 'BEFORE MODIFY fill Entity:' . print_r($formDatas ?? null, true));
foreach ($formDatas as $field => $value) {
$formDatas = $this->fieldhook_process($field, $value, $formDatas);
}
$entity->fill($formDatas);
log_message('debug', 'AFTER MODIFY fill Entity:' . print_r($entity ?? null, true));
if (!$entity->hasChanged()) {
return $entity;
}

View File

@ -38,50 +38,44 @@ class ClientService extends CustomerService
parent::setOrderBy($field, $value);
}
protected function validation_fieldhook(string $field, $value, array $formDatas): array
protected function fieldhook_process(string $field, $value, array $formDatas): array
{
switch ($field) {
case 'role':
if (is_string($value)) {
$value = ($value === '') ? [] : explode(DEFAULTS["DELIMITER_COMMA"], $value);
} elseif (!is_array($value)) {
$value = [];
}
$value = array_values(array_filter(array_map(
fn($v) => trim((string) ($v ?? ''), " \t\n\r\0\x0B\""),
$value
)));
$formDatas[$field] = $value;
$arr = is_array($value) ? $value : explode(',', (string) $value);
$arr = array_values(array_filter(array_map('trim', $arr)));
sort($arr);
$formDatas[$field] = implode(',', $arr);
break;
default:
$formDatas = parent::validation_fieldhook($field, $value, $formDatas);
$formDatas = parent::fieldhook_process($field, $value, $formDatas);
break;
}
return $formDatas;
}
protected function modify_process_fieldhook(array $formDatas): array
{
// 1) DB 컬럼 아닌 값 제거
unset($formDatas['confirmpassword']);
// protected function modify_process_fieldhook(array $formDatas): array
// {
// // 1) DB 컬럼 아닌 값 제거
// unset($formDatas['confirmpassword']);
// 2) role은 무조건 문자열로
if (array_key_exists('role', $formDatas)) {
$arr = is_array($formDatas['role'])
? $formDatas['role']
: explode(',', (string) $formDatas['role']);
// // 2) role은 무조건 문자열로
// if (array_key_exists('role', $formDatas)) {
// $arr = is_array($formDatas['role'])
// ? $formDatas['role']
// : explode(',', (string) $formDatas['role']);
$arr = array_values(array_filter(array_map('trim', $arr)));
sort($arr);
$formDatas['role'] = implode(',', $arr);
}
// $arr = array_values(array_filter(array_map('trim', $arr)));
// sort($arr);
// $formDatas['role'] = implode(',', $arr);
// }
// 3) passwd는 빈 값이면 업데이트 제외 (원하면)
if (array_key_exists('passwd', $formDatas) && $formDatas['passwd'] === '') {
unset($formDatas['passwd']);
}
// // 3) passwd는 빈 값이면 업데이트 제외 (원하면)
// if (array_key_exists('passwd', $formDatas) && $formDatas['passwd'] === '') {
// unset($formDatas['passwd']);
// }
return $formDatas;
}
// return $formDatas;
// }
public function history(string|int $uid, string $history): CommonEntity
{
return $this->dbTransaction(function () use ($uid, $history) {

View File

@ -26,51 +26,31 @@ class UserService extends CommonService
{
return $entity;
}
protected function validation_fieldhook(string $field, $value, array $formDatas): array
protected function fieldhook_process(string $field, $value, array $formDatas): array
{
switch ($field) {
case 'role':
if (is_string($value)) {
$value = ($value === '') ? [] : explode(DEFAULTS["DELIMITER_COMMA"], $value);
} elseif (!is_array($value)) {
$value = [];
$arr = is_array($value) ? $value : explode(',', (string) $value);
$arr = array_values(array_filter(array_map('trim', $arr)));
sort($arr);
$formDatas[$field] = implode(',', $arr);
break;
case 'passwd':
if ($formDatas[$field] !== '') {
$formDatas[$field] = password_hash($value, PASSWORD_BCRYPT);
} else {
unset($formDatas[$field]);
}
$value = array_values(array_filter(array_map(
fn($v) => trim((string) ($v ?? ''), " \t\n\r\0\x0B\""),
$value
)));
$formDatas[$field] = $value;
break;
case 'confirmpassword':
unset($formDatas['confirmpassword']);
break;
default:
$formDatas = parent::validation_fieldhook($field, $value, $formDatas);
$formDatas = parent::fieldhook_process($field, $value, $formDatas);
break;
}
return $formDatas;
}
protected function modify_process_fieldhook(array $formDatas): array
{
// 1) DB 컬럼 아닌 값 제거
unset($formDatas['confirmpassword']);
// 2) role은 무조건 문자열로
if (array_key_exists('role', $formDatas)) {
$arr = is_array($formDatas['role'])
? $formDatas['role']
: explode(',', (string) $formDatas['role']);
$arr = array_values(array_filter(array_map('trim', $arr)));
sort($arr);
$formDatas['role'] = implode(',', $arr);
}
// 3) passwd는 빈 값이면 업데이트 제외 (원하면)
if (array_key_exists('passwd', $formDatas) && $formDatas['passwd'] === '') {
unset($formDatas['passwd']);
}
return $formDatas;
}
//List 검색용
//FormFilter 조건절 처리
public function setFilter(string $field, mixed $filter_value): void