Files
sam-api/app/Services/ItemMaster/ItemDataService.php
hskwon bf92b37ff6 feat: 품목 마스터 소스 매핑 기능 추가
- ItemField 모델: 소스 매핑 컬럼 추가 (source_table, source_column 등)
- ItemPage 모델: source_table 컬럼 추가
- ItemDataService: 동적 데이터 조회 서비스
- ItemMasterApi Swagger 업데이트
- ItemTypeSeeder: 품목 유형 시더
- 스펙 문서: ITEM_MASTER_FIELD_INTEGRATION_PLAN.md
2025-12-09 09:39:16 +09:00

175 lines
5.8 KiB
PHP

<?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);
}
}