merge([ 'diseaseId' => $this->route('diseaseId'), 'recordId' => $this->route('recordId') ]); } /** * Get the validation rules that apply to the request. * * @return array|string> */ public function rules(): array { $mimeTypeMap = [ 'audio' => [ 'aac', 'midi', 'mp3', 'ogg', 'wav', 'webm', 'flac', 'aiff', 'amr', 'opus' ], 'video' => [ 'mp4', 'avi', 'mkv', 'webm', 'ogg', '3gp', 'flv', 'mov', 'wmv', 'mpg', 'mpeg', 'm4v', 'h264', 'hevc' ], 'image' => [ 'jpeg', 'jpg', 'png', 'gif', 'bmp', 'webp', 'tiff', 'svg', 'heif', 'heic', 'ico', 'jp2', 'j2k', 'avif' ], 'text-document' => [ 'pdf', 'doc', 'docx', 'txt' ], 'compressed-document' => [ 'zip', '7z', 'tar', 'gz', 'rar', 'bz2', 'xz' ], 'spreadsheet' => [ 'xls', 'xlsx', 'csv', 'ods' ], ]; $rules = [ 'diseaseId' => 'required|integer|exists:diseases,id', 'recordId' => [ 'required', 'integer', 'exists:disease_records,id', function ($attribute, $value, $fail) { $exists = \App\Models\DiseaseRecord::where('id', $value) ->where('disease_id', $this->route('diseaseId')) ->exists(); if (!$exists) { $fail('The selected record does not belong to the specified disease.'); } }, ], ]; if ($this->filled('diseaseId')) { try { $disease = Disease::findOrFail($this->route('diseaseId')); if ($disease && isset($disease->schema['columns'])) { foreach ($disease->schema['columns'] as $column) { $columnName = $column['name']; $columnRules = []; switch ($column['type']) { case 'string': case 'text': $columnRules[] = 'sometimes|required|string'; break; case 'integer': $columnRules[] = 'sometimes|required|integer'; break; case 'decimal': case 'float': $columnRules[] = 'sometimes|required|numeric'; break; case 'datetime': case 'date': $columnRules[] = 'sometimes|required|date'; break; case 'time': $columnRules[] = 'sometimes|required|date_format:H:i'; break; case 'file': $columnRules[] = 'sometimes|required'; // Ensures a file is uploaded. // Check if a 'format' is defined in the schema for the column. if (!empty($column['multiple'])) { // Ensure the field is an array of files. $rules[$columnName] = 'sometimes|required|array|min:1'; // Apply validation to each file in the array. $fileRules = ['file', 'max:20480']; // Start with basic file validation. // Add MIME type validation if a format is specified. if (!empty($column['format'])) { $category = trim($column['format'], '.'); // Strip the dot from the format. if (array_key_exists($category, $mimeTypeMap)) { $formats = $mimeTypeMap[$category]; $fileRules[] = 'mimes:' . implode(',', $formats); } else { Log::warning("Unsupported format '{$column['format']}' for column '{$columnName}'"); } } // Apply the rules to each file in the array. $rules[$columnName . '.*'] = implode('|', $fileRules); // print_r($rules); } else { // Single file upload handling. $columnRules[] = 'sometimes|required|file|max:20480'; // Add MIME type validation if a format is specified. if (!empty($column['format'])) { $category = trim($column['format'], '.'); // Strip the dot from the format. if (array_key_exists($category, $mimeTypeMap)) { $formats = $mimeTypeMap[$category]; $columnRules[] = 'mimes:' . implode(',', $formats); } else { Log::warning("Unsupported format '{$column['format']}' for column '{$columnName}'"); } } // Apply the accumulated rules for a single file. $rules[$columnName] = implode('|', $columnRules); // print_r($rules); } // print_r($columnRules); break; case 'boolean': $columnRules[] = 'sometimes|required|boolean'; break; case 'enum': if (!empty($column['options']) && is_array($column['options'])) { $columnRules[] = 'sometimes|required|string'; $columnRules[] = 'in:' . implode(',', $column['options']); } break; case 'email': $columnRules[] = 'sometimes|required|email'; break; case 'phone': $columnRules[] = 'sometimes|required|string'; $columnRules[] = 'regex:/^([0-9\s\-\+\(\)]*)$/'; break; } // // Add required/nullable validation // if (!empty($column['required'])) { // $columnRules[] = 'required'; // } else { // $columnRules[] = 'nullable'; // } // // Add min/max validation if specified // if (!empty($column['min'])) { // switch ($column['type']) { // case 'string': // case 'text': // $columnRules[] = 'min:' . $column['min']; // break; // case 'integer': // case 'decimal': // case 'float': // $columnRules[] = 'min_digits:' . $column['min']; // break; // case 'file': // $columnRules[] = 'min:' . $column['min']; // min in kilobytes // break; // } // } // if (!empty($column['max'])) { // switch ($column['type']) { // case 'string': // case 'text': // $columnRules[] = 'max:' . $column['max']; // break; // case 'integer': // case 'decimal': // case 'float': // $columnRules[] = 'max_digits:' . $column['max']; // break; // case 'file': // $columnRules[] = 'max:' . $column['max']; // max in kilobytes // break; // } // } if (!empty($columnRules)) { $rules[$columnName] = implode('|', $columnRules); } } } } catch (\Throwable $e) { Log::error('Error loading disease schema: ' . $e->getMessage()); } } return $rules; } public function validated($key = null, $default = null): array { $validated = parent::validated(); return [ 'diseaseId' => $validated['diseaseId'], 'data' => collect($validated) ->except('diseaseId', 'recordId') ->toArray() ]; } protected function failedValidation(Validator $validator) { throw new HttpResponseException( ResponseJson::failedResponse('Validation error', $validator->errors()->toArray()) ); } }