Files
sam-api/app/Http/Requests/Api/V1/Design/BomResolverFormRequest.php

274 lines
10 KiB
PHP
Raw Normal View History

<?php
namespace App\Http\Requests\Api\V1\Design;
use Illuminate\Foundation\Http\FormRequest;
use App\Models\Design\ModelParameter;
use App\Models\Design\BomTemplate;
class BomResolverFormRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return true; // Authorization handled by middleware
}
/**
* Get the validation rules that apply to the request.
*/
public function rules(): array
{
return [
'input_parameters' => ['required', 'array', 'min:1'],
'input_parameters.*' => ['required'],
'bom_template_id' => ['sometimes', 'integer', 'min:1'],
'include_calculated_values' => ['boolean'],
'include_bom_items' => ['boolean'],
'include_condition_rules' => ['boolean'],
'validate_before_resolve' => ['boolean'],
'calculation_precision' => ['integer', 'min:0', 'max:10'],
];
}
/**
* Get custom messages for validator errors.
*/
public function messages(): array
{
return [
'input_parameters.required' => '입력 매개변수는 필수입니다.',
'input_parameters.array' => '입력 매개변수는 배열 형태여야 합니다.',
'input_parameters.min' => '최소 하나 이상의 입력 매개변수가 필요합니다.',
'input_parameters.*.required' => '모든 입력 매개변수 값은 필수입니다.',
'bom_template_id.integer' => 'BOM 템플릿 ID는 정수여야 합니다.',
'bom_template_id.min' => 'BOM 템플릿 ID는 1 이상이어야 합니다.',
'calculation_precision.integer' => '계산 정밀도는 정수여야 합니다.',
'calculation_precision.min' => '계산 정밀도는 0 이상이어야 합니다.',
'calculation_precision.max' => '계산 정밀도는 10 이하여야 합니다.',
];
}
/**
* Get custom attribute names for validator errors.
*/
public function attributes(): array
{
return [
'input_parameters' => '입력 매개변수',
'bom_template_id' => 'BOM 템플릿 ID',
'include_calculated_values' => '계산값 포함 여부',
'include_bom_items' => 'BOM 아이템 포함 여부',
'include_condition_rules' => '조건 규칙 포함 여부',
'validate_before_resolve' => '해결 전 유효성 검사',
'calculation_precision' => '계산 정밀도',
];
}
/**
* Prepare the data for validation.
*/
protected function prepareForValidation(): void
{
$this->merge([
'include_calculated_values' => $this->boolean('include_calculated_values', true),
'include_bom_items' => $this->boolean('include_bom_items', true),
'include_condition_rules' => $this->boolean('include_condition_rules', true),
'validate_before_resolve' => $this->boolean('validate_before_resolve', true),
'calculation_precision' => $this->integer('calculation_precision', 2),
]);
// Ensure input_parameters is an array
if ($this->has('input_parameters') && !is_array($this->input('input_parameters'))) {
$params = json_decode($this->input('input_parameters'), true);
if (json_last_error() === JSON_ERROR_NONE) {
$this->merge(['input_parameters' => $params]);
}
}
}
/**
* Configure the validator instance.
*/
public function withValidator($validator)
{
$validator->after(function ($validator) {
$this->validateInputParameters($validator);
$this->validateBomTemplate($validator);
$this->validateParameterValues($validator);
});
}
/**
* Validate input parameters against model parameter definitions.
*/
private function validateInputParameters($validator): void
{
$modelId = $this->route('modelId');
$inputParameters = $this->input('input_parameters', []);
if (empty($inputParameters)) {
return;
}
// Get model's INPUT parameters
$modelParameters = ModelParameter::where('model_id', $modelId)
->where('parameter_type', 'INPUT')
->where('tenant_id', auth()->user()?->currentTenant?->id)
->whereNull('deleted_at')
->get()
->keyBy('parameter_name');
// Check for required parameters
$requiredParams = $modelParameters->where('is_required', true)->pluck('parameter_name')->toArray();
$providedParams = array_keys($inputParameters);
$missingRequired = array_diff($requiredParams, $providedParams);
if (!empty($missingRequired)) {
$validator->errors()->add('input_parameters',
'다음 필수 매개변수가 누락되었습니다: ' . implode(', ', $missingRequired)
);
}
// Check for unknown parameters
$knownParams = $modelParameters->pluck('parameter_name')->toArray();
$unknownParams = array_diff($providedParams, $knownParams);
if (!empty($unknownParams)) {
$validator->errors()->add('input_parameters',
'알 수 없는 매개변수가 포함되어 있습니다: ' . implode(', ', $unknownParams)
);
}
}
/**
* Validate BOM template exists and belongs to the model.
*/
private function validateBomTemplate($validator): void
{
$bomTemplateId = $this->input('bom_template_id');
$modelId = $this->route('modelId');
if (!$bomTemplateId) {
return;
}
$template = BomTemplate::where('id', $bomTemplateId)
->where('tenant_id', auth()->user()?->currentTenant?->id)
->whereNull('deleted_at')
->first();
if (!$template) {
$validator->errors()->add('bom_template_id', '지정된 BOM 템플릿이 존재하지 않습니다.');
return;
}
// Check if template belongs to the model (through model_version)
if ($template->modelVersion && $template->modelVersion->model_id != $modelId) {
$validator->errors()->add('bom_template_id', 'BOM 템플릿이 해당 모델에 속하지 않습니다.');
}
}
/**
* Validate parameter values against their constraints.
*/
private function validateParameterValues($validator): void
{
$modelId = $this->route('modelId');
$inputParameters = $this->input('input_parameters', []);
if (empty($inputParameters)) {
return;
}
// Get model parameter definitions
$modelParameters = ModelParameter::where('model_id', $modelId)
->where('parameter_type', 'INPUT')
->where('tenant_id', auth()->user()?->currentTenant?->id)
->whereNull('deleted_at')
->get()
->keyBy('parameter_name');
foreach ($inputParameters as $paramName => $value) {
$parameter = $modelParameters->get($paramName);
if (!$parameter) {
continue; // Unknown parameter already handled above
}
// Validate value against parameter constraints
$this->validateParameterValue($validator, $parameter, $paramName, $value);
}
}
/**
* Validate individual parameter value.
*/
private function validateParameterValue($validator, $parameter, string $paramName, $value): void
{
// Check for null/empty required values
if ($parameter->is_required && ($value === null || $value === '')) {
$validator->errors()->add("input_parameters.{$paramName}", "{$paramName}은(는) 필수 매개변수입니다.");
return;
}
// Validate data type
switch ($parameter->data_type ?? 'STRING') {
case 'INTEGER':
if (!is_numeric($value) || (int)$value != $value) {
$validator->errors()->add("input_parameters.{$paramName}", "{$paramName}은(는) 정수여야 합니다.");
return;
}
$value = (int)$value;
break;
case 'DECIMAL':
if (!is_numeric($value)) {
$validator->errors()->add("input_parameters.{$paramName}", "{$paramName}은(는) 숫자여야 합니다.");
return;
}
$value = (float)$value;
break;
case 'BOOLEAN':
if (!is_bool($value) && !in_array($value, [0, 1, '0', '1', 'true', 'false'])) {
$validator->errors()->add("input_parameters.{$paramName}", "{$paramName}은(는) 불린 값이어야 합니다.");
return;
}
break;
case 'STRING':
if (!is_string($value) && !is_numeric($value)) {
$validator->errors()->add("input_parameters.{$paramName}", "{$paramName}은(는) 문자열이어야 합니다.");
return;
}
$value = (string)$value;
break;
}
// Validate min/max values for numeric types
if (in_array($parameter->data_type, ['INTEGER', 'DECIMAL']) && is_numeric($value)) {
if ($parameter->min_value !== null && $value < $parameter->min_value) {
$validator->errors()->add("input_parameters.{$paramName}",
"{$paramName}은(는) {$parameter->min_value} 이상이어야 합니다."
);
}
if ($parameter->max_value !== null && $value > $parameter->max_value) {
$validator->errors()->add("input_parameters.{$paramName}",
"{$paramName}은(는) {$parameter->max_value} 이하여야 합니다."
);
}
}
// Validate options for select type parameters
if (!empty($parameter->options) && !in_array($value, $parameter->options)) {
$validOptions = implode(', ', $parameter->options);
$validator->errors()->add("input_parameters.{$paramName}",
"{$paramName}의 값은 다음 중 하나여야 합니다: {$validOptions}"
);
}
}
}