3

I am experiencing what to me is strange behavior when submitting and validating my Zend Framework 2 form. I have cut my form down to three elements to demonstrate what I mean, but the problem exists with other elements as well.

I have two elements; e-mail and gender. I have not specified an InputFilter for my form, so my logic tells me that my form will be valid. However, when I submit the form and do a validation check, the e-mail and gender elements give the Value is required and can't be empty error. However, I haven't explicitly said they they are required and cannot be empty.

My e-mail element is of the type Zend\Form\Element\Email and I suspect that might have something to do with it, because if I change it to Zend\Form\Element\Text, everything is fine. But what if I want to have an e-mail field that is not required?

Next, my gender select element gives me the same error. I set the empty option, but I thought this was just for using a default value, e.g. "Select your gender", but that it did not add a required constraint. It appears that if I remove this empty option, then I do not get this error. What is also very strange about this error is that if I do not choose a gender and submit my form, then a different option is selected in the dropdown on postback. In this case, I am submitting the form with the empty option ("Choose your gender"), but then the selected option changes to "Female" when the form is refreshed. The "Female" option has a value of 0, but if I change the value to 3, for example, it works fine. But what if I need to have a value of 0 in one of my options? Maybe it's caused by a type conversion or something. I really don't know.

Below is my code. I have written some comments so it may be easier to understand.

Form class excerpt:

// E-mail (generates "Value is required and can't be empty" - but what if the field should be optional? Where is this validator added?
        $email = new Element\Email('email'); // If I change to "Element\Text", there are no problems
        $email->setLabel('E-mail address');

        $email->setLabelAttributes(array(
                                        'for' => $email->getName(),
                                        'class' => 'some class'
                                   ));

        $email->setAttributes(array(
                                   'id' => $email->getName(),
                                   'placeholder' => 'E.g. andy@gmail.com',
                                   'class' => 'some class'
                              ));

// Gender dropdown (generates "Value is required and can't be empty error")
        $gender = new Element\Select('gender');
        $gender->setLabel('Gender');
        $gender->setEmptyOption('Choose your gender'); // Remove this and the error disappears

        $gender->setLabelAttributes(array(
                                    'for' => $gender->getName(),
                                    'class' => 'some class'
                               ));

        $gender->setAttributes(array(
                                    'id' => $gender->getName(),
                                    'class' => 'some class'
                               ));

        $gender->setValueOptions(array(
                                      '1' => 'Male',
                                      '0' => 'Female' // This is selected on postback when the empty option is selected. If I change the value to 3, for instance, it works fine
                                 ));

$this->add($email);
        $this->add($gender);

Controller:

public function registerAction() {
        $registerForm = new RegisterForm();
        $request = $this->getRequest();

        if ($request->isPost()) {
            $registerForm->setData($request->getPost());

            if ($registerForm->isValid()) {
                // Nothing here yet
            }

        }

        return new ViewModel(array('form' => $registerForm));
    }

View script excerpt:

$form = $this->form;
$email = $form->get('email');
$gender = $form->get('gender');

// E-mail
echo $this->formLabel()->openTag($email) . $email->getLabel() . $this->formLabel()->closeTag();
echo $this->formInput($email);
echo $this->formElementErrors($email);

// Gender
echo $this->formLabel()->openTag($gender) . $gender->getLabel() . $this->formLabel()->closeTag();
echo $this->formSelect($gender);
echo $this->formElementErrors($gender);

I apologize if I did not explain the problems well enough, but it is really hard to explain because it seems so strange to me. I looked in the documentation, but I couldn't find anything useful (the ZF2 documentation is still rather poor in my opinion).

akond
  • 15,865
  • 4
  • 35
  • 55
ba0708
  • 10,180
  • 13
  • 67
  • 99

4 Answers4

5

Many people loathed the forms in ZF1.x for the decorators but I loved it nonetheless. What is the worst part for me with ZF2 is that you can basically throw out all your knowledge because like so many things in ZF2 it has completely changed.

There is the new (HTML5) email element but it comes with a hard coded required constraint. Similar stuff happens with select elements. In ZF1 you had the setRequired() method on the element but it is now moved back into what are "inputs" in the form. In other word you have to get the inputs first.

$form->prepare();  // this is another new thing and necessary

$input = $form->getInputFilter();
$e_filter = $input->get('email');
$e_filter->setRequired(false);

$g_filter = $input->get('gender');
$g_filter->setRequired(false);

Now your form should accept empty submissions.

UPDATE: As for the postback problem. It all depends on the data you set with setData(). This could be either the POST data or on a redirect data from a session. If you submit without a selection the POST data you get should not have any "gender" data and so should the data you set into the form.

UPDATE 2 It looks like the root problem is that an empty gender value is submitted and POST values are always strings, hence an empty value is of type string with length 0. Looking into Form\View\Helper\FormSelect and the rendering of the option you will find a in_array($value, $selectedOptions) function handling the selected attribute. Unfortunately they don't set the strict flag and without that a 0 (zero) value matches an empty string.

$post = array('gender' => '');
$bool = in_array(0, $post);        // this is true
$bool = in_array(0, $post, true);  // but this is false

So, either filter/remove this empty value from the POST or change the empty string to NULL before you'll add it to setData() then it will be droped as a selected option. Additionally if you can of course add/change the strict option in the Zend class (which I wouldn't do).

Adrian World
  • 3,118
  • 2
  • 16
  • 25
  • Thank you very much. That is somewhat what I suspected, but I can't believe that they did this. It makes no sense to me. Now I will have to explicitly state that certain fields are not required, but for others this is normal behavior. Having different default behavior for each type of element seems ridiculous. Using `setRequired` on my inputs did the trick. Do you have any idea why my gender select element selects the option with the value of 0 on postback if the empty option is selected on submission? The PHP code is in the question, and [here is the markup](http://pastebin.com/NVkQQkr6). – ba0708 Nov 29 '12 at 13:47
  • @andy124 Dont' get me started about all the decisions. I think they were simply pissed because many compared it to Ruby on Rails and ZF1 wasn't as sophisticated. Anyways about your postback see my update in the answer in a minute or two. – Adrian World Nov 29 '12 at 14:59
  • Yes, that would make sense. I double checked, and I have nothing in my code that alters or even reads the value. I an experiment which to me suggests that the problem is not on my end. [Again, I have to refer you to Pastebin for the code](http://pastebin.com/iVzaUBEM). :-) I have accepted your answer, but I would very much appreciate if you would take a look at the link in this comment. Thank you very much for your help so far. – ba0708 Nov 29 '12 at 15:46
  • 1
    Yes, I suspected that was the problem. So much for dynamic typing. Thank you - you, Sir, are awesome! – ba0708 Nov 29 '12 at 18:53
4

You may also put it at the end of the Form constructor, like this:

public function __construct($name = 'user') {
   ...
   $this->getInputFilter()->get('first_name')->setRequired(false);
}
Conti
  • 1,153
  • 9
  • 12
1

I think there default validators like required.

Have you tried:

->setAllowEmpty(false)

or

->setRequired(false)

From this other so question.

Community
  • 1
  • 1
Iznogood
  • 12,447
  • 3
  • 26
  • 44
  • Thank you for the answer. Using `setRequired` worked, but it has to be done on the `InputFilter` as this is a ZF2 question. I apologize for the incorrect and misleading tag that indicating that the question was also relevant to ZF1. Thanks to akond for removing the incorrect tag. – ba0708 Nov 29 '12 at 13:51
0

For me any of above method didn't work, so I'll post my answer to this problem:

Add a validator like this:

@Form\Validator({"name":"NotEmpty", "options":{"type":"string"}})

It will work for both "0" and "1" value

rolacja
  • 2,381
  • 1
  • 13
  • 12