feat: field_key 시스템 필드 예약어 검증 추가
- SystemFields 상수 클래스 생성 (app/Constants/) - source_table 기반 테이블별 예약어 관리 - products/materials 테이블 고정 컬럼 정의 - 공통 시스템 컬럼 포함 - ItemFieldService 수정 - validateFieldKeyUnique에 시스템 필드 검증 추가 - source_table 미지정시 그룹 전체 예약어 체크 (안전 모드) - 에러 메시지 추가 (error.field_key_reserved) - 작업 문서 추가 (docs/specs/item-master-field-key-validation.md)
This commit is contained in:
@@ -2,11 +2,13 @@
|
||||
|
||||
namespace App\Services\ItemMaster;
|
||||
|
||||
use App\Constants\SystemFields;
|
||||
use App\Models\ItemMaster\EntityRelationship;
|
||||
use App\Models\ItemMaster\ItemField;
|
||||
use App\Services\Service;
|
||||
use App\Traits\LockCheckTrait;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
class ItemFieldService extends Service
|
||||
@@ -61,14 +63,64 @@ public function storeIndependent(array $data): ItemField
|
||||
'created_by' => $userId,
|
||||
]);
|
||||
|
||||
// 2. field_key 설정 ({ID}_{field_key} 형식)
|
||||
// 2. field_key 설정 (시스템 필드 + 중복 검증 후 저장)
|
||||
if (! empty($data['field_key'])) {
|
||||
$field->update(['field_key' => $field->id.'_'.$data['field_key']]);
|
||||
$sourceTable = $data['source_table'] ?? null;
|
||||
$groupId = $data['group_id'] ?? 1;
|
||||
$this->validateFieldKeyUnique($data['field_key'], $tenantId, $sourceTable, $groupId);
|
||||
$field->update(['field_key' => $data['field_key']]);
|
||||
}
|
||||
|
||||
return $field;
|
||||
}
|
||||
|
||||
/**
|
||||
* field_key 유효성 검증 (시스템 필드 + 중복 체크)
|
||||
*
|
||||
* @param string|null $sourceTable 소스 테이블 (products, materials), null이면 그룹 전체 예약어 체크
|
||||
* @param int $groupId 그룹 ID (1=품목관리), sourceTable이 null일 때 사용
|
||||
*
|
||||
* @throws ValidationException
|
||||
*/
|
||||
private function validateFieldKeyUnique(
|
||||
string $fieldKey,
|
||||
int $tenantId,
|
||||
?string $sourceTable = null,
|
||||
int $groupId = 1,
|
||||
?int $excludeId = null
|
||||
): void {
|
||||
// 1. 시스템 필드(예약어) 체크
|
||||
if ($sourceTable) {
|
||||
// source_table이 지정된 경우: 해당 테이블의 예약어만 체크
|
||||
if (SystemFields::isReserved($fieldKey, $sourceTable)) {
|
||||
throw ValidationException::withMessages([
|
||||
'field_key' => [__('error.field_key_reserved', ['field_key' => $fieldKey])],
|
||||
]);
|
||||
}
|
||||
} else {
|
||||
// source_table이 없는 경우: 그룹 내 모든 테이블의 예약어 체크 (안전 모드)
|
||||
if (SystemFields::isReservedInGroup($fieldKey, $groupId)) {
|
||||
throw ValidationException::withMessages([
|
||||
'field_key' => [__('error.field_key_reserved', ['field_key' => $fieldKey])],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 기존 필드 중복 체크
|
||||
$query = ItemField::where('tenant_id', $tenantId)
|
||||
->where('field_key', $fieldKey);
|
||||
|
||||
if ($excludeId) {
|
||||
$query->where('id', '!=', $excludeId);
|
||||
}
|
||||
|
||||
if ($query->exists()) {
|
||||
throw ValidationException::withMessages([
|
||||
'field_key' => [__('validation.unique', ['attribute' => 'field_key'])],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 필드 복제
|
||||
*
|
||||
@@ -109,11 +161,19 @@ public function clone(int $id): ItemField
|
||||
'created_by' => $userId,
|
||||
]);
|
||||
|
||||
// 2. field_key 복제 ({새ID}_{원본key} 형식)
|
||||
// 2. field_key 복제 (원본key_copy 형식, 중복 시 _copy2, _copy3...)
|
||||
if ($original->field_key) {
|
||||
// 원본 field_key에서 키 부분 추출 (예: "81_itemNum" → "itemNum")
|
||||
$originalKey = preg_replace('/^\d+_/', '', $original->field_key);
|
||||
$cloned->update(['field_key' => $cloned->id.'_'.$originalKey.'_copy']);
|
||||
$baseKey = $original->field_key.'_copy';
|
||||
$copyKey = $baseKey;
|
||||
$suffix = 1;
|
||||
|
||||
// 중복되지 않는 키 찾기
|
||||
while (ItemField::where('tenant_id', $tenantId)->where('field_key', $copyKey)->exists()) {
|
||||
$suffix++;
|
||||
$copyKey = $baseKey.$suffix;
|
||||
}
|
||||
|
||||
$cloned->update(['field_key' => $copyKey]);
|
||||
}
|
||||
|
||||
return $cloned;
|
||||
@@ -183,9 +243,12 @@ public function store(int $sectionId, array $data): ItemField
|
||||
'created_by' => $userId,
|
||||
]);
|
||||
|
||||
// 2. field_key 설정 ({ID}_{field_key} 형식)
|
||||
// 2. field_key 설정 (시스템 필드 + 중복 검증 후 저장)
|
||||
if (! empty($data['field_key'])) {
|
||||
$field->update(['field_key' => $field->id.'_'.$data['field_key']]);
|
||||
$sourceTable = $data['source_table'] ?? null;
|
||||
$groupId = $data['group_id'] ?? 1;
|
||||
$this->validateFieldKeyUnique($data['field_key'], $tenantId, $sourceTable, $groupId);
|
||||
$field->update(['field_key' => $data['field_key']]);
|
||||
}
|
||||
|
||||
// 3. 섹션-필드 관계 생성
|
||||
@@ -237,9 +300,16 @@ public function update(int $id, array $data): ItemField
|
||||
'updated_by' => $userId,
|
||||
];
|
||||
|
||||
// field_key 변경 시 {ID}_{field_key} 형식 유지
|
||||
// field_key 변경 시 시스템 필드 + 중복 검증 후 저장
|
||||
if (array_key_exists('field_key', $data)) {
|
||||
$updateData['field_key'] = $data['field_key'] ? $field->id.'_'.$data['field_key'] : null;
|
||||
if ($data['field_key']) {
|
||||
$sourceTable = $data['source_table'] ?? null;
|
||||
$groupId = $field->group_id ?? 1;
|
||||
$this->validateFieldKeyUnique($data['field_key'], $tenantId, $sourceTable, $groupId, $id);
|
||||
$updateData['field_key'] = $data['field_key'];
|
||||
} else {
|
||||
$updateData['field_key'] = null;
|
||||
}
|
||||
}
|
||||
|
||||
// is_locked 변경 처리
|
||||
|
||||
Reference in New Issue
Block a user