juergenweb / FrontendForms

A module for ProcessWire CMS to create and validate forms on the frontend easily using the Valitron library.
MIT License
15 stars 1 forks source link

How to use Valitrons `arrayHasKeys` rule? #13

Closed donatasben closed 1 month ago

donatasben commented 2 months ago

Hi @juergenweb I was wondering, if there is a way to use one of Valitron's default validators arrayHasKeys on something like InputCheckboxMultiple? I want to set specific checkboxes to be required, but not any - default required rule on input needs at least any one of checkboxes to be checked, but I want to specify exactly which one(s).

How arrayHasKeys works is described here https://github.com/vlucas/valitron?tab=readme-ov-file#arrayhaskeys-fields-usage

It's not very clear how should I pass the correct options to Valitron. I try providing the name I use for $input->InputCheckboxMultiple($name) input, and send an array of required checkbox values (same as in $input->addOption($label, $value)). But it doesn't seem to work for me.

I might suspect that FrontendForms passes the rule options wrapped in array key options to Valitron:

// ValitronAPI.php line 75
return ['name' => $validator, 'options' => $options];

But Valitron expects three parameters for this rule:

$v->rule('arrayHasKeys', 'address', ['name', 'street', 'city']);

Any advice how could I achieve something like that? Maybe I should look in completely different direction?

juergenweb commented 1 month ago

Hello @donatasben

I think that this validator will not work for your usecase. What you need is to check for the value of each checkbox, but this validator checks for the keys and not the values. What you need is an in_array check and this validator is called "listContains". Here is an example:

$checkboxmultiarray = new \FrontendForms\InputCheckboxMultiple('multicheckbox');
                $checkboxmultiarray->setLabel('Please select 1 and 3');
                $checkboxmultiarray->addOption('Checkbox 1', '1');
                $checkboxmultiarray->addOption('Checkbox 2', '2');
                $checkboxmultiarray->addOption('Checkbox 3', '3');
                $checkboxmultiarray->setRule('required'); // optional
                $checkboxmultiarray->setRule('listContains' , '1');
                $checkboxmultiarray->setRule('listContains' , '3');
                $form->add($checkboxmultiarray);

As you can see, you need to add the required validator first. This is necessary, otherwise all other validators afterwards will not be taken into account if no checkbox is checked. -> Edit: This is not true, you do not need the required validator!!

Then you must define each required value with a new "listContains" validation rule. Unfortunately it is not possible to add all values as an array.

That is all. Let me know if it works now for you.

BTW: Are the placeholder forms working now in your case?

Best regards Jürgen

donatasben commented 1 month ago

Thanks @juergenweb! How could have I missed it :) However it doesn't fully work on my end if I try to use setRule('required') together with setRule('listContains','2'). It throws a 500 type error.

It seems that checkbox field data array doesn't exist at all among form post values while posting the form if none of checkbox options where checked, so the field value gets assigned a null, when it is expecting an array. SCR-20240801-bxpz The HTML of the form seem fine - the checkbox input has the name with array[] brackets, has value attributes, etc.

If I check at least one of multiple checkboxes - the value array exists among post data, the listContains kicks in, form goes through validation, and shows a validation error as expected.

So all in all: if I use one of required or listContains rules - form posting works, nothing breaks, only not as I want. If I use both rules together - it breaks.

Any ideas how to make both of them play together?

My code looks something like this:

$multibox = new \FrontendForms\InputCheckboxMultiple('multibox');
$multibox->setLabel('Please select');
$multibox->setRule('required');
$multibox_options = [
    ['label' => 'One', 'value'=>1],
    ['label' => 'Two', 'value'=>2, 'required'=>1],
    ['label' => 'Three', 'value'=>3],
];
foreach($multibox_options as $option){
    $multibox->addOption($option['label'], $option['value']);
    if (isset($option['required']) && $option['required'] == 1){
        $multibox->setRule('listContains', $option['value']);
    }
}
$form->add($multibox);
juergenweb commented 1 month ago

Hi @donatasben

I have changed something in the Form.php yesterday. Maybe this solves your problem. So please replace your Form.php with the one here on Github. It should work, I have tested it yesterday with my checkbox example without problems (with or without required and with or without a value selected). I have made the changes before I have answered your question yesterday.

Checkboxes are a little bit special, because they are allowed to have no value attribute at all, so the treatment is a little bit different.

So please try it! I can check it today in the late afternoon if it does not work, so please be patient.

Jürgen

juergenweb commented 1 month ago

OK, I have tested it and you are right. It does not work if no value is set. I will try to solve this problem today and I'll inform you if everything works as expected.

juergenweb commented 1 month ago

Hi @donatasben

I have done 2 small changes inside the Validator.php. Now it handles null values properly. This was a missing piece in Valitron.

I know it is not the best practice to change something inside an external library, but I guess Valitron will not be maintained anymore. The last changes were done a long time ago.

So please change your Valitron.php with the one here on Github and everything should work fine. I hope that there will be no negative side effects due to the changes, but it is unlikely.

The next update will include this changes (if there are no problems at all).

Let me know if it works for you now.