From f04cfa3e715a75efc4201f9a3c2fd3ba3ada8da5 Mon Sep 17 00:00:00 2001 From: Antonio Carlos Ribeiro Date: Fri, 26 Jul 2024 16:56:26 +0200 Subject: [PATCH 1/4] Add support for nested block validation --- src/Repositories/Behaviors/HandleBlocks.php | 76 +++++++++++++++++-- src/Services/Blocks/Block.php | 19 +++++ .../Components/Blocks/TwillBlockComponent.php | 5 ++ 3 files changed, 94 insertions(+), 6 deletions(-) diff --git a/src/Repositories/Behaviors/HandleBlocks.php b/src/Repositories/Behaviors/HandleBlocks.php index d453bde92..dee9fe697 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->makeBlocksValidationArrayInfoForNestedBlocks($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,10 +198,17 @@ 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) { - $finalValidator->getMessageBag()->add("blocks[$id][$key]", $error); + if ($this->errorMessageIsOnNestedBlock($key)) { + $blockInfo = $this->makeBlocksValidationArrayInfoForNestedBlocks($id, $key, $formData, true); + + $id = $blockInfo['nestedBlockId']; + $key = $blockInfo['nestedField']; + } + + $finalValidator->getMessageBag()->add($this->makeBlocksValidationArrayName($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,52 @@ protected function hasRelatedTable(): bool } return static::$hasRelatedTableCache; } + + protected function makeBlocksValidationArrayName($rootBlockId, $failedKey) + { + // Split the string by '.' + $parts = explode('.', $failedKey); + + // Initialize an empty string for the result + $bracketNotationString = ''; + + // Iterate over the parts and wrap each in brackets + foreach ($parts as $part) { + // Check if the part contains nested dot notation and handle it accordingly + if (strpos($part, '.') !== false) { + $nestedParts = explode('.', $part); + + foreach ($nestedParts as $nestedPart) { + $bracketNotationString .= '[' . $nestedPart . ']'; + } + } else { + $bracketNotationString .= '[' . $part . ']'; + } + } + + return "blocks[$rootBlockId]$bracketNotationString"; + } + + public function errorMessageIsOnNestedBlock($failedKey) + { + return strpos($failedKey, '.content.') !== false; + } + + public function makeBlocksValidationArrayInfoForNestedBlocks($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..3189867ef 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; } @@ -335,6 +341,7 @@ public function toList(): Collection 'component' => $this->component, 'rules' => $this->getRules(), 'rulesForTranslatedFields' => $this->getRulesForTranslatedFields(), + 'messages' => $this->getMessages(), 'max' => $this->type === self::TYPE_REPEATER ? $this->max : null, ]); } @@ -391,6 +398,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 +426,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 From ac6a31c5de862aa2328be64f015319d07dc9ce9b Mon Sep 17 00:00:00 2001 From: Antonio Carlos Ribeiro Date: Sat, 27 Jul 2024 11:57:41 +0200 Subject: [PATCH 2/4] Refactor to remove some code --- src/Repositories/Behaviors/HandleBlocks.php | 27 +-------------------- 1 file changed, 1 insertion(+), 26 deletions(-) diff --git a/src/Repositories/Behaviors/HandleBlocks.php b/src/Repositories/Behaviors/HandleBlocks.php index dee9fe697..23623e2bb 100644 --- a/src/Repositories/Behaviors/HandleBlocks.php +++ b/src/Repositories/Behaviors/HandleBlocks.php @@ -208,7 +208,7 @@ private function validate(array $formData, int $id, array $basicRules, array $tr $key = $blockInfo['nestedField']; } - $finalValidator->getMessageBag()->add($this->makeBlocksValidationArrayName($id, $key), $error); + $finalValidator->getMessageBag()->add("blocks[$id][$key]", $error); } } } @@ -550,31 +550,6 @@ protected function hasRelatedTable(): bool return static::$hasRelatedTableCache; } - protected function makeBlocksValidationArrayName($rootBlockId, $failedKey) - { - // Split the string by '.' - $parts = explode('.', $failedKey); - - // Initialize an empty string for the result - $bracketNotationString = ''; - - // Iterate over the parts and wrap each in brackets - foreach ($parts as $part) { - // Check if the part contains nested dot notation and handle it accordingly - if (strpos($part, '.') !== false) { - $nestedParts = explode('.', $part); - - foreach ($nestedParts as $nestedPart) { - $bracketNotationString .= '[' . $nestedPart . ']'; - } - } else { - $bracketNotationString .= '[' . $part . ']'; - } - } - - return "blocks[$rootBlockId]$bracketNotationString"; - } - public function errorMessageIsOnNestedBlock($failedKey) { return strpos($failedKey, '.content.') !== false; From f0e13596e0066c5573e56f912990bb7b352b4097 Mon Sep 17 00:00:00 2001 From: Antonio Carlos Ribeiro Date: Sat, 27 Jul 2024 11:59:37 +0200 Subject: [PATCH 3/4] Simplify method name --- src/Repositories/Behaviors/HandleBlocks.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Repositories/Behaviors/HandleBlocks.php b/src/Repositories/Behaviors/HandleBlocks.php index 23623e2bb..fe56ddaa3 100644 --- a/src/Repositories/Behaviors/HandleBlocks.php +++ b/src/Repositories/Behaviors/HandleBlocks.php @@ -185,7 +185,7 @@ private function validate(array $formData, int $id, array $basicRules, array $tr foreach ($validator->messages()->getMessages() as $key => $errors) { foreach ($errors as $error) { if ($this->errorMessageIsOnNestedBlock($key)) { - $blockInfo = $this->makeBlocksValidationArrayInfoForNestedBlocks($id, $key, $formData, true); + $blockInfo = $this->makeValidationInfoForNestedBlocks($id, $key, $formData, true); $id = $blockInfo['nestedBlockId']; $key = $blockInfo['nestedField']; @@ -202,7 +202,7 @@ private function validate(array $formData, int $id, array $basicRules, array $tr foreach ($validator->messages()->getMessages() as $key => $errors) { foreach ($errors as $error) { if ($this->errorMessageIsOnNestedBlock($key)) { - $blockInfo = $this->makeBlocksValidationArrayInfoForNestedBlocks($id, $key, $formData, true); + $blockInfo = $this->makeValidationInfoForNestedBlocks($id, $key, $formData, true); $id = $blockInfo['nestedBlockId']; $key = $blockInfo['nestedField']; @@ -555,7 +555,7 @@ public function errorMessageIsOnNestedBlock($failedKey) return strpos($failedKey, '.content.') !== false; } - public function makeBlocksValidationArrayInfoForNestedBlocks($rootBlockId, $failedKey, $formData, $translated = false) + public function makeValidationInfoForNestedBlocks($rootBlockId, $failedKey, $formData, $translated = false) { $blockFilter = Str::beforeLast($failedKey, '.content.'); From f29cb91f105af92d8f4aebcf6c69767fcd909771 Mon Sep 17 00:00:00 2001 From: Antonio Carlos Ribeiro Date: Tue, 15 Oct 2024 11:22:49 +0200 Subject: [PATCH 4/4] Remove messages from block list --- src/Services/Blocks/Block.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Services/Blocks/Block.php b/src/Services/Blocks/Block.php index 3189867ef..63cbef342 100644 --- a/src/Services/Blocks/Block.php +++ b/src/Services/Blocks/Block.php @@ -341,7 +341,6 @@ public function toList(): Collection 'component' => $this->component, 'rules' => $this->getRules(), 'rulesForTranslatedFields' => $this->getRulesForTranslatedFields(), - 'messages' => $this->getMessages(), 'max' => $this->type === self::TYPE_REPEATER ? $this->max : null, ]); }