diff --git a/core/modules/sdc/src/Component/ComponentValidator.php b/core/modules/sdc/src/Component/ComponentValidator.php index 3e849798ebf2b70927cfd4644818f0aed1d84a42..57872670a320c613e03bd230986067746cbf4aec 100644 --- a/core/modules/sdc/src/Component/ComponentValidator.php +++ b/core/modules/sdc/src/Component/ComponentValidator.php @@ -5,6 +5,7 @@ use Drupal\sdc\Exception\InvalidComponentException; use Drupal\sdc\Plugin\Component; use Drupal\sdc\Utilities; +use JsonSchema\Constraints\Constraint; use JsonSchema\Validator; /** @@ -164,7 +165,7 @@ public function validateProps(array $context, Component $component): bool { $schema = Validator::arrayToObjectRecursive($schema); $props = Validator::arrayToObjectRecursive($props_raw); $validator = new Validator(); - $validator->validate($props, $schema); + $validator->validate($props, $schema, Constraint::CHECK_MODE_TYPE_CAST); $validator->getErrors(); if ($validator->isValid()) { return TRUE; @@ -183,7 +184,17 @@ function (array $error) use ($context): bool { return TRUE; } $message_parts = array_map( - static fn(array $error): string => sprintf("[%s] %s", $error['property'], $error['message']), + static function (array $error): string { + // We check the error message instead of values and definitions here + // because it's hard to access both given the possible complexity of a + // schema. Since this is a small non critical DX improvement error + // message checking should be sufficient. + if (str_contains($error['message'], 'NULL value found, but a ')) { + $error['message'] .= '. This may be because the property is empty instead of having data present. If possible fix the source data, use the |default() twig filter, or update the schema to allow multiple types.'; + } + + return sprintf("[%s] %s", $error['property'], $error['message']); + }, $errors ); $message = implode("/n", $message_parts); diff --git a/core/modules/sdc/tests/modules/sdc_test/components/array-to-object/array-to-object.component.yml b/core/modules/sdc/tests/modules/sdc_test/components/array-to-object/array-to-object.component.yml new file mode 100644 index 0000000000000000000000000000000000000000..1d70540cde52cec48d5eb4360f50c510964068cf --- /dev/null +++ b/core/modules/sdc/tests/modules/sdc_test/components/array-to-object/array-to-object.component.yml @@ -0,0 +1,8 @@ +$schema: https://git.drupalcode.org/project/sdc/-/raw/1.x/src/metadata.schema.json +name: Array to Object +props: + type: object + properties: + testProp: + title: 'Needs object' + type: object diff --git a/core/modules/sdc/tests/modules/sdc_test/components/array-to-object/array-to-object.twig b/core/modules/sdc/tests/modules/sdc_test/components/array-to-object/array-to-object.twig new file mode 100644 index 0000000000000000000000000000000000000000..695c56f89abfae299292e9b408dd5e38cc4cb96b --- /dev/null +++ b/core/modules/sdc/tests/modules/sdc_test/components/array-to-object/array-to-object.twig @@ -0,0 +1,3 @@ +<div> + {{ testProp }} +</div> diff --git a/core/modules/sdc/tests/src/Kernel/ComponentRenderTest.php b/core/modules/sdc/tests/src/Kernel/ComponentRenderTest.php index 747ff27ea5515518a1e361c02562b04ca3dd05f8..174a2dcbbe4d83f5a498da41f486c44669c08115 100644 --- a/core/modules/sdc/tests/src/Kernel/ComponentRenderTest.php +++ b/core/modules/sdc/tests/src/Kernel/ComponentRenderTest.php @@ -37,6 +37,7 @@ public function testRender(): void { $this->checkIncludeDataMapping(); $this->checkEmbedWithNested(); $this->checkPropValidation(); + $this->checkArrayObjectTypeCast(); $this->checkNonExistingComponent(); $this->checkLibraryOverrides(); $this->checkAttributeMerging(); @@ -171,6 +172,25 @@ protected function checkPropValidation(): void { } } + /** + * Ensure fuzzy coercing of arrays and objects works properly. + */ + protected function checkArrayObjectTypeCast(): void { + $content = ['test' => []]; + $build = [ + '#type' => 'inline_template', + '#context' => ['content' => $content], + '#template' => "{{ include('sdc_test:array-to-object', { testProp: content.test }, with_context = false) }}", + ]; + try { + $this->renderComponentRenderArray($build); + $this->addToAssertionCount(1); + } + catch (\Throwable $e) { + $this->fail('Empty array was not converted to object'); + } + } + /** * Ensures that including an invalid component creates an error. */