contributte / forms-multiplier

:repeat: Form multiplier & replicator for Nette Framework
https://contributte.org/packages/contributte/forms-multiplier.html
MIT License
26 stars 20 forks source link

Soft limits on number of copies #75

Open jtojnar opened 2 years ago

jtojnar commented 2 years ago

I have a registration for a team race, which allows the teams consisting of two to five people. For this, the minCopies and maxCopies parameters work great. But now I also want to allow a special race category that only admits pairs.

The form has a category SelectBox, to which I attached a validation rule to prevent submitting the form with too many participants than the category allows. But I would still like to disable adding more copies than allowed for the category. Only I cannot just change the maxCopies or it would also hide the “Add” button, preventing adding a member even after changing the category back.

Would a PR adding a feature to support this be accepted?

MartkCz commented 1 year ago

What about this solution?

$multiplier->onCreateComponents[] = function (Multiplier $multiplier): void {
    $form = $multiplier->getForm();

    if ($form->isSubmitted() && iterator_count($multiplier->getContainers()) >= 3) {
        $form->addError('Max limit of participants for this category reached.');
    }
};

If form is not valid new copy is not added.

jtojnar commented 1 year ago

Oh, looks like that will run before new copies are added, and that will happen only if the form is valid, which is disrupted by adding the error. Thanks, that does work great.

Now I just need to have the equivalent thing for removing team members. If I am reading the code right, I need to somehow prevent the remove action to be triggered before https://github.com/contributte/forms-multiplier/blob/5bafb566431294220f7723616d8417cc631d0a43/src/Multiplier.php#L253, since otherwise the ComponentResolver will remove the copy without any way to prevent it. But it needs to happen after the form is attached to be able to get the category from the form.

jtojnar commented 1 year ago

Apparently, your suggestion will prevent the form from being submitted. I needed something like this horror:

$multiplier->onCreateComponents[] = function (Multiplier $multiplier): void {
    $form = $multiplier->getForm();

    if (!$form->isSubmitted()) {
        return;
    }

    // 🙀
    $multiplierReflection = new \ReflectionClass(Multiplier::class);
    $httpData = $multiplierReflection->getProperty('httpData');
    $httpData->setAccessible(true);
    $values = $multiplierReflection->getProperty('values');
    $values->setAccessible(true);
    $resolver = new \Contributte\FormMultiplier\ComponentResolver($httpData->getValue($multiplier), $values->getValue($multiplier), $multiplier->getMaxCopies(), $multiplier->getMinCopies());

    $count = iterator_count($multiplier->getContainers());

    if ($resolver->isCreateAction() && $count >= 3) {
        $form->addError('Max limit of participants for this category reached.');
    }
};