From ed96ba0276a2db878841836c403f60568c7a3a98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milan=20Felix=20=C5=A0ulc?= Date: Mon, 17 Jul 2023 11:54:36 +0200 Subject: [PATCH] Codebase: upgrade to PHP 8.0 --- .docs/README.md | 15 - composer.json | 2 +- phpstan.neon | 4 - src/Buttons/CreateButton.php | 14 +- src/Buttons/RemoveButton.php | 9 +- src/ComponentResolver.php | 36 +- src/DI/MultiplierExtension.php | 12 +- .../Extension/Node/MultiplierAddNode.php | 27 +- src/Latte/Extension/Node/MultiplierNode.php | 10 +- .../Extension/Node/MultiplierRemoveNode.php | 32 +- src/Macros/MultiplierMacros.php | 104 ----- src/Multiplier.php | 430 +++++++++--------- src/Submitter.php | 3 +- tests/includes/MultiplierBuilder.php | 2 + tests/unit/LatteTest.php | 7 +- tests/unit/templates/macros.latte | 6 +- 16 files changed, 272 insertions(+), 441 deletions(-) delete mode 100644 src/Macros/MultiplierMacros.php diff --git a/.docs/README.md b/.docs/README.md index 976537b..4744313 100644 --- a/.docs/README.md +++ b/.docs/README.md @@ -44,21 +44,6 @@ $multiplier->addCreateButton('Add 5', 5); // add five containers ### Macros -Latte 2 - -```latte -{form multiplier} -
- - {btnRemove 'class' => 'myClass'} -
- {btnCreate multiplier class => myClass} - {btnCreate $form[multiplier]:5} -{/form} -``` - -Latte 3 - ```latte {form multiplier}
diff --git a/composer.json b/composer.json index ec7954f..0a106bd 100644 --- a/composer.json +++ b/composer.json @@ -9,7 +9,7 @@ "multiplier" ], "require": { - "php": ">=7.2", + "php": ">=8.0", "nette/forms": "^3.1.0" }, "require-dev": { diff --git a/phpstan.neon b/phpstan.neon index 6983d58..2a23b2d 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -16,7 +16,3 @@ parameters: paths: - src - .docs - - excludePaths: - analyse: - - src/Macros/MultiplierMacros.php diff --git a/src/Buttons/CreateButton.php b/src/Buttons/CreateButton.php index 42a35b8..0ad01b9 100644 --- a/src/Buttons/CreateButton.php +++ b/src/Buttons/CreateButton.php @@ -11,20 +11,18 @@ final class CreateButton use SmartObject; - /** @var string|null */ - private $caption; + /** @var callable[] */ + public array $onCreate = []; - /** @var int */ - private $copyCount; + private ?string $caption = null; - /** @var callable[] */ - public $onCreate = []; + private int $copyCount; /** @var mixed[]|null */ - private $validationScope = null; + private ?array $validationScope = null; /** @var string[] */ - private $classes = []; + private array $classes = []; public function __construct(?string $caption, int $copyCount = 1) { diff --git a/src/Buttons/RemoveButton.php b/src/Buttons/RemoveButton.php index 55c4db6..df99e56 100644 --- a/src/Buttons/RemoveButton.php +++ b/src/Buttons/RemoveButton.php @@ -11,14 +11,13 @@ final class RemoveButton use SmartObject; - /** @var string|null */ - private $caption; - /** @var callable[] */ - public $onCreate = []; + public array $onCreate = []; + + private ?string $caption = null; /** @var string[] */ - private $classes = []; + private array $classes = []; public function __construct(?string $caption) { diff --git a/src/ComponentResolver.php b/src/ComponentResolver.php index 110532c..ae654cd 100644 --- a/src/ComponentResolver.php +++ b/src/ComponentResolver.php @@ -2,40 +2,31 @@ namespace Contributte\FormMultiplier; -use Nette\Utils\Strings; - final class ComponentResolver { - /** @var string|int|null */ - private $removeId; + private string|int|null $removeId = null; - /** @var bool */ - private $removeAction = false; + private bool $removeAction = false; - /** @var bool */ - private $createAction = false; + private bool $createAction = false; - /** @var int */ - private $createNum = 1; + private int $createNum = 1; /** @var mixed[] */ - private $httpData = []; + private array $httpData = []; - /** @var int|null */ - private $maxCopies; + private ?int $maxCopies = null; /** @var mixed[] */ - private $purgedHttpData; + private ?array $purgedHttpData = null; /** @var mixed[] */ - private $defaults; + private array $defaults = []; - /** @var int */ - private $minCopies; + private int $minCopies; - /** @var bool */ - private $reached = false; + private bool $reached = false; /** * @param mixed[] $httpData @@ -49,7 +40,7 @@ public function __construct(array $httpData, array $defaults, ?int $maxCopies, i $this->minCopies = $minCopies; foreach ($httpData as $index => $_) { - if (Strings::startsWith((string) $index, Multiplier::SUBMIT_CREATE_NAME)) { + if (str_starts_with((string) $index, Multiplier::SUBMIT_CREATE_NAME)) { $this->createAction = true; $num = substr($index, 18); if ($num) { @@ -131,10 +122,7 @@ public function isRemoveAction(): bool return $this->removeAction; } - /** - * @return int|string|null - */ - public function getRemoveId() + public function getRemoveId(): int|string|null { return $this->removeId; } diff --git a/src/DI/MultiplierExtension.php b/src/DI/MultiplierExtension.php index a56f93b..8f10cf6 100644 --- a/src/DI/MultiplierExtension.php +++ b/src/DI/MultiplierExtension.php @@ -2,9 +2,8 @@ namespace Contributte\FormMultiplier\DI; -use Contributte\FormMultiplier\Macros\MultiplierMacros; +use Contributte\FormMultiplier\Latte\Extension\MultiplierExtension as LatteMultiplierExtension; use Contributte\FormMultiplier\Multiplier; -use Latte\Engine; use Nette\Bridges\ApplicationLatte\LatteFactory; use Nette\DI\CompilerExtension; use Nette\DI\Definitions\FactoryDefinition; @@ -35,18 +34,13 @@ public function beforeCompile(): void sprintf( 'latte.latteFactory service definition must be of type %s, not %s', FactoryDefinition::class, - get_class($factory) + $factory::class ) ); } $resultDefinition = $factory->getResultDefinition(); - - if (version_compare(Engine::VERSION, '3', '<')) { // @phpstan-ignore-line - $resultDefinition->addSetup(MultiplierMacros::class . '::install(?->getCompiler())', ['@self']); - } else { - $resultDefinition->addSetup('addExtension', [new Statement(\Contributte\FormMultiplier\Latte\Extension\MultiplierExtension::class)]); - } + $resultDefinition->addSetup('addExtension', [new Statement(LatteMultiplierExtension::class)]); } public function afterCompile(ClassType $class): void diff --git a/src/Latte/Extension/Node/MultiplierAddNode.php b/src/Latte/Extension/Node/MultiplierAddNode.php index 6e67ce0..98a67ba 100644 --- a/src/Latte/Extension/Node/MultiplierAddNode.php +++ b/src/Latte/Extension/Node/MultiplierAddNode.php @@ -10,22 +10,15 @@ use Latte\Compiler\Nodes\StatementNode; use Latte\Compiler\PrintContext; use Latte\Compiler\Tag; -use LogicException; -use Nette\ComponentModel\IComponent; -use Nette\Forms\Container; -use Nette\Forms\SubmitterControl; final class MultiplierAddNode extends StatementNode { - /** @var ExpressionNode */ - public $name; + public ExpressionNode $name; - /** @var ArrayNode */ - public $attributes; + public ArrayNode $attributes; - /** @var ExpressionNode */ - public $part; + public ExpressionNode $part; public static function create(Tag $tag): self { @@ -47,6 +40,11 @@ public static function create(Tag $tag): self return $node; } + public static function getCreateButton(Multiplier $multiplier, int|string $buttonId): ?Submitter + { + return $multiplier->getCreateButtons()[$buttonId] ?? null; + } + public function print(PrintContext $context): string { return $context->format( @@ -66,18 +64,11 @@ public function print(PrintContext $context): string ); } - /** - * @param int|string $buttonId - */ - public static function getCreateButton(Multiplier $multiplier, $buttonId): ?Submitter - { - return $multiplier->getCreateButtons()[$buttonId] ?? null; - } - public function &getIterator(): \Generator { yield $this->name; yield $this->attributes; yield $this->part; } + } diff --git a/src/Latte/Extension/Node/MultiplierNode.php b/src/Latte/Extension/Node/MultiplierNode.php index c1ea8fd..a881d79 100644 --- a/src/Latte/Extension/Node/MultiplierNode.php +++ b/src/Latte/Extension/Node/MultiplierNode.php @@ -7,18 +7,15 @@ use Latte\Compiler\Nodes\Php\ExpressionNode; use Latte\Compiler\Nodes\Php\Scalar\StringNode; use Latte\Compiler\Nodes\StatementNode; -use Latte\Compiler\Position; use Latte\Compiler\PrintContext; use Latte\Compiler\Tag; final class MultiplierNode extends StatementNode { - /** @var ExpressionNode */ - public $name; + public ExpressionNode $name; - /** @var AreaNode */ - public $content; + public AreaNode $content; /** * @return Generator @@ -28,7 +25,7 @@ public static function create(Tag $tag): Generator $tag->outputMode = $tag::OutputRemoveIndentation; $tag->expectArguments(); - $node = new static; + $node = new static(); $node->name = $tag->parser->parseUnquotedStringOrExpression(); [$node->content] = yield; @@ -57,6 +54,7 @@ public function print(PrintContext $context): string $this->content ); } + public function &getIterator(): \Generator { yield $this->name; diff --git a/src/Latte/Extension/Node/MultiplierRemoveNode.php b/src/Latte/Extension/Node/MultiplierRemoveNode.php index bf1f370..00acc80 100644 --- a/src/Latte/Extension/Node/MultiplierRemoveNode.php +++ b/src/Latte/Extension/Node/MultiplierRemoveNode.php @@ -10,13 +10,11 @@ use LogicException; use Nette\ComponentModel\IComponent; use Nette\Forms\Container; -use Nette\Forms\SubmitterControl; final class MultiplierRemoveNode extends StatementNode { - /** @var ArrayNode */ - public $attributes; + public ArrayNode $attributes; public static function create(Tag $tag): self { @@ -26,20 +24,6 @@ public static function create(Tag $tag): self return $node; } - public function print(PrintContext $context): string - { - return $context->format( - 'if ($ʟ_input = %raw::getRemoveButton($this->global->formsStack)) {' - . 'echo $ʟ_input->getControl()' - . ($this->attributes->items ? '->addAttributes(%1.node)' : '') - . ';' - . '} %2.line', - self::class, - $this->attributes, - $this->position - ); - } - /** * @param Container[] $formsStack */ @@ -54,6 +38,20 @@ public static function getRemoveButton(array $formsStack): ?IComponent return $container->getComponent(Multiplier::SUBMIT_REMOVE_NAME, false); } + public function print(PrintContext $context): string + { + return $context->format( + 'if ($ʟ_input = %raw::getRemoveButton($this->global->formsStack)) {' + . 'echo $ʟ_input->getControl()' + . ($this->attributes->items ? '->addAttributes(%1.node)' : '') + . ';' + . '} %2.line', + self::class, + $this->attributes, + $this->position + ); + } + public function &getIterator(): \Generator { yield $this->attributes; diff --git a/src/Macros/MultiplierMacros.php b/src/Macros/MultiplierMacros.php deleted file mode 100644 index c76a1bf..0000000 --- a/src/Macros/MultiplierMacros.php +++ /dev/null @@ -1,104 +0,0 @@ -addMacro('multiplier', [$me, 'multiplierMacro'], 'array_pop($this->global->formsStack); $formContainer = $_form = end($this->global->formsStack); }'); - $me->addMacro('btnCreate', [$me, 'createMacro']); - $me->addMacro('btnRemove', [$me, 'removeMacro']); - } - - public function createMacro(MacroNode $node, PhpWriter $writer): string - { - if ($node->modifiers) { - throw new CompileException('Modifiers are not allowed in ' . $node->getNotation()); - } - - $words = $node->tokenizer->fetchWords(); - if (!$words) { - throw new CompileException('Missing name in ' . $node->getNotation()); - } - - $node->replaced = true; - $name = array_shift($words); - if (isset($words[0]) && is_numeric($words[0])) { - $copyCount = array_shift($words); - } else { - $copyCount = 1; - } - - $code = $name[0] === '$' ? '$_multiplier = is_object(%0.word) ? %0.word : end($this->global->formsStack)[%0.word];' : '$_multiplier = end($this->global->formsStack)[%0.word];'; - $code .= 'if (isset($_multiplier->getCreateButtons()[%1.word])) {'; - $code .= '$_input = $_multiplier->getCreateButtons()[%1.word];'; - - return $writer->write( - $code - . 'echo $_input' - . '->%2.raw' - . ($node->tokenizer->isNext() ? '->addAttributes(%node.array);' : ';') - . '}', - $name, - (string) $copyCount, - $words ? 'getControlPart(' . implode(', ', array_map([$writer, 'formatWord'], $words)) . ')' : 'getControl()' - ); - } - - public function removeMacro(MacroNode $node, PhpWriter $writer): string - { - if ($node->modifiers) { - throw new CompileException('Modifiers are not allowed in ' . $node->getNotation()); - } - - $code = '$_tmp = end($this->global->formsStack);'; - $code .= '$_input = isset($_tmp[\'' . Multiplier::SUBMIT_REMOVE_NAME . '\']) ? $_tmp[\'' . Multiplier::SUBMIT_REMOVE_NAME . '\'] : null;'; - $code .= 'if ($_input) {'; - $code .= 'echo $_input->getControl()'; - $node->replaced = true; - - return $writer->write( - $code - . ($node->tokenizer->isNext() ? '->addAttributes(%node.array);' : ';') - . '}' - ); - } - - public function multiplierMacro(MacroNode $node, PhpWriter $writer): string - { - if ($node->modifiers) { - throw new CompileException('Modifiers are not allowed in ' . $node->getNotation()); - } - - $words = $node->tokenizer->fetchWords(); - if (!$words) { - throw new CompileException('Missing name in ' . $node->getNotation()); - } - - $node->replaced = true; - $name = array_shift($words); - - return $writer->write( - ( - $name[0] === '$' ? - '$_multiplier = is_object(%0.word) ? %0.word : end($this->global->formsStack)[%0.word];' : // variable - '$_multiplier = end($this->global->formsStack)[%0.word];' // string - ) - . 'foreach ($_multiplier->getContainers() as $_multiplier) {' . "\n" - . '$this->global->formsStack[] = $formContainer = $_multiplier', - $name - ); - } - -} diff --git a/src/Multiplier.php b/src/Multiplier.php index f148d4e..435bb21 100644 --- a/src/Multiplier.php +++ b/src/Multiplier.php @@ -22,62 +22,51 @@ class Multiplier extends Container public const SUBMIT_REMOVE_NAME = 'multiplier_remover'; - /** @var Form|null */ - private $form; + /** @var callable[] */ + public array $onCreate = []; + + /** @var callable[] */ + public array $onRemove = []; - /** @var bool */ - private $attachedCalled = false; + /** @var callable[] */ + public array $onCreateComponents = []; /** @var callable */ protected $factory; - /** @var int */ - protected $copyNumber; + protected int $copyNumber; - /** @var int */ - protected $number = 0; + protected int $number = 0; - /** @var bool */ - protected $created = false; + protected bool $created = false; /** @var mixed[] */ - protected $values = []; + protected array $values = []; - /** @var bool */ - protected $erase = false; + protected bool $erase = false; /** @var CreateButton[] */ - protected $createButtons = []; + protected array $createButtons = []; - /** @var RemoveButton|null */ - protected $removeButton; + protected ?RemoveButton $removeButton = null; /** @var mixed[] */ - protected $httpData = []; - - /** @var int|null */ - protected $maxCopies = null; + protected array $httpData = []; - /** @var int */ - protected $totalCopies = 0; + protected ?int $maxCopies = null; - /** @var int */ - protected $minCopies = 1; + protected int $totalCopies = 0; - /** @var bool */ - protected $resetKeys = true; + protected int $minCopies = 1; - /** @var callable[] */ - public $onCreate = []; + protected bool $resetKeys = true; - /** @var callable[] */ - public $onRemove = []; + /** @var Container[] */ + protected array $noValidate = []; - /** @var callable[] */ - public $onCreateComponents = []; + private ?Form $form = null; - /** @var Container[] */ - protected $noValidate = []; + private bool $attachedCalled = false; public function __construct(callable $factory, int $copyNumber = 1, ?int $maxCopies = null) { @@ -109,25 +98,23 @@ public function __construct(callable $factory, int $copyNumber = 1, ?int $maxCop $this->monitor(self::class, [$this, 'whenAttached']); } - public function getForm(bool $throw = true): ?Form + public static function register(string $name = 'addMultiplier'): void { - if ($this->form) { - return $this->form; - } + Container::extensionMethod($name, function (Container $form, $name, $factory, $copyNumber = 1, $maxCopies = null) { + $multiplier = new Multiplier($factory, $copyNumber, $maxCopies); + $multiplier->setCurrentGroup($form->getCurrentGroup()); - return parent::getForm($throw); + return $form[$name] = $multiplier; + }); } - protected function whenAttached(): void + public function getForm(bool $throw = true): ?Form { - if ($this->attachedCalled) { - return; + if ($this->form) { + return $this->form; } - $this->loadHttpData(); - $this->createCopies(); - - $this->attachedCalled = true; + return parent::getForm($throw); } public function setResetKeys(bool $reset = true): self @@ -176,50 +163,29 @@ public function addCreateButton(?string $caption = null, int $copyCount = 1): Cr return $this->createButtons[$copyCount] = new CreateButton($caption, $copyCount); } - protected function onCreateEvent(): void - { - foreach ($this->onCreate as $callback) { - foreach ($this->getContainers() as $container) { - $callback($container); - } - } - } - - protected function onRemoveEvent(): void - { - foreach ($this->onRemove as $callback) { - $callback($this); - } - } - - protected function isValidMaxCopies(): bool - { - return $this->maxCopies === null || $this->totalCopies < $this->maxCopies; - } - /** * @param Control[]|null $controls */ public function validate(?array $controls = null): void { - /** @var Control[] $controls */ - $controls = $controls ?? iterator_to_array($this->getComponents()); + /** @var Control[] $components */ + $components = $controls ?? iterator_to_array($this->getComponents()); - foreach ($controls as $index => $control) { + foreach ($components as $index => $control) { foreach ($this->noValidate as $item) { if ($control === $item) { - unset($controls[$index]); + unset($components[$index]); } } } - parent::validate($controls); + parent::validate($components); } /** * @param mixed[]|object $defaults */ - public function addCopy(?int $number = null, $defaults = []): Container + public function addCopy(?int $number = null, array|object $defaults = []): Container { if (!is_numeric($number)) { $number = $this->createNumber(); @@ -244,57 +210,6 @@ public function addCopy(?int $number = null, $defaults = []): Container return $container; } - private function createComponents(ComponentResolver $resolver): void - { - $containers = []; - - // Components from httpData - if ($this->isFormSubmitted()) { - foreach ($resolver->getValues() as $number => $_) { - $containers[] = $container = $this->addCopy($number); - - /** @var BaseControl $control */ - foreach ($container->getControls() as $control) { - $control->loadHttpData(); - } - } - } else { // Components from default values - foreach ($resolver->getDefaults() as $number => $values) { - $containers[] = $this->addCopy($number, $values); - } - } - - // Default number of copies - if (!$this->isFormSubmitted() && !$this->values) { - $copyNumber = $this->copyNumber; - while ($copyNumber > 0 && $this->isValidMaxCopies()) { - $containers[] = $container = $this->addCopy(); - $copyNumber--; - } - } - - // Dynamic - foreach ($this->onCreateComponents as $callback) { - $callback($this); - } - - // New containers, if create button hitted - if ($this->form !== null && $resolver->isCreateAction() && $this->form->isValid()) { - $count = $resolver->getCreateNum(); - while ($count > 0 && $this->isValidMaxCopies()) { - $this->noValidate[] = $containers[] = $container = $this->addCopy(); - $container->setValues($this->createContainer()->getValues('array')); - $count--; - } - } - - if ($this->removeButton && $this->totalCopies <= $this->minCopies) { - foreach ($containers as $container) { - $this->detachRemoveButton($container); - } - } - } - public function createCopies(): void { if ($this->created === true) { @@ -327,37 +242,133 @@ public function createCopies(): void $this->onCreateEvent(); } - private function detachCreateButtons(): void + /** + * @return Submitter[] + */ + public function getCreateButtons(): array { + if ($this->maxCopies !== null && $this->totalCopies >= $this->maxCopies) { + return []; + } + + $buttons = []; foreach ($this->createButtons as $button) { - $this->removeComponentProperly($this->getComponent($button->getComponentName())); + $buttons[$button->getCopyCount()] = $this->getComponent($button->getComponentName()); } + + return $buttons; } - private function attachCreateButtons(): void + /** + * @internal + */ + public function resetFormEvents(): void { - foreach ($this->createButtons as $button) { - $this->addComponent($button->create($this), $button->getComponentName()); + if ($this->form === null) { + return; } + + $this->form->onSuccess = $this->form->onError = $this->form->onSubmit = []; } - private function detachRemoveButton(Container $container): void + /** + * @param Control[]|null $controls + * @return object|mixed[] + * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingAnyTypeHint + */ + public function getValues($returnType = null, ?array $controls = null): object|array { - $button = $container->getComponent(self::SUBMIT_REMOVE_NAME); - if ($this->getCurrentGroup() !== null) { - $this->getCurrentGroup()->remove($button); + if (!$this->resetKeys) { + return parent::getValues($returnType, $controls); } - $container->removeComponent($button); + /** @var mixed[] $values */ + $values = parent::getValues('array', $controls); + $values = array_values($values); + + $returnType = $returnType === true ? 'array' : $returnType; // @phpstan-ignore-line nette backwards compatibility + + return $returnType === 'array' ? $values : ArrayHash::from($values); } - private function attachRemoveButton(Container $container): void + /** + * @return Iterator|Control[] + */ + public function getControls(): Iterator { - if (!$this->removeButton) { + $this->createCopies(); + + return parent::getControls(); + } + + /** + * @return Iterator + */ + public function getContainers(): Iterator + { + $this->createCopies(); + + /** @var Iterator $containers */ + $containers = $this->getComponents(false, Container::class); + + return $containers; + } + + /** + * @param mixed[]|object $values + * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint + */ + public function setValues($values, bool $erase = false): self + { + $values = $values instanceof Traversable ? iterator_to_array($values) : (array) $values; + + $this->values = $values; + $this->erase = $erase; + + if ($this->created) { + foreach ($this->getContainers() as $container) { + $this->removeComponent($container); + } + + $this->created = false; + $this->detachCreateButtons(); + $this->createCopies(); + } + + return $this; + } + + protected function whenAttached(): void + { + if ($this->attachedCalled) { return; } - $container->addComponent($this->removeButton->create($this), self::SUBMIT_REMOVE_NAME); + $this->loadHttpData(); + $this->createCopies(); + + $this->attachedCalled = true; + } + + protected function onCreateEvent(): void + { + foreach ($this->onCreate as $callback) { + foreach ($this->getContainers() as $container) { + $callback($container); + } + } + } + + protected function onRemoveEvent(): void + { + foreach ($this->onRemove as $callback) { + $callback($this); + } + } + + protected function isValidMaxCopies(): bool + { + return $this->maxCopies === null || $this->totalCopies < $this->maxCopies; } protected function isFormSubmitted(): bool @@ -372,7 +383,6 @@ protected function loadHttpData(): void } } - protected function createNumber(): int { $count = iterator_count($this->getComponents(false, Form::class)); @@ -405,23 +415,6 @@ protected function createContainer(): Container return $control; } - /** - * @return Submitter[] - */ - public function getCreateButtons(): array - { - if ($this->maxCopies !== null && $this->totalCopies >= $this->maxCopies) { - return []; - } - - $buttons = []; - foreach ($this->createButtons as $button) { - $buttons[$button->getCopyCount()] = $this->getComponent($button->getComponentName()); - } - - return $buttons; - } - /** * Return name of first submit button */ @@ -449,95 +442,88 @@ protected function removeComponentProperly(IComponent $component): void $this->removeComponent($component); } - /** - * @internal - */ - public function resetFormEvents(): void + private function createComponents(ComponentResolver $resolver): void { - if ($this->form === null) { - return; - } + $containers = []; - $this->form->onSuccess = $this->form->onError = $this->form->onSubmit = []; - } + // Components from httpData + if ($this->isFormSubmitted()) { + foreach ($resolver->getValues() as $number => $_) { + $containers[] = $container = $this->addCopy($number); - /** - * @param string|object|null $returnType - * @param Control[]|null $controls - * @return object|mixed[] - * @phpcsSuppress SlevomatCodingStandard.TypeHints.TypeHintDeclaration.MissingParameterTypeHint - */ - public function getValues($returnType = null, ?array $controls = null) - { - if (!$this->resetKeys) { - return parent::getValues($returnType, $controls); + /** @var BaseControl $control */ + foreach ($container->getControls() as $control) { + $control->loadHttpData(); + } + } + } else { // Components from default values + foreach ($resolver->getDefaults() as $number => $values) { + $containers[] = $this->addCopy($number, $values); + } } - /** @var mixed[] $values */ - $values = parent::getValues('array', $controls); - $values = array_values($values); + // Default number of copies + if (!$this->isFormSubmitted() && !$this->values) { + $copyNumber = $this->copyNumber; + while ($copyNumber > 0 && $this->isValidMaxCopies()) { + $containers[] = $container = $this->addCopy(); + $copyNumber--; + } + } - $returnType = $returnType === true ? 'array' : $returnType; // @phpstan-ignore-line nette backwards compatibility - return $returnType === 'array' ? $values : ArrayHash::from($values); - } + // Dynamic + foreach ($this->onCreateComponents as $callback) { + $callback($this); + } - /** - * @return Iterator|Control[] - */ - public function getControls(): Iterator - { - $this->createCopies(); + // New containers, if create button hitted + if ($this->form !== null && $resolver->isCreateAction() && $this->form->isValid()) { + $count = $resolver->getCreateNum(); + while ($count > 0 && $this->isValidMaxCopies()) { + $this->noValidate[] = $containers[] = $container = $this->addCopy(); + $container->setValues($this->createContainer()->getValues('array')); + $count--; + } + } - return parent::getControls(); + if ($this->removeButton && $this->totalCopies <= $this->minCopies) { + foreach ($containers as $container) { + $this->detachRemoveButton($container); + } + } } - /** - * @return Iterator - */ - public function getContainers(): Iterator + private function detachCreateButtons(): void { - $this->createCopies(); - - /** @var Iterator $containers */ - $containers = $this->getComponents(false, Container::class); - return $containers; + foreach ($this->createButtons as $button) { + $this->removeComponentProperly($this->getComponent($button->getComponentName())); + } } - /** - * @param mixed[]|object $values - */ - public function setValues($values, bool $erase = false): self + private function attachCreateButtons(): void { - if ($values instanceof Traversable) { - $values = iterator_to_array($values); - } else { - $values = (array) $values; + foreach ($this->createButtons as $button) { + $this->addComponent($button->create($this), $button->getComponentName()); } + } - $this->values = $values; - $this->erase = $erase; - - if ($this->created) { - foreach ($this->getContainers() as $container) { - $this->removeComponent($container); - } - - $this->created = false; - $this->detachCreateButtons(); - $this->createCopies(); + private function detachRemoveButton(Container $container): void + { + $button = $container->getComponent(self::SUBMIT_REMOVE_NAME); + if ($this->getCurrentGroup() !== null) { + $this->getCurrentGroup()->remove($button); } - return $this; + $container->removeComponent($button); } - public static function register(string $name = 'addMultiplier'): void + private function attachRemoveButton(Container $container): void { - Container::extensionMethod($name, function (Container $form, $name, $factory, $copyNumber = 1, $maxCopies = null) { - $multiplier = new Multiplier($factory, $copyNumber, $maxCopies); - $multiplier->setCurrentGroup($form->getCurrentGroup()); + if (!$this->removeButton) { + return; + } - return $form[$name] = $multiplier; - }); + $container->addComponent($this->removeButton->create($this), self::SUBMIT_REMOVE_NAME); } } diff --git a/src/Submitter.php b/src/Submitter.php index 49e4037..89ba88d 100644 --- a/src/Submitter.php +++ b/src/Submitter.php @@ -7,8 +7,7 @@ final class Submitter extends SubmitButton implements ISubmitter { - /** @var int */ - private $copyCount = 1; + private int $copyCount = 1; public function __construct(?string $caption, int $copyCount = 1) { diff --git a/tests/includes/MultiplierBuilder.php b/tests/includes/MultiplierBuilder.php index 4b5735b..9c851cc 100644 --- a/tests/includes/MultiplierBuilder.php +++ b/tests/includes/MultiplierBuilder.php @@ -112,6 +112,8 @@ public function createForm(): Form $form->addSubmit('send'); + $form->onSuccess[] = function(Form $form) {}; + return $form; } diff --git a/tests/unit/LatteTest.php b/tests/unit/LatteTest.php index da4310d..7eeb366 100644 --- a/tests/unit/LatteTest.php +++ b/tests/unit/LatteTest.php @@ -1,11 +1,12 @@ latte = $latte = new Engine(); - MultiplierMacros::install($latte->getCompiler()); - FormMacros::install($latte->getCompiler()); + $latte->addExtension(new FormsExtension()); + $latte->addExtension(new MultiplierExtension()); } public function testBtnCreate() diff --git a/tests/unit/templates/macros.latte b/tests/unit/templates/macros.latte index 45852cb..e9dc615 100644 --- a/tests/unit/templates/macros.latte +++ b/tests/unit/templates/macros.latte @@ -1,4 +1,4 @@ {form $form} - {btnCreate m class => myClass} - {btnCreate m:2 class => myClass} -{/form} \ No newline at end of file + {multiplier:add m class => myClass} + {multiplier:add m:2 class => myClass} +{/form}