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

209 lines
6.5 KiB
PHP

<?php
namespace App\Services;
use App\Models\Commons\Category;
use App\Models\Commons\CategoryTemplate;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Validator;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
class CategoryTemplateService extends Service
{
public function index(int $categoryId, array $params)
{
$tenantId = $this->tenantId();
$size = (int) ($params['size'] ?? 20);
return CategoryTemplate::query()
->where('tenant_id', $tenantId)
->where('category_id', $categoryId)
->orderByDesc('version_no')
->paginate($size);
}
public function store(int $categoryId, array $data)
{
$tenantId = $this->tenantId();
$userId = $this->apiUserId();
$this->assertCategoryExists($tenantId, $categoryId);
$v = Validator::make($data, [
'version_no' => 'required|integer|min:1',
'template_json' => 'required|json',
'applied_at' => 'required|date',
'remarks' => 'nullable|string|max:255',
]);
$payload = $v->validate();
$dup = CategoryTemplate::query()
->where('tenant_id', $tenantId)
->where('category_id', $categoryId)
->where('version_no', $payload['version_no'])
->exists();
if ($dup) {
throw new BadRequestHttpException(__('error.duplicate_key')); // version_no 중복
}
$payload['tenant_id'] = $tenantId;
$payload['category_id'] = $categoryId;
$payload['created_by'] = $userId;
return CategoryTemplate::create($payload);
}
public function show(int $tplId)
{
$tenantId = $this->tenantId();
$tpl = CategoryTemplate::query()
->where('tenant_id', $tenantId)
->find($tplId);
if (! $tpl) {
throw new BadRequestHttpException(__('error.not_found'));
}
return $tpl;
}
public function update(int $tplId, array $data)
{
$tenantId = $this->tenantId();
$userId = $this->apiUserId();
$tpl = CategoryTemplate::query()
->where('tenant_id', $tenantId)
->find($tplId);
if (! $tpl) {
throw new BadRequestHttpException(__('error.not_found'));
}
$v = Validator::make($data, [
'template_json' => 'nullable|json',
'applied_at' => 'nullable|date',
'remarks' => 'nullable|string|max:255',
]);
$payload = $v->validate();
$payload['updated_by'] = $userId;
$tpl->update($payload);
return $tpl->refresh();
}
public function destroy(int $tplId): void
{
$tenantId = $this->tenantId();
$tpl = CategoryTemplate::query()
->where('tenant_id', $tenantId)
->find($tplId);
if (! $tpl) {
throw new BadRequestHttpException(__('error.not_found'));
}
$tpl->delete();
}
/**
* 적용 정책:
* - categories.active_template_version (또는 별도 맵 테이블)에 version_no 반영
* - (옵션) template_json 기반으로 category_fields를 실제로 갱신하려면 여기서 동기화
*/
public function apply(int $categoryId, int $tplId): void
{
$tenantId = $this->tenantId();
$userId = $this->apiUserId();
$tpl = CategoryTemplate::query()
->where('tenant_id', $tenantId)
->where('category_id', $categoryId)
->find($tplId);
if (! $tpl) {
throw new BadRequestHttpException(__('error.not_found'));
}
DB::transaction(function () {
// 1) categories 테이블에 활성 버전 반영(컬럼이 있다면)
// Category::where('tenant_id', $tenantId)->where('id', $categoryId)->update([
// 'active_template_version' => $tpl->version_no,
// 'updated_by' => $userId,
// ]);
// 2) (선택) template_json → category_fields 동기화
// - 추가/수정/삭제 전략은 운영정책에 맞게 구현
// - 여기서는 예시로 "fields" 배열만 처리
// $snapshot = json_decode($tpl->template_json, true);
// foreach (($snapshot['fields'] ?? []) as $i => $f) {
// // key, name, type, required, default, options 매핑 ...
// }
});
}
public function preview(int $categoryId, int $tplId): array
{
$tenantId = $this->tenantId();
$tpl = CategoryTemplate::query()
->where('tenant_id', $tenantId)
->where('category_id', $categoryId)
->find($tplId);
if (! $tpl) {
throw new BadRequestHttpException(__('error.not_found'));
}
$json = json_decode($tpl->template_json, true);
if (! is_array($json)) {
throw new BadRequestHttpException(__('error.invalid_payload'));
}
// 프론트 렌더링 편의 구조로 가공 가능
return $json;
}
public function diff(int $categoryId, int $a, int $b): array
{
$tenantId = $this->tenantId();
$aTpl = CategoryTemplate::query()
->where('tenant_id', $tenantId)->where('category_id', $categoryId)
->where('version_no', $a)->first();
$bTpl = CategoryTemplate::query()
->where('tenant_id', $tenantId)->where('category_id', $categoryId)
->where('version_no', $b)->first();
if (! $aTpl || ! $bTpl) {
throw new BadRequestHttpException(__('error.not_found'));
}
$aj = json_decode($aTpl->template_json, true) ?: [];
$bj = json_decode($bTpl->template_json, true) ?: [];
// 아주 단순한 diff 예시 (fields 키만 비교)
$aKeys = collect($aj['fields'] ?? [])->pluck('key')->all();
$bKeys = collect($bj['fields'] ?? [])->pluck('key')->all();
return [
'added' => array_values(array_diff($bKeys, $aKeys)),
'removed' => array_values(array_diff($aKeys, $bKeys)),
// 변경(diff in detail)은 정책에 맞게 확장
];
}
private function assertCategoryExists(int $tenantId, int $categoryId): void
{
$exists = Category::query()
->where('tenant_id', $tenantId)
->where('id', $categoryId)
->exists();
if (! $exists) {
throw new BadRequestHttpException(__('error.category_not_found'));
}
}
}