diff --git a/CHANGELOG.md b/CHANGELOG.md index c59dd8d..342d219 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +3.3.0 +===== + +* (feature) Add support for setting minimum and maximum count of selected `ChoiceField` options. + + 3.2.2 ===== diff --git a/src/Field/Definition/ChoiceField.php b/src/Field/Definition/ChoiceField.php index 4148236..04b5dcc 100644 --- a/src/Field/Definition/ChoiceField.php +++ b/src/Field/Definition/ChoiceField.php @@ -4,9 +4,11 @@ use Symfony\Component\Validator\Constraints\All; use Symfony\Component\Validator\Constraints\AtLeastOneOf; +use Symfony\Component\Validator\Constraints\Count; use Symfony\Component\Validator\Constraints\NotNull; use Symfony\Component\Validator\Constraints\Type; use Torr\Storyblok\Context\ComponentContext; +use Torr\Storyblok\Exception\InvalidFieldConfigurationException; use Torr\Storyblok\Field\Choices\ChoicesInterface; use Torr\Storyblok\Field\FieldType; use Torr\Storyblok\Visitor\DataVisitorInterface; @@ -20,9 +22,29 @@ public function __construct ( private readonly ChoicesInterface $choices, private readonly bool $allowMultiselect = false, private readonly int|string|\BackedEnum|null $defaultValue = null, + private readonly ?int $minimumNumberOfOptions = null, + private readonly ?int $maximumNumberOfOptions = null, ) { parent::__construct($label, $this->defaultValue); + + if (!$this->allowMultiselect && (null !== $this->minimumNumberOfOptions || null !== $this->maximumNumberOfOptions)) + { + throw new InvalidFieldConfigurationException(\sprintf( + "Can't configure minimum or maximum amount of options for single-select choice.", + )); + } + + if ( + null !== $this->minimumNumberOfOptions + && null !== $this->maximumNumberOfOptions + && $this->minimumNumberOfOptions > $this->maximumNumberOfOptions + ) + { + throw new InvalidFieldConfigurationException(\sprintf( + "The minimum number of options value can't be higher than the maximum", + )); + } } @@ -48,6 +70,8 @@ protected function toManagementApiData () : array "default_value" => $this->defaultValue instanceof \BackedEnum ? $this->defaultValue->value : $this->defaultValue, + "min_options" => $this->minimumNumberOfOptions, + "max_options" => $this->maximumNumberOfOptions, ], ); } @@ -57,19 +81,29 @@ protected function toManagementApiData () : array */ public function validateData (ComponentContext $context, array $contentPath, mixed $data, array $fullData) : void { - $allowedValueTypeConstraints = new AtLeastOneOf([ + $valueConstraints = new AtLeastOneOf([ new Type("string"), new Type("int"), ]); + if ($this->allowMultiselect && (null !== $this->minimumNumberOfOptions || null !== $this->maximumNumberOfOptions)) + { + $valueConstraints[] = new Count( + min: $this->minimumNumberOfOptions, + max: $this->maximumNumberOfOptions, + minMessage: "At least {{ limit }} option(s) must be selected.", + maxMessage: "You cannot specify more than {{ limit }} options.", + ); + } + $context->ensureDataIsValid( $contentPath, $this, $data, [ $this->allowMultiselect - ? new All([new NotNull(), $allowedValueTypeConstraints]) - : $allowedValueTypeConstraints, + ? new All([new NotNull(), $valueConstraints]) + : $valueConstraints, ], );