After taking a short break from Zend Framework 2 I started working on a new project in it. I created a Form class that looks like:
<?php
namespace MyProject\Form;
use ...
class RegistrationForm extends Form
{
public function __construct($name = null)
{
parent::__construct('registration');
$this->add(
array(
'name' => 'name',
'type' => 'Text',
'options' => array(
'label' => _t('Full name'),
),
)
);
$this->add(
array(
'name' => 'email',
'type' => 'Email',
'options' => array(
'label' => _t('Email'),
)
)
);
}
public function getInputFilter()
{
$inputFilter = new InputFilter();
$fullName = new Input('name');
$v = new ValidatorChain();
$v->attach(new StringLength(array('min' => 3)));
$fullName->setValidatorChain($v);
$fullName->setRequired(true);
$inputFilter->add($fullName);
$email = new Input('email');
$email->setRequired(true);
$v = new ValidatorChain();
$v->attach(new EmailAddress());
$email->setValidatorChain($v);
$inputFilter->add($email);
return $inputFilter;
}
}
The problem arose when I validated the form and tried to access the data in the controller:
$form->setData($postData);
if ($form->isValid()) {
$data = $form->getData();
}
However, for some reason, the output of $data was missing values, something of this sort:
[
'name' => null,
'email' => null,
]
So, the getData() keys were present, but values were missing!
It took me a while to figure out what’s going on. In order to validate the form, Zend Framework 2 calls getInputFilter on it. However, it does not store the resulting InputFilter, but rather calls this method again whenever it needs it – essentially overwriting any data put into it by $form->setData($postData);
The solution was to store form’s InputFilter as a static variable. The code now looks like this and behaves as expected:
<?php
namespace MyProject\Form;
use ...
class RegistrationForm extends Form
{
/**
* @var \Zend\InputFilter\InputFilter
*/
static private $inputFilter;
public function __construct($name = null)
{
... same as before ...
}
public function getInputFilter()
{
// Keep reusing the same instance of InputFilter
if (isset(self::$inputFilter)) return self::$inputFilter;
$inputFilter = new InputFilter();
$fullName = new Input('name');
$v = new ValidatorChain();
$v->attach(new StringLength(array('min' => 3)));
$fullName->setValidatorChain($v);
$fullName->setRequired(true);
$inputFilter->add($fullName);
$email = new Input('email');
$email->setRequired(true);
$v = new ValidatorChain();
$v->attach(new EmailAddress());
$email->setValidatorChain($v);
$inputFilter->add($email);
// Make sure we store $inputFilter as a static variable
return self::$inputFilter = $inputFilter;
}
}
Output of getData() looks like:
[
'name' => 'User submitted name',
'email' => 'their@email.com',
]
Looks like a bug at the first glance, but I suppose this behavior allows for bigger flexibility when validating forms. I still have to find the good use for it, but oh well…
This is actually not a very good solution (though it works), since you should avoid using static variables like this.
A better solution would be something like this:
inputFilter) {
$inputFilter = new InputFilter();
$fullName = new Input('name');
$v = new ValidatorChain();
$v->attach(new StringLength(array('min' => 3)));
$fullName->setValidatorChain($v);
$fullName->setRequired(true);
$inputFilter->add($fullName);
$email = new Input('email');
$email->setRequired(true);
$v = new ValidatorChain();
$v->attach(new EmailAddress());
$email->setValidatorChain($v);
$inputFilter->add($email);
// Make sure we store $inputFilter as a static variable
$this->inputFilter = $inputFilter;
}
return $this->inputFilter
}
}
This way the $inputFilter can be accessed in child classes (since it’s protected) and you’re avoiding any static access.
I am trying to find if there is a solution to fix this issue with Annotations…
Any idea ?