Files
sam-api/app/Services/TenantFieldSettingService.php
hskwon cc206fdbed style: Laravel Pint 코드 포맷팅 적용
- PSR-12 스타일 가이드 준수
- 302개 파일 스타일 이슈 자동 수정
- 코드 로직 변경 없음 (포맷팅만)
2025-11-06 17:45:49 +09:00

213 lines
7.5 KiB
PHP

<?php
namespace App\Services;
use App\Models\Tenants\SettingFieldDef;
use App\Models\Tenants\TenantFieldSetting;
use App\Models\Tenants\TenantOptionGroup;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
class TenantFieldSettingService
{
/** 활성 테넌트 ID 조회 (공통) */
protected static function tenantId(): ?int
{
return app('tenant_id'); // 미들웨어에서 주입된 테넌트 ID 가정
}
/** 효과값 1개 빌드: 전역정의 + 테넌트설정 merge */
protected static function buildEffectiveRow(SettingFieldDef $def, ?TenantFieldSetting $s): array
{
$enabled = $s ? (bool) $s->enabled : (bool) $def->is_core;
return [
'field_key' => $def->field_key,
'label' => $def->label,
'data_type' => $def->data_type,
'input_type' => $def->input_type,
'option_source' => $def->option_source,
'option_payload' => $def->option_payload,
'storage_area' => $def->storage_area,
'storage_key' => $def->storage_key,
'is_core' => (bool) $def->is_core,
// tenant overlay
'enabled' => $enabled,
'required' => $s ? (bool) $s->required : false,
'sort_order' => $s ? (int) $s->sort_order : 0,
'option_group_id' => $s ? $s->option_group_id : null,
'code_group' => $s ? $s->code_group : null,
];
}
/**
* [GET] 효과값 목록
* - 코어(is_core=1)는 기본 enabled=true
* - enabled=false는 제외
* - sort_order → field_key 순 정렬
*/
public static function index(array $params = [])
{
$tenantId = self::tenantId();
if (! $tenantId) {
return ['error' => '활성 테넌트가 없습니다.', 'code' => 400];
}
$defs = SettingFieldDef::query()
->orderByDesc('is_core')
->orderBy('field_key')
->get()
->keyBy('field_key');
$settings = TenantFieldSetting::where('tenant_id', $tenantId)
->get()
->keyBy('field_key');
$rows = [];
foreach ($defs as $key => $def) {
$s = $settings->get($key);
$effective = self::buildEffectiveRow($def, $s);
if ($effective['enabled'] !== true) {
continue;
}
$rows[] = $effective;
}
usort($rows, fn ($a, $b) => ($a['sort_order'] <=> $b['sort_order']) ?: strcmp($a['field_key'], $b['field_key'])
);
return array_values($rows);
}
/**
* [PUT] 대량 업서트
* params:
* items: [{field_key, enabled?, required?, sort_order?, option_group_id?, code_group?}, ...]
*/
public static function bulkUpsert(array $params = [])
{
$tenantId = self::tenantId();
if (! $tenantId) {
return ['error' => '활성 테넌트가 없습니다.', 'code' => 400];
}
$v = Validator::make($params, [
'items' => ['required', 'array', 'min:1'],
'items.*.field_key' => ['required', 'string', 'max:64', Rule::exists('setting_field_defs', 'field_key')],
'items.*.enabled' => ['nullable', 'boolean'],
'items.*.required' => ['nullable', 'boolean'],
'items.*.sort_order' => ['nullable', 'integer'],
'items.*.option_group_id' => ['nullable', 'integer'],
'items.*.code_group' => ['nullable', 'string', 'max:50'],
]);
if ($v->fails()) {
return ['error' => $v->errors()->first(), 'code' => 422];
}
$payload = $v->validated();
try {
DB::transaction(function () use ($payload, $tenantId) {
foreach ($payload['items'] as $row) {
// option_group_id 유효성(해당 테넌트 소유인지)
if (! empty($row['option_group_id'])) {
$exists = TenantOptionGroup::where('tenant_id', $tenantId)
->where('id', $row['option_group_id'])
->exists();
if (! $exists) {
throw new \RuntimeException('option_group_id is invalid for this tenant');
}
}
TenantFieldSetting::updateOrCreate(
['tenant_id' => $tenantId, 'field_key' => $row['field_key']],
[
'enabled' => isset($row['enabled']) ? (int) $row['enabled'] : 0,
'required' => isset($row['required']) ? (int) $row['required'] : 0,
'sort_order' => isset($row['sort_order']) ? (int) $row['sort_order'] : 0,
'option_group_id' => $row['option_group_id'] ?? null,
'code_group' => $row['code_group'] ?? null,
]
);
}
});
} catch (\RuntimeException $e) {
return ['error' => $e->getMessage(), 'code' => 422];
} catch (\Throwable $e) {
return ['error' => '저장 중 오류가 발생했습니다.', 'code' => 500];
}
return ['updated' => true];
}
/**
* [PATCH] 단건 수정
* - 응답은 FieldEffective 포맷으로 반환
*/
public static function updateOne(string $fieldKey, array $params = [])
{
$tenantId = self::tenantId();
if (! $tenantId) {
return ['error' => '활성 테넌트가 없습니다.', 'code' => 400];
}
// 전역 key 존재 확인
$def = SettingFieldDef::where('field_key', $fieldKey)->first();
if (! $def) {
return ['error' => 'field_key not found', 'code' => 404];
}
$v = Validator::make($params, [
'enabled' => ['nullable', 'boolean'],
'required' => ['nullable', 'boolean'],
'sort_order' => ['nullable', 'integer'],
'option_group_id' => ['nullable', 'integer'],
'code_group' => ['nullable', 'string', 'max:50'],
]);
if ($v->fails()) {
return ['error' => $v->errors()->first(), 'code' => 422];
}
$data = $v->validated();
// option_group_id 테넌트 소유 검증
if (! empty($data['option_group_id'])) {
$ok = TenantOptionGroup::where('tenant_id', $tenantId)
->where('id', $data['option_group_id'])
->exists();
if (! $ok) {
return ['error' => 'option_group_id is invalid for this tenant', 'code' => 422];
}
}
try {
$item = TenantFieldSetting::firstOrNew([
'tenant_id' => $tenantId,
'field_key' => $fieldKey,
]);
// 제공된 키만 반영
foreach (['enabled', 'required', 'sort_order', 'option_group_id', 'code_group'] as $k) {
if (array_key_exists($k, $data)) {
$item->{$k} = $data[$k];
}
}
$item->save();
// 최신 설정 다시 로드하여 효과값으로 리턴
$s = TenantFieldSetting::where('tenant_id', $tenantId)
->where('field_key', $fieldKey)
->first();
$effective = self::buildEffectiveRow($def, $s);
return $effective;
} catch (\Throwable $e) {
return ['error' => '수정 중 오류가 발생했습니다.', 'code' => 500];
}
}
}