'required|integer|exists:models,id', 'name' => 'required|string|max:50|regex:/^[a-zA-Z][a-zA-Z0-9_]*$/', 'label' => 'required|string|max:100', 'expression' => 'required|string|max:1000', 'unit' => 'nullable|string|max:20', 'order' => 'nullable|integer|min:1', 'description' => 'nullable|string|max:500', 'is_active' => 'boolean', ]; } /** * Get custom messages for validator errors. */ public function messages(): array { return [ 'name.regex' => __('error.formula_name_format'), 'expression.required' => __('error.formula_expression_required'), ]; } /** * Configure the validator instance. */ public function withValidator($validator): void { $validator->after(function ($validator) { $this->validateFormulaNameUnique($validator); $this->validateFormulaExpression($validator); $this->validateDependencies($validator); }); } /** * 공식명 중복 검증 */ private function validateFormulaNameUnique($validator): void { if (!$this->input('model_id') || !$this->input('name')) { return; } $query = ModelFormula::where('model_id', $this->input('model_id')) ->where('name', $this->input('name')); // 수정 시 자기 자신 제외 if ($this->route('id')) { $query->where('id', '!=', $this->route('id')); } if ($query->exists()) { $validator->errors()->add('name', __('error.formula_name_duplicate')); } // 매개변수명과 중복 검증 $parameterExists = \Shared\Models\Products\ModelParameter::where('model_id', $this->input('model_id')) ->where('name', $this->input('name')) ->exists(); if ($parameterExists) { $validator->errors()->add('name', __('error.formula_name_conflicts_with_parameter')); } } /** * 공식 표현식 검증 */ private function validateFormulaExpression($validator): void { if (!$this->input('expression')) { return; } $tempFormula = new ModelFormula([ 'expression' => $this->input('expression'), 'model_id' => $this->input('model_id'), ]); $expressionErrors = $tempFormula->validateExpression(); if (!empty($expressionErrors)) { foreach ($expressionErrors as $error) { $validator->errors()->add('expression', $error); } } } /** * 의존성 검증 */ private function validateDependencies($validator): void { if (!$this->input('model_id') || !$this->input('expression')) { return; } $tempFormula = new ModelFormula([ 'expression' => $this->input('expression'), 'model_id' => $this->input('model_id'), ]); $dependencies = $tempFormula->extractVariables(); if (empty($dependencies)) { return; } // 매개변수 목록 가져오기 $parameters = \Shared\Models\Products\ModelParameter::where('model_id', $this->input('model_id')) ->active() ->pluck('name') ->toArray(); // 기존 공식 목록 가져오기 (자기 자신 제외) $formulasQuery = ModelFormula::where('model_id', $this->input('model_id')) ->active(); if ($this->route('id')) { $formulasQuery->where('id', '!=', $this->route('id')); } $formulas = $formulasQuery->pluck('name')->toArray(); $validNames = array_merge($parameters, $formulas); foreach ($dependencies as $dep) { if (!in_array($dep, $validNames)) { $validator->errors()->add('expression', __('error.dependency_not_found', ['name' => $dep])); } } // 순환 의존성 검증 $this->validateCircularDependency($validator, $dependencies); } /** * 순환 의존성 검증 */ private function validateCircularDependency($validator, array $dependencies): void { if (!$this->input('model_id') || !$this->input('name')) { return; } $allFormulasQuery = ModelFormula::where('model_id', $this->input('model_id')) ->active(); if ($this->route('id')) { $allFormulasQuery->where('id', '!=', $this->route('id')); } $allFormulas = $allFormulasQuery->get(); // 현재 공식을 임시로 추가 $tempFormula = new ModelFormula([ 'name' => $this->input('name'), 'dependencies' => $dependencies, ]); $allFormulas->push($tempFormula); if ($this->hasCircularDependency($tempFormula, $allFormulas->toArray())) { $validator->errors()->add('expression', __('error.circular_dependency_detected')); } } /** * 순환 의존성 검사 */ private function hasCircularDependency(ModelFormula $formula, array $allFormulas, array $visited = []): bool { if (in_array($formula->name, $visited)) { return true; } $visited[] = $formula->name; foreach ($formula->dependencies ?? [] as $dep) { foreach ($allFormulas as $depFormula) { if ($depFormula->name === $dep) { if ($this->hasCircularDependency($depFormula, $allFormulas, $visited)) { return true; } break; } } } return false; } }