274 lines
10 KiB
PHP
274 lines
10 KiB
PHP
|
|
<?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}"
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|