dbmsv4 init...4

This commit is contained in:
최준흠 2026-01-06 13:28:43 +09:00
parent cba596fa0a
commit 298e498cf5
4 changed files with 104 additions and 47 deletions

View File

@ -79,8 +79,8 @@ class ClientEntity extends CustomerEntity
}
// 2-b. JSON이 아니면 CSV로 가정하고 변환
$parts = explode(DEFAULTS["DELIMITER_ROLE"], $role);
// 각 요소의 불필요한 공백과 따옴표 제거
$cleanedRoles = array_map(fn($item) => trim($item, " \t\n\r\0\x0B\""), $parts);
// 각 요소의 불필요한 공백과 따옴표 제거. null 가능성에 대비해 string 형변환 추가
$cleanedRoles = array_map(fn($item) => trim((string) ($item ?? ''), " \t\n\r\0\x0B\""), $parts);
return array_filter($cleanedRoles);
}
// 3. 변환에 실패했거나 데이터가 없는 경우 빈 배열 반환
@ -109,8 +109,8 @@ class ClientEntity extends CustomerEntity
elseif (is_array($role)) {
$roleArray = $role;
}
// 배열의 각 요소를 정리
$cleanedRoles = array_map(fn($item) => trim($item, " \t\n\r\0\x0B\""), $roleArray);
// 배열의 각 요소를 정리. null이나 scalar 타입이 섞여있을 경우에 대비해 string으로 명시적 형변환 후 trim 수행
$cleanedRoles = array_map(fn($item) => trim((string) ($item ?? ''), " \t\n\r\0\x0B\""), $roleArray);
$roleArray = array_filter($cleanedRoles);
// 최종적으로 DB에 삽입될 단일 CSV 문자열로 변환하여 저장합니다.
$this->attributes['role'] = implode(DEFAULTS["DELIMITER_ROLE"], $roleArray);

View File

@ -57,8 +57,8 @@ class UserEntity extends CommonEntity
}
// 2-b. JSON이 아니면 CSV로 가정하고 변환
$parts = explode(DEFAULTS["DELIMITER_ROLE"], $role);
// 각 요소의 불필요한 공백과 따옴표 제거
$cleanedRoles = array_map(fn($item) => trim($item, " \t\n\r\0\x0B\""), $parts);
// 각 요소의 불필요한 공백과 따옴표 제거. null 가능성에 대비해 string 형변환 추가
$cleanedRoles = array_map(fn($item) => trim((string) ($item ?? ''), " \t\n\r\0\x0B\""), $parts);
return array_filter($cleanedRoles);
}
// 3. 변환에 실패했거나 데이터가 없는 경우 빈 배열 반환
@ -95,8 +95,8 @@ class UserEntity extends CommonEntity
elseif (is_array($role)) {
$roleArray = $role;
}
// 배열의 각 요소를 정리
$cleanedRoles = array_map(fn($item) => trim($item, " \t\n\r\0\x0B\""), $roleArray);
// 배열의 각 요소를 정리. null이나 scalar 타입이 섞여있을 경우에 대비해 string으로 명시적 형변환 후 trim 수행
$cleanedRoles = array_map(fn($item) => trim((string) ($item ?? ''), " \t\n\r\0\x0B\""), $roleArray);
$roleArray = array_filter($cleanedRoles);
// 최종적으로 DB에 삽입될 단일 CSV 문자열로 변환하여 저장합니다.
$this->attributes['role'] = implode(DEFAULTS["DELIMITER_ROLE"], $roleArray);

View File

@ -132,7 +132,7 @@ abstract class CommonForm
* @param array $formDatas 검증할 데이터
* @throws RuntimeException
*/
final public function validate(array $formDatas): void
final public function validate(array &$formDatas): void
{
if ($this->_validation === null) {
throw new RuntimeException(static::class . '->' . __FUNCTION__ . "에서 오류발생: Validation 서비스가 초기화되지 않았습니다.");
@ -141,12 +141,23 @@ abstract class CommonForm
try {
$dynamicRules = [];
// 0. Ensure all scalar inputs are strings to prevent trim() error on PHP 8.1+
foreach ($formDatas as $key => $value) {
if (is_scalar($value) && !is_string($value)) {
$formDatas[$key] = (string) $value;
// 0. Ensure all scalar/null inputs are strings to prevent trim() error on PHP 8.1+
$castToString = function (&$data, $path = '') use (&$castToString) {
foreach ($data as $key => &$value) {
$currentField = $path ? "{$path}.{$key}" : $key;
try {
if (is_array($value)) {
$castToString($value, $currentField);
} elseif ($value === null || (is_scalar($value) && !is_string($value))) {
$data[$key] = (string) ($value ?? '');
}
} catch (\Throwable $e) {
throw new RuntimeException("데이터 정제 중 오류 발생 (필드: {$currentField}): " . $e->getMessage());
}
}
};
$castToString($formDatas);
// 1. 현재 서비스의 필드 라벨 정보 로드 (언어 파일 기반)
$formFields = $this->getFormFields();
$formRules = $this->getFormRules();
@ -156,6 +167,7 @@ abstract class CommonForm
}
foreach ($formRules as $field => $rule) {
try {
// 2. 필드명과 규칙 추출
list($field, $rule) = $this->getValidationRule($field, $rule);
@ -176,28 +188,73 @@ abstract class CommonForm
'label' => $label,
'rules' => $rule
];
// 4.5. Ensure the field exists in formDatas to prevent trim(null) in the engine
if (!array_key_exists($field, $formDatas) && !str_contains($field, '.*')) {
$formDatas[$field] = '';
}
} catch (\Throwable $e) {
throw new RuntimeException("유효성 검사 규칙 준비 중 오류 발생 (필드: {$field}): " . $e->getMessage());
}
}
// 5. 검증 규칙 설정 (인자를 하나만 전달하여 설정 충돌 방지)
$this->_validation->setRules($dynamicRules);
// 6. 검증 실행
try {
if (!$this->_validation->run($formDatas)) {
// 한글 라벨이 적용된 에러 메시지들을 배열로 가져와 한 줄씩 합침
$errors = $this->_validation->getErrors();
throw new RuntimeException(implode("\n", $errors));
}
} catch (\TypeError $e) {
// TypeError(예: trim(null) 등) 발생 시 범인(field) 찾기 시도
$culpritField = "알 수 없음";
$culpritValue = "null";
foreach ($dynamicRules as $f => $r) {
if (str_contains($r['rules'], 'trim')) {
// 중첩 필드(.*)와 일반 필드 구분하여 null 체크
if (str_contains($f, '.*')) {
$parentKey = str_replace('.*', '', $f);
if (isset($formDatas[$parentKey]) && is_array($formDatas[$parentKey])) {
foreach ($formDatas[$parentKey] as $k => $v) {
if ($v === null) {
$culpritField = "{$parentKey}.{$k} ({$r['label']})";
break 2;
}
}
}
} else {
if (!isset($formDatas[$f]) || $formDatas[$f] === null) {
$culpritField = "{$f} ({$r['label']})";
break;
}
}
}
}
$errorMsg = $e->getMessage();
// "trim(): Argument #1 ($string) must be of type string, null given" 문구에서 Argument #1을 필드명으로 교체
$errorMsg = str_replace("Argument #1 (\$string)", "데이터(필드: {$culpritField}, 값: {$culpritValue})", $errorMsg);
throw new RuntimeException("검증 도중 타입 오류 발생: " . $errorMsg);
}
// 검증 성공 시 추가 로직 없이 종료
} catch (\Throwable $e) {
// 이미 RuntimeException으로 포장된 경우 그대로 던짐
if ($e instanceof RuntimeException) {
throw $e;
}
// 오류 발생 시 디버깅을 위해 로그 기록
log_message('debug', '--- Validation Error Detail ---');
log_message('debug', 'Rules: ' . var_export($this->getFormRules(), true));
log_message('debug', 'Data: ' . var_export($formDatas, true));
log_message('debug', 'Message: ' . $e->getMessage());
throw new RuntimeException($e->getMessage());
throw new RuntimeException("유효성 검사 중 시스템 오류 발생: " . $e->getMessage());
}
}

View File

@ -22,7 +22,7 @@ class ClientHelper extends CustomerHelper
break;
case 'role':
$currentRoles = is_array($value)
? array_map('strtolower', array_map('trim', $value))
? array_map('strtolower', array_map(fn($item) => trim((string) ($item ?? ''), " \t\n\r\0\x0B\""), $value))
: [];
$form = '';
//Form페이지에서는 맨앞에것 제외하기 위함