feat: 품목 마스터 소스 매핑 기능 추가
- ItemField 모델: 소스 매핑 컬럼 추가 (source_table, source_column 등) - ItemPage 모델: source_table 컬럼 추가 - ItemDataService: 동적 데이터 조회 서비스 - ItemMasterApi Swagger 업데이트 - ItemTypeSeeder: 품목 유형 시더 - 스펙 문서: ITEM_MASTER_FIELD_INTEGRATION_PLAN.md
This commit is contained in:
174
app/Services/ItemMaster/ItemDataService.php
Normal file
174
app/Services/ItemMaster/ItemDataService.php
Normal file
@@ -0,0 +1,174 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\ItemMaster;
|
||||
|
||||
use App\Models\ItemMaster\ItemField;
|
||||
use App\Services\Service;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class ItemDataService extends Service
|
||||
{
|
||||
/**
|
||||
* 필드 값을 적절한 테이블/컬럼에 저장
|
||||
*
|
||||
* @param string $sourceTable 대상 테이블 (products, materials 등)
|
||||
* @param array $fieldValues [field_id => value] 형태
|
||||
* @param int|null $recordId 수정 시 레코드 ID
|
||||
* @return array 저장된 데이터
|
||||
*/
|
||||
public function saveData(string $sourceTable, array $fieldValues, ?int $recordId = null): array
|
||||
{
|
||||
// 해당 테이블의 필드 매핑 정보 조회
|
||||
$fields = ItemField::where('tenant_id', $this->tenantId())
|
||||
->where('source_table', $sourceTable)
|
||||
->get()
|
||||
->keyBy('id');
|
||||
|
||||
$columnData = []; // DB 컬럼 직접 저장
|
||||
$jsonData = []; // JSON (attributes/options) 저장
|
||||
|
||||
foreach ($fieldValues as $fieldId => $value) {
|
||||
$field = $fields->get($fieldId);
|
||||
|
||||
if (! $field) {
|
||||
// 시스템 필드가 아닌 커스텀 필드
|
||||
$customField = ItemField::find($fieldId);
|
||||
if ($customField) {
|
||||
$jsonPath = $customField->json_path ?? "attributes.{$customField->field_key}";
|
||||
data_set($jsonData, $jsonPath, $value);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($field->isColumnStorage()) {
|
||||
// DB 컬럼에 직접 저장
|
||||
$columnData[$field->source_column] = $this->castValue($value, $field);
|
||||
} else {
|
||||
// JSON 필드에 저장
|
||||
$jsonPath = $field->json_path ?? "attributes.{$field->field_key}";
|
||||
data_set($jsonData, $jsonPath, $value);
|
||||
}
|
||||
}
|
||||
|
||||
// JSON 데이터 병합
|
||||
if (! empty($jsonData['attributes'])) {
|
||||
$columnData['attributes'] = json_encode($jsonData['attributes']);
|
||||
}
|
||||
if (! empty($jsonData['options'])) {
|
||||
$columnData['options'] = json_encode($jsonData['options']);
|
||||
}
|
||||
|
||||
// 공통 컬럼 추가
|
||||
$columnData['tenant_id'] = $this->tenantId();
|
||||
$columnData['updated_by'] = $this->apiUserId();
|
||||
|
||||
if ($recordId) {
|
||||
// 수정
|
||||
DB::table($sourceTable)
|
||||
->where('tenant_id', $this->tenantId())
|
||||
->where('id', $recordId)
|
||||
->update($columnData);
|
||||
|
||||
return array_merge(['id' => $recordId], $columnData);
|
||||
} else {
|
||||
// 생성
|
||||
$columnData['created_by'] = $this->apiUserId();
|
||||
$id = DB::table($sourceTable)->insertGetId($columnData);
|
||||
|
||||
return array_merge(['id' => $id], $columnData);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 필드 타입에 따른 값 변환
|
||||
*/
|
||||
private function castValue($value, ItemField $field)
|
||||
{
|
||||
return match ($field->field_type) {
|
||||
'number' => is_numeric($value) ? (float) $value : null,
|
||||
'checkbox' => filter_var($value, FILTER_VALIDATE_BOOLEAN),
|
||||
'date' => $value ? date('Y-m-d', strtotime($value)) : null,
|
||||
default => $value,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 레코드 조회 시 필드 매핑 적용
|
||||
*
|
||||
* @param string $sourceTable 대상 테이블 (products, materials 등)
|
||||
* @param int $recordId 레코드 ID
|
||||
* @return array 필드 ID => 값 형태의 데이터
|
||||
*/
|
||||
public function getData(string $sourceTable, int $recordId): array
|
||||
{
|
||||
$record = DB::table($sourceTable)
|
||||
->where('tenant_id', $this->tenantId())
|
||||
->where('id', $recordId)
|
||||
->first();
|
||||
|
||||
if (! $record) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// 필드 매핑 정보 조회
|
||||
$fields = ItemField::where('tenant_id', $this->tenantId())
|
||||
->where('source_table', $sourceTable)
|
||||
->get();
|
||||
|
||||
$result = [];
|
||||
$attributes = json_decode($record->attributes ?? '{}', true);
|
||||
$options = json_decode($record->options ?? '{}', true);
|
||||
|
||||
foreach ($fields as $field) {
|
||||
if ($field->isColumnStorage()) {
|
||||
$result[$field->id] = $record->{$field->source_column} ?? null;
|
||||
} else {
|
||||
$jsonPath = $field->json_path ?? "attributes.{$field->field_key}";
|
||||
$result[$field->id] = data_get(
|
||||
['attributes' => $attributes, 'options' => $options],
|
||||
$jsonPath
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 페이지 기반으로 레코드 저장
|
||||
*
|
||||
* @param int $pageId ItemPage ID
|
||||
* @param array $fieldValues [field_id => value] 형태
|
||||
* @param int|null $recordId 수정 시 레코드 ID
|
||||
* @return array 저장된 데이터
|
||||
*/
|
||||
public function saveDataByPage(int $pageId, array $fieldValues, ?int $recordId = null): array
|
||||
{
|
||||
$page = \App\Models\ItemMaster\ItemPage::find($pageId);
|
||||
|
||||
if (! $page || ! $page->source_table) {
|
||||
throw new \InvalidArgumentException("Invalid page or source_table not defined for page: {$pageId}");
|
||||
}
|
||||
|
||||
return $this->saveData($page->source_table, $fieldValues, $recordId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 페이지 기반으로 레코드 조회
|
||||
*
|
||||
* @param int $pageId ItemPage ID
|
||||
* @param int $recordId 레코드 ID
|
||||
* @return array 필드 ID => 값 형태의 데이터
|
||||
*/
|
||||
public function getDataByPage(int $pageId, int $recordId): array
|
||||
{
|
||||
$page = \App\Models\ItemMaster\ItemPage::find($pageId);
|
||||
|
||||
if (! $page || ! $page->source_table) {
|
||||
throw new \InvalidArgumentException("Invalid page or source_table not defined for page: {$pageId}");
|
||||
}
|
||||
|
||||
return $this->getData($page->source_table, $recordId);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user