# Possible Bug validating non-text input

**URL:** <https://discourse.laminas.dev/t/possible-bug-validating-non-text-input/3418>\
**Category:** Components & MVC\
**Tags:** laminas-form, laminas-inputfilter\
**Created:** [July 29, 2023, 5:23am UTC](https://discourse.laminas.dev/t/possible-bug-validating-non-text-input/3418 "2023-07-29T05:23:56Z")\
**Posts on this page:** 15\
**Page:** 1

<div class="post-metadata">

**Author:** ![juliangorge](https://yyz2.discourse-cdn.com/flex032/user_avatar/discourse.laminas.dev/juliangorge/32/608_2.png) [@juliangorge](https://discourse.laminas.dev/u/juliangorge)\
**Post date:** [July 29, 2023, 5:23am UTC](https://discourse.laminas.dev/t/possible-bug-validating-non-text-input/3418/1 "2023-07-29T05:23:56Z")

</div>

Hey,

Just a little bit of context:  
I’m using OptionalInputFilter into my forms because I have multiple inputs depending of others. For example: if you have to complete a payment form between Bank payment or Credit card payment and all the inputs you can imagine.

Before OptionalInputFilter, [I read in blogs](https://olegkrivtsov.github.io/using-zend-framework-3-book/html/en/Advanced_Usage_of_Forms/Using_Validation_Groups.html) that people recommend to set your ValidationGroup array for each case, but when I was refactoring my code I found its not a good idea set this in Controller and I prefer to do all the stuff of Form inside itself (at least for me 🤷‍♂️).  
For that reason I began to use OptionalInputFilter.

Here my form class:

```auto
<?php
namespace Application\Form;

use Laminas\Form\Form;
use Laminas\InputFilter\OptionalInputFilter;

class BillingForm extends Form
{
    public function __construct()
    {
        parent::__construct('profileForm');
        $this->addElements();
        $this->addInputFilter();
    }

    protected function addElements()
    {
        $address = new Fieldset\Address($this->em);
        $this->add($address->get('address'));
        $this->add($address->get('address_height'));

        $this->add([
            'name' => 'submit',
            'type' => 'submit',
            'attributes' => [
                'class' => 'btn btn-sm btn-primary',
                'value' => 'Guardar cambios',
            ]
        ]);
    }

    protected function addInputFilter()
    {
        $inputFilter = $this->getInputFilter();
        $addressFilter = new OptionalInputFilter();
        $addressFilter->add([
            'name' => 'address',
            'required' => true,
            'filters' => [
                ['name' => 'StripTags'],
                ['name' => 'StringTrim'],
            ],
            'validators' => [
                [
                    'name' => 'StringLength',
                    'options' => [
                        'min' => 2,
                        'max' => 50
                    ],
                ],
            ],
        ]);

        $addressFilter->add([
            'name' => 'address_height',
            'required' => true,
            'filters' => [
                ['name' => 'StringTrim'],
            ],
            'validators' => [
                ['name' => 'Digits'],
                [
                    'name' => 'StringLength',
                    'options' => [
                        'max' => 10
                    ],
                ],
            ],
        ]);
        $inputFilter->add($addressFilter, 'address_inputs');
    }
}

```

And my Fieldset:

```auto

$this->add([
            'name' => 'address',
            'attributes' => [
                'required' => true,
                'class' => 'form-control required',
                'maxlength' => 50,
                'placeholder' => 'Calle'
            ],
            'options' => [
                'label' => 'Calle<sup class="red"><small>*</small></sup>',
                'label_options' => [
                    'disable_html_escape' => true,
                ],
            ],
        ]);

        $this->add([
            'name' => 'address_height',
            'type' => 'number',
            'attributes' => [
                'required' => true,
                'class' => 'form-control required',
                'maxlength' => 10,
                'placeholder' => 'Altura'
            ],
            'options' => [
                'label' => 'Altura<sup class="red"><small>*</small></sup>',
                'label_options' => [
                    'disable_html_escape' => true,
                ],
            ],
        ]);

```

When I submit the form, (using $form-\>setData($data) and check $form-\>getMessages() after $form-\>isValid() clause), I have this array:

`array(1) { ["address_height"]=> array(1) { ["isEmpty"]=> string(50) "A value is required and it can not be empty" }`

But … if I change type attribute from number to text, this input is valid.

What can it be?  
This occurs with OptionalInputFilter and InputFilter, I just named it because there is not enough information about how to use it and I like to record this issue with this usage for other people that may google-it and find there are people using this 😆.

Thanks.  
Julian

---

<div class="post-metadata">

**Author:** ![Tyrsson](https://yyz2.discourse-cdn.com/flex032/user_avatar/discourse.laminas.dev/tyrsson/32/1664_2.png) [@Tyrsson](https://discourse.laminas.dev/u/Tyrsson)\
**Post date:** [July 29, 2023, 6:06am UTC](https://discourse.laminas.dev/t/possible-bug-validating-non-text-input/3418/2 "2023-07-29T06:06:39Z")

</div>

If address\_height is required, it can not be empty.

This “may” be causing your issue:

> **[NotEmpty - laminas-validator - Laminas Docs](https://docs.laminas.dev/laminas-validator/validators/not-empty/#default-behaviour)**
>
> Validation classes for a wide range of domains, and the ability to chain validators to create complex validation criteria.

---

<div class="post-metadata">

**Author:** ![froschdesign](https://yyz2.discourse-cdn.com/flex032/user_avatar/discourse.laminas.dev/froschdesign/32/38_2.png) [@froschdesign](https://discourse.laminas.dev/u/froschdesign)\
**Post date:** [July 29, 2023, 6:47am UTC](https://discourse.laminas.dev/t/possible-bug-validating-non-text-input/3418/3 "2023-07-29T06:47:10Z")

</div>

> [@juliangorge](#):
>
> What can it be?

The various elements come with different predefined input filter specifications that include validators and filters.

For the number element this means:

> <https://github.com/laminas/laminas-form/blob/c6100a664879b6f5638b0fae0c844051f2159902/src/Element/Number.php#L87-L103>

And `required` will be handled in an input of laminas-inputfilter:

> <https://github.com/laminas/laminas-inputfilter/blob/988672846aefd8240b6d6c277f472fab54532e75/src/Input.php#L379-L393>

However, the text element defines nothing:

> <https://github.com/laminas/laminas-form/blob/c6100a664879b6f5638b0fae0c844051f2159902/src/Element/Text.php#L9-L19>

So the hint to the `NotEmpty` validator is correct here.

---

<div class="post-metadata">

**Author:** ![juliangorge](https://yyz2.discourse-cdn.com/flex032/user_avatar/discourse.laminas.dev/juliangorge/32/608_2.png) [@juliangorge](https://discourse.laminas.dev/u/juliangorge)\
**Post date:** [July 31, 2023, 9:30pm UTC](https://discourse.laminas.dev/t/possible-bug-validating-non-text-input/3418/4 "2023-07-31T21:30:53Z")

</div>

Thank you @Tyrsson @froschdesign !!  
Very interesting to know that.

If I add allow\_empty and continue\_is\_empty fields in my validation, it continues be required. I think is my ValidationGroup that is verifying this first, is it correct?  
I mean I am trying to understand setValidationGroup is applied before inputFilter

---

<div class="post-metadata">

**Author:** ![Tyrsson](https://yyz2.discourse-cdn.com/flex032/user_avatar/discourse.laminas.dev/tyrsson/32/1664_2.png) [@Tyrsson](https://discourse.laminas.dev/u/Tyrsson)\
**Post date:** [August 1, 2023, 5:54am UTC](https://discourse.laminas.dev/t/possible-bug-validating-non-text-input/3418/5 "2023-08-01T05:54:55Z")

</div>

If you call setValidationGroup and do not pass that field, it will not be returned when you call the data from the form via $form-\>getData() post validation.

---

<div class="post-metadata">

**Author:** ![froschdesign](https://yyz2.discourse-cdn.com/flex032/user_avatar/discourse.laminas.dev/froschdesign/32/38_2.png) [@froschdesign](https://discourse.laminas.dev/u/froschdesign)\
**Post date:** [August 1, 2023, 7:42am UTC](https://discourse.laminas.dev/t/possible-bug-validating-non-text-input/3418/6 "2023-08-01T07:42:54Z")

</div>

> [@juliangorge](#):
>
> I mean I am trying to understand setValidationGroup is applied before inputFilter

The validation group is a part of the input filter:

> It is also possible to specify a “validation group”, a subset of the data to be validated; this may be done using the `setValidationGroup()` method. You can specify the list of the input names as an array or as individual parameters.

> **[Introduction - laminas-inputfilter - Laminas Docs](https://docs.laminas.dev/laminas-inputfilter/intro/#introduction)**
>
> Normalize and validate input sets from the web, APIs, the CLI, and more, including files.

---

<div class="post-metadata">

**Author:** ![juliangorge](https://yyz2.discourse-cdn.com/flex032/user_avatar/discourse.laminas.dev/juliangorge/32/608_2.png) [@juliangorge](https://discourse.laminas.dev/u/juliangorge)\
**Post date:** [August 1, 2023, 11:26am UTC](https://discourse.laminas.dev/t/possible-bug-validating-non-text-input/3418/7 "2023-08-01T11:26:48Z")

</div>

There is something I don’t understand with ValidationGroup. Let me tell you and I’m sorry if I’m not precise.  
For example, if I have these 4 inputs:

1. billing\_type _(non-required select box)_
2. tax\_number _(required if billing\_type was filled)_
3. address _(required if billing\_type was filled)_

Do I need to handle and set ValidationGroup? How can I do it inside of form? Do I need to know the form data?

---

<div class="post-metadata">

**Author:** ![froschdesign](https://yyz2.discourse-cdn.com/flex032/user_avatar/discourse.laminas.dev/froschdesign/32/38_2.png) [@froschdesign](https://discourse.laminas.dev/u/froschdesign)\
**Post date:** [August 1, 2023, 12:46pm UTC](https://discourse.laminas.dev/t/possible-bug-validating-non-text-input/3418/8 "2023-08-01T12:46:05Z")

</div>

> [@juliangorge](#):
>
> Do I need to handle and set ValidationGroup?

This can be handled with a validation group. As an example, overwrite the `setData` method of your form.

Here is an **incomplete** example:

```php
final class ExampleForm extends Laminas\Form\Form
    implements Laminas\InputFilter\InputFilterProviderInterface
{
    public function init(): void
    {
        $this->add(
            [
                'name' => 'billing_type',
                'type' => Laminas\Form\Element\Select::class,
                'options' => [
                    'value_options' => [
                        'Credit Card',
                        'Prepayment',
                    ],
                ],
            ]
        );

        $this->add(
            [
                'name' => 'tax_number',
                'type' => Laminas\Form\Element\Number::class,
            ]
        );

        $this->add(
            [
                'name' => 'address',
                'type' => Laminas\Form\Element\Text::class,
            ]
        );
    }

    public function getInputFilterSpecification(): array
    {
        // "billing_type" and "tax_number" are already required

        return [
            [
                'name' => 'address',
                'required' => true,
            ],
        ];
    }

    public function setData(iterable $data): self
    {
        if ($data instanceof Traversable) {
            $data = ArrayUtils::iteratorToArray($data);
        }
        
        $group = ['billing_type'];
        if (array_key_exists('billing_type', $data)) {
            $group[] = 'tax_number';
            $group[] = 'address';
        }

        $this->setValidationGroup($group);

        return parent::setData($data);
    }
}

```

Test 1:

```php
$form = new ExampleForm();
$form->init();

$form->setData([]);
var_dump($form->isValid()); // false
var_dump($form->getMessages()); // billing_type => isEmpty

```

Test 2:

```php
$form = new ExampleForm();
$form->init();

$form->setData(['billing_type' => '1']);
var_dump($form->isValid()); // false
var_dump($form->getMessages()); // tax_number => isEmpty, address => isEmpty

```

---

<div class="post-metadata">

**Author:** ![juliangorge](https://yyz2.discourse-cdn.com/flex032/user_avatar/discourse.laminas.dev/juliangorge/32/608_2.png) [@juliangorge](https://discourse.laminas.dev/u/juliangorge)\
**Post date:** [August 2, 2023, 3:05pm UTC](https://discourse.laminas.dev/t/possible-bug-validating-non-text-input/3418/9 "2023-08-02T15:05:09Z")

</div>

Thanks, @froschdesign

Could you show me the same example but **tax\_number** input using [OptionalInputFilter](https://docs.laminas.dev/laminas-inputfilter/optional-input-filters/)? (for example:

```auto
$this->add([
   'name' => 'tax_name',
   'attributes' => [
       'required' => true
   ]
]);

$taxNumber = new OptionalInputFilter();
$taxNumber->add([
   'name' => 'tax_number',
   'required' => true,
   'validators' => ['name' => 'IsInt'],
]);

$this->getInputFilter()->add($taxNumber, 'tax_filter');

```

Because I can’t make it work.  
I understand that if tax\_number has value, input filter will run, but I enter “text” on it, my form is still valid. Do I need to add tax\_filter to validationGroup?

Thank you so much 🙏

---

<div class="post-metadata">

**Author:** ![froschdesign](https://yyz2.discourse-cdn.com/flex032/user_avatar/discourse.laminas.dev/froschdesign/32/38_2.png) [@froschdesign](https://discourse.laminas.dev/u/froschdesign)\
**Post date:** [August 2, 2023, 9:31pm UTC](https://discourse.laminas.dev/t/possible-bug-validating-non-text-input/3418/10 "2023-08-02T21:31:44Z")

</div>

You create a nested structure with your OptionalInputFilter:

```php
$taxNumber = new Laminas\InputFilter\OptionalInputFilter();
$taxNumber->add(
    [
        'name' => 'tax_number',
        'required' => true,
        'validators' => [
            ['name' => Laminas\I18n\Validator\IsInt::class],
        ],
    ]
);
$inputFilter = new Laminas\InputFilter\InputFilter();
$inputFilter->add($taxNumber, 'tax_filter');

$inputFilter->setData([]);
var_dump($inputFilter->isValid()); // true

$inputFilter->setData(['tax_filter' => '']);
var_dump($inputFilter->isValid()); // true

$inputFilter->setData(['tax_filter' => []]);
var_dump($inputFilter->isValid()); // true

$inputFilter->setData(['tax_filter' => ['tax_number' => '']]);
var_dump($inputFilter->isValid()); // false
var_dump($inputFilter->getMessages()); // tax_filter => tax_number => isEmpty

$inputFilter->setData(['tax_filter' => ['tax_number' => 'foo']]);
var_dump($inputFilter->isValid()); // false
var_dump($inputFilter->getMessages()); // tax_filter => tax_number => notInt

$inputFilter->setData(['tax_filter' => ['tax_number' => '100']]);
var_dump($inputFilter->isValid()); // true

```

This nested structure does not match your form elements.

---

<div class="post-metadata">

**Author:** ![juliangorge](https://yyz2.discourse-cdn.com/flex032/user_avatar/discourse.laminas.dev/juliangorge/32/608_2.png) [@juliangorge](https://discourse.laminas.dev/u/juliangorge)\
**Post date:** [August 2, 2023, 9:47pm UTC](https://discourse.laminas.dev/t/possible-bug-validating-non-text-input/3418/11 "2023-08-02T21:47:10Z")

</div>

Hi @froschdesign, I really appreciate your time.  
So, to be consistent with OptionalInputFilter usage, do I need to create a nested form input structure to use it? only?

---

<div class="post-metadata">

**Author:** ![juliangorge](https://yyz2.discourse-cdn.com/flex032/user_avatar/discourse.laminas.dev/juliangorge/32/608_2.png) [@juliangorge](https://discourse.laminas.dev/u/juliangorge)\
**Post date:** [August 2, 2023, 10:57pm UTC](https://discourse.laminas.dev/t/possible-bug-validating-non-text-input/3418/12 "2023-08-02T22:57:36Z")

</div>

I deleted ‘tax\_filter’ $name parameter of InputFilterInterface::add(), and it is still “valid”

---

<div class="post-metadata">

**Author:** ![froschdesign](https://yyz2.discourse-cdn.com/flex032/user_avatar/discourse.laminas.dev/froschdesign/32/38_2.png) [@froschdesign](https://discourse.laminas.dev/u/froschdesign)\
**Post date:** [August 3, 2023, 6:13am UTC](https://discourse.laminas.dev/t/possible-bug-validating-non-text-input/3418/13 "2023-08-03T06:13:56Z")

</div>

> [@juliangorge](#):
>
> I deleted ‘tax\_filter’ $name parameter of InputFilterInterface::add(),

This produces an unusable result because the name is an empty string:

```php
var_dump(array_keys($inputFilter->getInputs()));
/*
array(1) {
  [0]=>
  string(0) ""
}
*/

```

---

<div class="post-metadata">

**Author:** ![juliangorge](https://yyz2.discourse-cdn.com/flex032/user_avatar/discourse.laminas.dev/juliangorge/32/608_2.png) [@juliangorge](https://discourse.laminas.dev/u/juliangorge)\
**Post date:** [August 4, 2023, 4:24am UTC](https://discourse.laminas.dev/t/possible-bug-validating-non-text-input/3418/14 "2023-08-04T04:24:36Z")

</div>

Thank you a lot,  
Finally I decided to overwrite setData method to set my validation group instead of use OptionalInputFilter.

---

<div class="post-metadata">

**Author:** ![froschdesign](https://yyz2.discourse-cdn.com/flex032/user_avatar/discourse.laminas.dev/froschdesign/32/38_2.png) [@froschdesign](https://discourse.laminas.dev/u/froschdesign)\
**Post date:** [August 4, 2023, 10:37am UTC](https://discourse.laminas.dev/t/possible-bug-validating-non-text-input/3418/15 "2023-08-04T10:37:41Z")

</div>

Good decision, because the elements are not always optional according to your requirements. Once a payment method is selected, the elements should be filled. This means that these are no longer optional. But with OptionalInputFilter they are always optional.

> [@juliangorge](#):
>
> 1. billing\_type _(non-required select box)_
> 2. tax\_number _(required if billing\_type was filled)_
> 3. address _(required if billing\_type was filled)_
