From 14c49175d87bfd81c69aa0c538ad49c375d629dd Mon Sep 17 00:00:00 2001 From: David Grudl Date: Sun, 19 May 2024 13:43:53 +0200 Subject: [PATCH] ChoiceControl, MultiChoiceControl: choices are internally stored in $choices array --- src/Forms/Controls/ChoiceControl.php | 41 ++++++++++++++--------- src/Forms/Controls/MultiChoiceControl.php | 35 +++++++++---------- 2 files changed, 40 insertions(+), 36 deletions(-) diff --git a/src/Forms/Controls/ChoiceControl.php b/src/Forms/Controls/ChoiceControl.php index 38229031..c3ece118 100644 --- a/src/Forms/Controls/ChoiceControl.php +++ b/src/Forms/Controls/ChoiceControl.php @@ -10,6 +10,7 @@ namespace Nette\Forms\Controls; use Nette; +use Nette\Utils\Arrays; /** @@ -21,7 +22,9 @@ abstract class ChoiceControl extends BaseControl { private bool $checkDefaultValue = true; - private array $items = []; + + /** @var list */ + private array $choices = []; public function __construct($label = null, ?array $items = null) @@ -46,26 +49,28 @@ public function loadHttpData(): void /** * Sets selected item (by key). - * @param string|int|\BackedEnum|null $value + * @param string|int|\BackedEnum|\Stringable|null $value * @return static * @internal */ public function setValue($value) { - if ($value instanceof \BackedEnum) { + if ($value === null) { + $this->value = null; + return $this; + } elseif ($value instanceof \BackedEnum) { $value = $value->value; + } elseif (!is_string($value) && !is_int($value) && !$value instanceof \Stringable) { // do ChoiceControl + throw new Nette\InvalidArgumentException(sprintf('Value must be scalar|enum|Stringable, %s given.', get_debug_type($value))); } - if ($this->checkDefaultValue && $value !== null && !array_key_exists((string) $value, $this->items)) { - $set = Nette\Utils\Strings::truncate( - implode(', ', array_map(fn($s) => var_export($s, return: true), array_keys($this->items))), - 70, - '...', - ); + $value = Arrays::toKey((string) $value); + if ($this->checkDefaultValue && !Arrays::some($this->choices, fn($choice) => $choice[0] === $value)) { + $set = Nette\Utils\Strings::truncate(implode(', ', array_map(fn($choice) => var_export($choice[0], return: true), $this->choices)), 70, '...'); throw new Nette\InvalidArgumentException("Value '$value' is out of allowed set [$set] in field '{$this->getName()}'."); } - $this->value = $value === null ? null : key([(string) $value => null]); + $this->value = $value; return $this; } @@ -76,8 +81,8 @@ public function setValue($value) */ public function getValue(): mixed { - return array_key_exists($this->value, $this->items) - ? $this->value + return $this->value !== null && ([$res] = Arrays::first($this->choices, fn($choice) => $choice[0] === $this->value)) + ? $res : null; } @@ -106,7 +111,10 @@ public function isFilled(): bool */ public function setItems(array $items, bool $useKeys = true) { - $this->items = $useKeys ? $items : array_combine($items, $items); + $this->choices = []; + foreach ($items as $k => $v) { + $this->choices[] = [$useKeys ? $k : Arrays::toKey((string) $v), $v]; + } return $this; } @@ -116,7 +124,7 @@ public function setItems(array $items, bool $useKeys = true) */ public function getItems(): array { - return $this->items; + return array_column($this->choices, 1, 0); } @@ -125,8 +133,9 @@ public function getItems(): array */ public function getSelectedItem(): mixed { - $value = $this->getValue(); - return $value === null ? null : $this->items[$value]; + return $this->value !== null && ([, $res] = Arrays::first($this->choices, fn($choice) => $choice[0] === $this->value)) + ? $res + : null; } diff --git a/src/Forms/Controls/MultiChoiceControl.php b/src/Forms/Controls/MultiChoiceControl.php index a1aade76..229e6974 100644 --- a/src/Forms/Controls/MultiChoiceControl.php +++ b/src/Forms/Controls/MultiChoiceControl.php @@ -10,6 +10,7 @@ namespace Nette\Forms\Controls; use Nette; +use Nette\Utils\Arrays; /** @@ -21,7 +22,9 @@ abstract class MultiChoiceControl extends BaseControl { private bool $checkDefaultValue = true; - private array $items = []; + + /** @var list */ + private array $choices = []; public function __construct($label = null, ?array $items = null) @@ -67,12 +70,8 @@ public function setValue($values) } $values = array_keys($flip); - if ($this->checkDefaultValue && ($diff = array_diff($values, array_keys($this->items)))) { - $set = Nette\Utils\Strings::truncate( - implode(', ', array_map(fn($s) => var_export($s, return: true), array_keys($this->items))), - 70, - '...', - ); + if ($this->checkDefaultValue && ($diff = array_diff($values, $tmp = array_column($this->choices, 0)))) { + $set = Nette\Utils\Strings::truncate(implode(', ', array_map(fn($s) => var_export($s, return: true), $tmp)), 70, '...'); $vals = (count($diff) > 1 ? 's' : '') . " '" . implode("', '", $diff) . "'"; throw new Nette\InvalidArgumentException("Value$vals are out of allowed set [$set] in field '{$this->getName()}'."); } @@ -87,7 +86,7 @@ public function setValue($values) */ public function getValue(): array { - return array_values(array_intersect($this->value, array_keys($this->items))); + return array_values(array_intersect($this->value, array_column($this->choices, 0))); } @@ -100,22 +99,16 @@ public function getRawValue(): array } - /** - * Is any item selected? - */ - public function isFilled(): bool - { - return $this->getValue() !== []; - } - - /** * Sets items from which to choose. * @return static */ public function setItems(array $items, bool $useKeys = true) { - $this->items = $useKeys ? $items : array_combine($items, $items); + $this->choices = []; + foreach ($items as $k => $v) { + $this->choices[] = [$useKeys ? $k : Arrays::toKey((string) $v), $v]; + } return $this; } @@ -125,7 +118,7 @@ public function setItems(array $items, bool $useKeys = true) */ public function getItems(): array { - return $this->items; + return array_column($this->choices, 1, 0); } @@ -134,7 +127,9 @@ public function getItems(): array */ public function getSelectedItems(): array { - return array_intersect_key($this->items, array_flip($this->value)); + $flip = array_flip($this->value); + $res = array_filter($this->choices, fn($choice) => isset($flip[$choice[0]])); + return array_column($res, 1, 0); }