diff --git a/src/TwigComponent/CHANGELOG.md b/src/TwigComponent/CHANGELOG.md index 8ddd89fb4b6..cc648c629e7 100644 --- a/src/TwigComponent/CHANGELOG.md +++ b/src/TwigComponent/CHANGELOG.md @@ -1,5 +1,9 @@ # CHANGELOG +## 2.26.0 + +- Add the ability to spread attributes from the `...` attribute + ## 2.25.2 - Fix `ComponentAttributes` rendering when using `StimulusAttributes` as default attributes diff --git a/src/TwigComponent/src/ComponentAttributes.php b/src/TwigComponent/src/ComponentAttributes.php index bd4029145e2..2176945d5b5 100644 --- a/src/TwigComponent/src/ComponentAttributes.php +++ b/src/TwigComponent/src/ComponentAttributes.php @@ -26,6 +26,11 @@ final class ComponentAttributes implements \Stringable, \IteratorAggregate, \Cou private const ALPINE_REGEX = '#^x-([a-z]+):[^:]+$#'; private const VUE_REGEX = '#^v-([a-z]+):[^:]+$#'; + private const SPREADABLE_ATTRIBUTE = '...'; + + /** @var array */ + private array $attributes = []; + /** @var array */ private array $rendered = []; @@ -33,9 +38,10 @@ final class ComponentAttributes implements \Stringable, \IteratorAggregate, \Cou * @param array $attributes */ public function __construct( - private array $attributes, + array $attributes, private readonly EscaperRuntime $escaper, ) { + $this->attributes = $this->handleAttributes($attributes); } public function __toString(): string @@ -103,6 +109,31 @@ public function __clone(): void $this->rendered = []; } + private function handleAttributes(array $attributes): array + { + $spreadAttributes = $attributes[self::SPREADABLE_ATTRIBUTE] ?? null; + + if (!$spreadAttributes) { + return $attributes; + } + + if ($spreadAttributes instanceof StimulusAttributes) { + $spreadAttributes = $spreadAttributes->toArray(); + } + + if ($spreadAttributes instanceof \Traversable) { + $spreadAttributes = iterator_to_array($spreadAttributes); + } + + if (!\is_array($spreadAttributes)) { + throw new \InvalidArgumentException(\sprintf('The "%s" attribute must be an array, "%s" given.', self::SPREADABLE_ATTRIBUTE, get_debug_type($spreadAttributes))); + } + + unset($attributes[self::SPREADABLE_ATTRIBUTE]); + + return [...$attributes, ...$spreadAttributes]; + } + public function render(string $attribute): ?string { if (null === $value = $this->attributes[$attribute] ?? null) { diff --git a/src/TwigComponent/src/ComponentFactory.php b/src/TwigComponent/src/ComponentFactory.php index 22619fb7842..d0003e5de14 100644 --- a/src/TwigComponent/src/ComponentFactory.php +++ b/src/TwigComponent/src/ComponentFactory.php @@ -113,12 +113,6 @@ public function mountFromObject(object $component, array $data, ComponentMetadat $attributes = $data[$attributesVar] ?? []; unset($data[$attributesVar]); - foreach ($data as $key => $value) { - if ($value instanceof \Stringable) { - $data[$key] = (string) $value; - } - } - return new MountedComponent( $componentMetadata->getName(), $component,