diff --git a/app/Http/Controllers/Api/V1/CategoryFieldController.php b/app/Http/Controllers/Api/V1/CategoryFieldController.php index ffec341..595f863 100644 --- a/app/Http/Controllers/Api/V1/CategoryFieldController.php +++ b/app/Http/Controllers/Api/V1/CategoryFieldController.php @@ -4,6 +4,8 @@ use App\Helpers\ApiResponse; use App\Http\Controllers\Controller; +use App\Http\Requests\CategoryField\CategoryFieldStoreRequest; +use App\Http\Requests\CategoryField\CategoryFieldUpdateRequest; use App\Services\CategoryFieldService; use Illuminate\Http\Request; @@ -16,15 +18,15 @@ public function index(int $id, Request $request) { return ApiResponse::handle(function () use ($id, $request) { return $this->service->index($id, $request->all()); - }, '카테고리 필드 목록'); + }, __('message.category_field.fetched')); } // POST /categories/{id}/fields - public function store(int $id, Request $request) + public function store(int $id, CategoryFieldStoreRequest $request) { return ApiResponse::handle(function () use ($id, $request) { - return $this->service->store($id, $request->all()); - }, '카테고리 필드 생성'); + return $this->service->store($id, $request->validated()); + }, __('message.category_field.created')); } // GET /categories/fields/{field} @@ -32,15 +34,15 @@ public function show(int $field) { return ApiResponse::handle(function () use ($field) { return $this->service->show($field); - }, '카테고리 필드 조회'); + }, __('message.category_field.fetched')); } // PATCH /categories/fields/{field} - public function update(int $field, Request $request) + public function update(int $field, CategoryFieldUpdateRequest $request) { return ApiResponse::handle(function () use ($field, $request) { - return $this->service->update($field, $request->all()); - }, '카테고리 필드 수정'); + return $this->service->update($field, $request->validated()); + }, __('message.category_field.updated')); } // DELETE /categories/fields/{field} @@ -50,7 +52,7 @@ public function destroy(int $field) $this->service->destroy($field); return 'success'; - }, '카테고리 필드 삭제'); + }, __('message.category_field.deleted')); } // POST /categories/{id}/fields/reorder @@ -60,7 +62,7 @@ public function reorder(int $id, Request $request) $this->service->reorder($id, $request->input()); return 'success'; - }, '카테고리 필드 정렬 저장'); + }, __('message.category_field.reordered')); } // PUT /categories/{id}/fields/bulk-upsert @@ -68,6 +70,6 @@ public function bulkUpsert(int $id, Request $request) { return ApiResponse::handle(function () use ($id, $request) { return $this->service->bulkUpsert($id, $request->input('items', [])); - }, '카테고리 필드 일괄 업서트'); + }, __('message.category_field.bulk_upsert')); } } diff --git a/app/Http/Requests/CategoryField/CategoryFieldStoreRequest.php b/app/Http/Requests/CategoryField/CategoryFieldStoreRequest.php new file mode 100644 index 0000000..5731a18 --- /dev/null +++ b/app/Http/Requests/CategoryField/CategoryFieldStoreRequest.php @@ -0,0 +1,27 @@ + 'required|string|max:30|alpha_dash', + 'field_name' => 'required|string|max:100', + 'field_type' => 'required|string|max:20', + 'is_required' => 'nullable|boolean', + 'sort_order' => 'nullable|integer|min:0', + 'default_value' => 'nullable|string|max:100', + 'options' => 'nullable|json', + 'description' => 'nullable|string|max:255', + ]; + } +} diff --git a/app/Http/Requests/CategoryField/CategoryFieldUpdateRequest.php b/app/Http/Requests/CategoryField/CategoryFieldUpdateRequest.php new file mode 100644 index 0000000..106b177 --- /dev/null +++ b/app/Http/Requests/CategoryField/CategoryFieldUpdateRequest.php @@ -0,0 +1,27 @@ + 'sometimes|string|max:30|alpha_dash', + 'field_name' => 'sometimes|string|max:100', + 'field_type' => 'sometimes|string|max:20', + 'is_required' => 'sometimes|boolean', + 'sort_order' => 'sometimes|integer|min:0', + 'default_value' => 'nullable|string|max:100', + 'options' => 'nullable|json', + 'description' => 'nullable|string|max:255', + ]; + } +} diff --git a/app/Models/Commons/CategoryField.php b/app/Models/Commons/CategoryField.php index e22b388..7b92073 100644 --- a/app/Models/Commons/CategoryField.php +++ b/app/Models/Commons/CategoryField.php @@ -34,6 +34,6 @@ public function category() // 편의 스코프 public function scopeRequired($q) { - return $q->where('is_required', 1); + return $q->where('is_required', true); } } diff --git a/app/Services/CategoryFieldService.php b/app/Services/CategoryFieldService.php index 047d26a..3a06101 100644 --- a/app/Services/CategoryFieldService.php +++ b/app/Services/CategoryFieldService.php @@ -5,7 +5,6 @@ use App\Models\Commons\Category; use App\Models\Commons\CategoryField; use Illuminate\Support\Facades\DB; -use Illuminate\Support\Facades\Validator; // 가정: Eloquent 모델 경로 use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; class CategoryFieldService extends Service @@ -32,31 +31,22 @@ public function store(int $categoryId, array $data) $this->assertCategoryExists($tenantId, $categoryId); - $v = Validator::make($data, [ - 'field_key' => 'required|string|max:30|alpha_dash', - 'field_name' => 'required|string|max:100', - 'field_type' => 'required|string|max:20', - 'is_required' => 'nullable|in:Y,N', - 'sort_order' => 'nullable|integer|min:0', - 'default_value' => 'nullable|string|max:100', - 'options' => 'nullable|json', - 'description' => 'nullable|string|max:255', - ]); - $payload = $v->validate(); + // FormRequest에서 이미 검증됨 + $payload = $data; // 카테고리 내 field_key 유니크 검증 $exists = CategoryField::query() - ->where(compact('tenant_id')) + ->where('tenant_id', $tenantId) ->where('category_id', $categoryId) ->where('field_key', $payload['field_key']) ->exists(); if ($exists) { - throw new BadRequestHttpException(__('error.duplicate_key')); // ko/error.php에 매핑 + throw new BadRequestHttpException(__('error.duplicate_key')); } $payload['tenant_id'] = $tenantId; $payload['category_id'] = $categoryId; - $payload['is_required'] = $payload['is_required'] ?? 'N'; + $payload['is_required'] = $payload['is_required'] ?? false; $payload['sort_order'] = $payload['sort_order'] ?? 0; $payload['created_by'] = $userId; @@ -91,17 +81,8 @@ public function update(int $fieldId, array $data) throw new BadRequestHttpException(__('error.not_found')); } - $v = Validator::make($data, [ - 'field_key' => 'sometimes|string|max:30|alpha_dash', - 'field_name' => 'sometimes|string|max:100', - 'field_type' => 'sometimes|string|max:20', - 'is_required' => 'sometimes|in:Y,N', - 'sort_order' => 'sometimes|integer|min:0', - 'default_value' => 'nullable|string|max:100', - 'options' => 'nullable|json', - 'description' => 'nullable|string|max:255', - ]); - $payload = $v->validate(); + // FormRequest에서 이미 검증됨 + $payload = $data; if (isset($payload['field_key']) && $payload['field_key'] !== $field->field_key) { $dup = CategoryField::query() @@ -170,19 +151,11 @@ public function bulkUpsert(int $categoryId, array $items): array $result = ['created' => 0, 'updated' => 0]; DB::transaction(function () use ($tenantId, $userId, $categoryId, $items, &$result) { - foreach ($items as $it) { - $v = Validator::make($it, [ - 'id' => 'nullable|integer', - 'field_key' => 'sometimes|required_without:id|string|max:30|alpha_dash', - 'field_name' => 'required|string|max:100', - 'field_type' => 'required|string|max:20', - 'is_required' => 'nullable|in:Y,N', - 'sort_order' => 'nullable|integer|min:0', - 'default_value' => 'nullable|string|max:100', - 'options' => 'nullable|json', - 'description' => 'nullable|string|max:255', - ]); - $payload = $v->validate(); + foreach ($items as $payload) { + // 기본적인 검증 (FormRequest가 배열 내부까지는 검증 못함) + if (! isset($payload['field_name'], $payload['field_type'])) { + throw new BadRequestHttpException(__('error.invalid_payload')); + } if (! empty($payload['id'])) { $model = CategoryField::query() @@ -224,7 +197,7 @@ public function bulkUpsert(int $categoryId, array $items): array $payload['tenant_id'] = $tenantId; $payload['category_id'] = $categoryId; - $payload['is_required'] = $payload['is_required'] ?? 'N'; + $payload['is_required'] = $payload['is_required'] ?? false; $payload['sort_order'] = $payload['sort_order'] ?? 0; $payload['created_by'] = $userId; diff --git a/app/Swagger/v1/CategoryExtras.php b/app/Swagger/v1/CategoryExtras.php index 07c3be8..e8176b0 100644 --- a/app/Swagger/v1/CategoryExtras.php +++ b/app/Swagger/v1/CategoryExtras.php @@ -26,7 +26,7 @@ * @OA\Property(property="field_key", type="string", example="width"), * @OA\Property(property="field_name", type="string", example="폭(mm)"), * @OA\Property(property="field_type", type="string", example="number"), - * @OA\Property(property="is_required", type="string", enum={"Y","N"}, example="N"), + * @OA\Property(property="is_required", type="boolean", example=false), * @OA\Property(property="sort_order", type="integer", example=1), * @OA\Property(property="default_value", type="string", nullable=true, example=null), * @OA\Property( diff --git a/lang/ko/message.php b/lang/ko/message.php index 21ece4c..f054c5e 100644 --- a/lang/ko/message.php +++ b/lang/ko/message.php @@ -65,6 +65,15 @@ 'template_applied' => '카테고리 템플릿이 적용되었습니다.', ], + 'category_field' => [ + 'fetched' => '카테고리 필드를 조회했습니다.', + 'created' => '카테고리 필드가 생성되었습니다.', + 'updated' => '카테고리 필드가 수정되었습니다.', + 'deleted' => '카테고리 필드가 삭제되었습니다.', + 'reordered' => '카테고리 필드 정렬이 변경되었습니다.', + 'bulk_upsert' => '카테고리 필드가 일괄 저장되었습니다.', + ], + 'design' => [ 'template_cloned' => 'BOM 템플릿이 복제되었습니다.', 'template_diff' => 'BOM 템플릿 차이를 계산했습니다.',