diff --git a/src/Repositories/Behaviors/HandleBlocks.php b/src/Repositories/Behaviors/HandleBlocks.php index d453bde92..fe56ddaa3 100644 --- a/src/Repositories/Behaviors/HandleBlocks.php +++ b/src/Repositories/Behaviors/HandleBlocks.php @@ -2,6 +2,8 @@ namespace A17\Twill\Repositories\Behaviors; +use Illuminate\Support\Str; +use Illuminate\Support\Arr; use A17\Twill\Facades\TwillBlocks; use A17\Twill\Facades\TwillUtil; use A17\Twill\Models\Behaviors\HasMedias; @@ -174,15 +176,21 @@ private function updateBlock( return $blockCreated; } - private function validate(array $formData, int $id, array $basicRules, array $translatedFieldRules): void + private function validate(array $formData, int $id, array $basicRules, array $translatedFieldRules, array $messages): void { $finalValidator = $this->blockValidator; foreach ($translatedFieldRules as $field => $rules) { foreach (config('translatable.locales') as $locale) { - $data = $formData[$field][$locale] ?? null; - $validator = Validator::make([$field => $data], [$field => $rules]); + $validator = Validator::make($formData, ["$field.$locale" => $rules], $messages); foreach ($validator->messages()->getMessages() as $key => $errors) { foreach ($errors as $error) { + if ($this->errorMessageIsOnNestedBlock($key)) { + $blockInfo = $this->makeValidationInfoForNestedBlocks($id, $key, $formData, true); + + $id = $blockInfo['nestedBlockId']; + $key = $blockInfo['nestedField']; + } + $finalValidator->getMessageBag()->add("blocks.$id" . "[$key][$locale]", $error); $finalValidator->getMessageBag()->add("blocks.$locale", 'Failed'); } @@ -190,9 +198,16 @@ private function validate(array $formData, int $id, array $basicRules, array $tr } } foreach ($basicRules as $field => $rules) { - $validator = Validator::make([$field => $formData[$field] ?? null], [$field => $rules]); + $validator = Validator::make($formData, [$field => $rules], $messages); foreach ($validator->messages()->getMessages() as $key => $errors) { foreach ($errors as $error) { + if ($this->errorMessageIsOnNestedBlock($key)) { + $blockInfo = $this->makeValidationInfoForNestedBlocks($id, $key, $formData, true); + + $id = $blockInfo['nestedBlockId']; + $key = $blockInfo['nestedField']; + } + $finalValidator->getMessageBag()->add("blocks[$id][$key]", $error); } } @@ -300,7 +315,8 @@ private function validateBlockArray( (array)$block['content'] + ($block['medias'] ?? []) + ($block['browsers'] ?? []) + ($block['blocks'] ?? []), $block['id'], $blockInstance->getRules(), - $handleTranslations ? $blockInstance->getRulesForTranslatedFields() : [] + $handleTranslations ? $blockInstance->getRulesForTranslatedFields() : [], + $blockInstance->getMessages() ); } @@ -533,4 +549,27 @@ protected function hasRelatedTable(): bool } return static::$hasRelatedTableCache; } + + public function errorMessageIsOnNestedBlock($failedKey) + { + return strpos($failedKey, '.content.') !== false; + } + + public function makeValidationInfoForNestedBlocks($rootBlockId, $failedKey, $formData, $translated = false) + { + $blockFilter = Str::beforeLast($failedKey, '.content.'); + + $blockField = Str::afterLast($failedKey, '.content.'); + + if ($translated) { + $blockField = Str::beforeLast($blockField, '.'); + } + + $block = Arr::get($formData, $blockFilter); + + return [ + 'nestedBlockId' => $block['id'], + 'nestedField' => $blockField, + ]; + } } diff --git a/src/Services/Blocks/Block.php b/src/Services/Blocks/Block.php index de4ffd841..63cbef342 100644 --- a/src/Services/Blocks/Block.php +++ b/src/Services/Blocks/Block.php @@ -130,6 +130,11 @@ class Block */ public $rulesForTranslatedFields = []; + /** + * @var array + */ + public $messages = []; + /** * Renderedata. */ @@ -161,6 +166,7 @@ public static function forComponent(string $componentClass): self $class->hideTitlePrefix = $componentClass::shouldHidePrefix(); $class->rulesForTranslatedFields = (new $componentClass())->getTranslatableValidationRules(); $class->rules = (new $componentClass())->getValidationRules(); + $class->messages = (new $componentClass())->getValidationMessages(); return $class; } @@ -391,6 +397,10 @@ public function parse(): self $this->rulesForTranslatedFields = $value ?? $this->rulesForTranslatedFields; }); + $this->parseArrayProperty('ValidationMessages', $contents, $this->name, function ($value) { + $this->messages = $value ?? $this->messages; + }); + $this->parseMixedProperty('titleField', $contents, $this->name, function ($value, $options) { $this->titleField = $value; $this->hideTitlePrefix = (bool)($options['hidePrefix'] ?? false); @@ -415,6 +425,14 @@ public function getRulesForTranslatedFields(): array return $this->rulesForTranslatedFields; } + /** + * Checks both the blade file or helper class for validation rules. Returns in order the first one with data. + */ + public function getMessages(): array + { + return $this->messages; + } + /** * Parse a string property directive in the form of `@twillTypeProperty('value')`. * diff --git a/src/View/Components/Blocks/TwillBlockComponent.php b/src/View/Components/Blocks/TwillBlockComponent.php index 480b83de9..00bc7da8c 100644 --- a/src/View/Components/Blocks/TwillBlockComponent.php +++ b/src/View/Components/Blocks/TwillBlockComponent.php @@ -123,6 +123,11 @@ public function getTranslatableValidationRules(): array return []; } + public function getValidationMessages(): array + { + return []; + } + abstract public function getForm(): Form; final public function renderForm(): View