check(); } /** * Prepare the data for validation. */ protected function prepareForValidation(): void { if (is_null($this->data)) { $this->merge([ 'data' => [], ]); } foreach ($this->{$this->modelName}->data as $iptName => $input) { $input = \json_decode(\json_encode($input), true); // input without inputType we skip if (! isset($input['inputType'])) { continue; } switch ($input['inputType']) { case InputType::text->value: if (is_null($this->data[$iptName]['value'] ?? null) && ! $input['required']) { $this->merge([ $this->data[$iptName]['value'] => '', ]); } break; } } } /** * Get the validation rules that apply to the request. * * @phpcs:disable Generic.Metrics.CyclomaticComplexity.MaxExceeded * * @phpcs:disable Generic.Metrics.NestingLevel.TooHigh */ public function rules(): array { // phpcs:enable $rules = []; foreach ($this->{$this->modelName}->data as $iptName => $input) { $input = \json_decode(\json_encode($input), true); // input without inputType we skip if (! isset($input['inputType'])) { continue; } switch ($input['inputType']) { case InputType::number->value: $rules = array_merge($rules, [ "data.$iptName.value" => $this->compileValidate('required|numeric', $input), ]); break; case InputType::videoYoutube->value: $rules = array_merge($rules, [ "data.$iptName.value" => $this->compileValidate('regex:%([^"&?\/\s]{11})%i', $input), ]); break; case InputType::videoVimeo->value: $rules = array_merge($rules, [ "data.$iptName.value" => $this->compileValidate('regex:%([0-9]{8,10})%i', $input), ]); break; case InputType::video->value: $rules = array_merge($rules, [ "data.$iptName.value" => $this->compileValidate([ 'regex:%([0-9]{8,10}|[^"&?\/\s]{11})%i', ], $input), ]); break; case InputType::string->value: $rules = array_merge($rules, [ "data.$iptName.value" => $this->compileValidate('required|string|min:3', $input), ]); break; case InputType::text->value: $required = $input['required'] ? 'required' : 'nullable'; $mergeRules = $this->compileValidate($required . '|string|min:15', $input); $mergeRules = collect($mergeRules) ->map(function ($rule) { if (\strpos($rule, 'max:') !== false) { $max = \intval(str_replace('max:', '', $rule)); return function ($attribute, $value, $fail) use ($max) { if (\strlen(\strip_tags($value)) > $max) { $fail("$attribute ne doit pas excéder $max caractères"); } }; } return $rule; }); $rules = array_merge($rules, [ "data.$iptName.value" => $mergeRules, ]); break; case InputType::image->value: $required = $this->isUpdate() ? 'sometimes' : 'required'; $ipt = $this->data[$iptName]; $max = isset($ipt['dimensionsMax']); $dim = \is_string($input['dimensions']) ? collect(explode( ',', (isset($ipt['dimensionsMax']) and is_string($ipt['dimensionsMax'])) ? $ipt['dimensionsMax'] : $input['dimensions'] ))->mapWithKeys(function ($v) { $v = explode(' ', $v); return [$v[0] => $v[1]]; }) : $input['dimensions']; // * Adapt dimensions $sizes = (object) [ 'minHeight' => $dim['height'], 'maxHeight' => $dim['height'], 'minWidth' => $dim['width'], 'maxWidth' => $dim['width'], ]; $rules = array_merge($rules, [ "data.$iptName.value" => [ function (string $attribute, $value, callable $fail) use ($sizes, $input) { // Picture can be null. if ($input['required'] || $value !== null) { ToolboxHelper::validatePicture($attribute, $value, $fail, $sizes); } }, ], $this->compileValidate( implode('|', [ $required, 'image', 'mimes:png,jpg,jpeg,svg', sprintf( 'dimensions:%s=%d,%s=%d', $max ? 'max_height' : 'height', $dim['height'], $max ? 'max_width' : 'width', $dim['width'] ), ]), $input ), ]); // Attributes are required if a picture is set. $required = $this->data[$iptName]['value'] ? 'required' : 'nullable'; $rules = array_merge($rules, [ "data.$iptName.alt" => $this->compileValidate("$required|string|min:5", $input), "data.$iptName.title" => $this->compileValidate("$required|string|min:5", $input), ]); break; case InputType::url->value: $rules = array_merge($rules, [ "data.$iptName.value" => array_merge( $this->compileValidate(['required', 'url'], $input), [ // @phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed function ($attribute, $value, $fail) { if (empty($value)) { return; } try { ToolboxHelper::assertNoInternalBoUrls($attribute, $value); } catch (ValidationException $e) { $fail(collect($e->errors())->first()[0]); } }] ), ]); break; case InputType::boolean->value: $all = $this->all(); $nested = ToolboxHelper::dotStringToNestedArray( "data.$iptName.value", $this->input("data.$iptName.value") ? true : false ); $all = ToolboxHelper::arrayMergeRecursiveDistinct($all, $nested); $this->merge($all); $rules = array_merge($rules, [ "data.$iptName.value" => 'boolean', ]); break; case InputType::slider->value: foreach ($input['columns'] as $column) { $columnId = $column['id']; $validation = $column['validation'] ?? ''; if (! empty($validation)) { $rules["data.$iptName.value.*.$columnId"] = $validation; } } break; case InputType::selectImageVideo->value: $value = $this->input("data.$iptName.value", []); if (Arr::exists($value, 'picture')) { $rules = array_merge($rules, [ "data.$iptName.value.picture" => 'required|array', "data.$iptName.value.picture.path" => 'required', "data.$iptName.value.picture.alt" => 'required|string|between:3,255', "data.$iptName.value.picture.title" => 'required|string|between:3,255', ]); } if (Arr::exists($value, 'video')) { $rules = array_merge($rules, [ "data.$iptName.value.video" => 'required|array', "data.$iptName.value.video.id" => 'required|string', "data.$iptName.value.video.alt" => 'required|string|between:3,255', "data.$iptName.value.video.title" => 'required|string|between:3,255', ]); } break; } // end switch } // end foreach return $rules; } /** * Undocumented function */ public function compileValidate(string | array $inputTypeValidate, array $input): array { if (\is_string($inputTypeValidate)) { $inputTypeValidate = explode('|', $inputTypeValidate); } /** @var array|string */ $inputValidate = \collect($input)->get('validate', $input); $inputValidate = \is_string($inputValidate) ? explode('|', $inputValidate) : $inputValidate; foreach ($inputValidate as $validate) { if (! \in_array($validate, $inputTypeValidate)) { $inputTypeValidate[] = $validate; } // if required is hardcode if (\in_array('nullable', $inputTypeValidate)) { $inputTypeValidate = \array_filter($inputTypeValidate, fn ($r) => $r !== 'required'); } } return \collect($inputTypeValidate)->unique()->sort(function ($valueA, $valueB) { $valueA = $this->ruleHasToComeFirst($valueA) ? 0 : 2; $valueB = $this->ruleHasToComeFirst($valueB) ? 1 : 2; return $valueA - $valueB; })->all(); } /** * Does the rule need to come first ? */ private function ruleHasToComeFirst(string $rule): bool { $rule = \collect(\explode(':', $rule, 1))->filter()->first(null, ''); return \collect([ 'nullable', 'present', 'required', 'required_if', 'required_unless', 'required_with', 'required_with_all', 'required_without', 'required_without_all', 'required_with_all', 'missing', 'missing_if', 'missing_unless', 'missing_with', 'missing_with_all', ])->first(fn (string $firstRule) => \strpos($rule, $firstRule) === 0) ? true : false; } /** * Is this request an update request ? */ private function isUpdate(): bool { return $this->isMethod('PUT'); } }