Zend Framework validation: set element value to empty string when there is no data in POST - forms

I'm sorry if my question is a bit dumb, but I have looked at other questions/Googled this and still didn't get it.
I have a form with default and custom elements:
class Form_Client extends Planner_Form {
public function init() {
$this->addPrefixPath('Planner_Form_Element', 'Planner/Form/Element', 'element');
$this->setAttrib('name', 'clientForm');
$this->addElement('text', 'phone', array(
'label' => 'Телефон',
'required' => true,
'filters' => array('Digits'),
'ng-model' => 'clientForm.phone',
));
$this->addElement('text', 'extra_phone_1', array(
'label' => 'Дополнительный телефон',
'required' => false,
'filters' => array(
array('Digits'),
),
'ng-model' => 'clientForm.extra_phone_1',
));
$this->addElement('text', 'name', array(
'label' => 'Имя',
'required' => false,
'ng-model' => 'clientForm.name',
));
$this->addElement('datetime', 'birthday', array(
'label' => 'Дата рождения',
'required' => false,
'ng-model' => 'clientForm.birthday',
));
I send form via AngularJs, and when I check it
if ($request->isPost()) {
$body = $this->getRequest()->getRawBody();
$data = Zend_Json::decode($body);
Zend_Debug::dump($data);
$form = new Form_Client();
if ($form->isValid($data)) {
$values = $form->getValues();
Zend_Debug::dump($values);
}
I get the following:
array(1) {
["phone"] => string(10) "9138521376"
}
array(4) {
["phone"] => string(10) "9138521376"
["extra_phone_1"] => string(0) ""
["name"] => NULL
["birthday"] => NULL
}
So my question is: why extra_phone_1 field gets empty string, and name gets NULL? Is it because of filter on extra_phone_1 field? If so, how can I set field value to empty string, when there is no data in POST for this field?

You are right, the value is'' due Digits filter and null without this filter.
To have a non-null value in the name field, for example, you can use the same technique and try to put the filter 'StringTrim'

Related

Validation error "This value should not be blank" when submitting a form on production website

I'm developing a website using php 7.4, symfony 5.4 and twig. This website is deployed on several servers.
On one of the servers (RedHat), a form cannot be submitted. I get the following error 4 times : "This value should not be blank.".
The messages appear on top of the form and aren't attached to a particular field.
I can't reproduce this error on another server, nor on my development environment...
The problem might comes from a validator but I'm not sure whether it's a symfony or a doctrine error.
The POST data is identical on production server and dev environment :
report_selection[iFrame]: 1
report_selection[dteFrom]: 2023-01-30 07:00
report_selection[dteTo]: 2023-01-31 07:00
report_selection[reportType]: 1
report_selection[size]: 200
report_selection[product]: 1
report_selection[submit]:
I assume that the empty field submit is not a problem since other forms work fine while having the same field empty.
The database structure is the same on all servers.
Here is the form's code :
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$bDisplaySize = $options['bDisplaySize'];
$bDisplayReportType = $options['bDisplayReportType'];
$bDisplayProduct = $options['bDisplayProduct'];
$defaultValue = $options['defaultValue'];
$em = $options['entity_manager'];
list($H, $m) = explode(":", $iShiftStart);
$initialFromDate = (new DateTime())->modify('-'.$H.' hour');
$initialFromDate = $initialFromDate->modify('-1 day');
$initialFromDate->setTime((int)$iShiftStart, (int)$m, 0);
$initialToDate = clone $initialFromDate;
$initialToDate = $initialToDate->modify('+1 day');
$builder->add(
'iFrame',
ChoiceType::class,
array(
'label' => 'master.preselection',
'choices' => [
'master.yesterday' => false,
'master.today' => false,
'master.thisWeek' => false,
'master.lastWeek' => false,
'master.thisMonth' => false,
'master.lastMonth' => false,
'master.memomryDate' => false,
],
'attr' => ['onchange' => 'refreshPreselectedChoices()'],
'choice_attr' => [
'master.yesterday' => [],
'master.today' => ['selected' => 'selected'],
'master.thisWeek' => [],
'master.lastWeek' => [],
'master.thisMonth' => [],
'master.lastMonth' => [],
'master.memomryDate' => ['disabled' => true],
],
)
);
$builder->add(
'dteFrom',
TextType::class,
array(
'label' => 'form.from',
'data' => $initialFromDate->format('Y-m-d H:i'),
'attr' => array(
'style' => 'width:150px;',
'oninput' => 'dteFromToCustom()',
'onchange' => 'dteFromToCustom()',
),
)
);
$builder->add(
'dteTo',
TextType::class,
array(
'label' => 'form.to',
'data' => $initialToDate->format('Y-m-d H:i'),
'attr' => array(
'label' => 'form.to',
'style' => 'width:150px;',
'oninput' => 'dteFromToCustom()',
'onchange' => 'dteFromToCustom()',
),
)
);
if ($bDisplayReportType) {
$builder->add(
'reportType',
ChoiceType::class,
array(
'label' => 'summaryReport.data',
'choices' => array(
'summaryReport.type1' => '1',
'summaryReport.type2' => '2',
),
)
);
}
if ($bDisplaySize) {
$builder->add(
'size',
EntityType::class,
array(
'class' => ProductsSizeSpecs::class,
'choice_label' => 'rSize',
'choice_value' => 'rSize',
'placeholder' => '',
'label' => 'form.size',
'required' => false,
'mapped' => false,
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('e')
->groupBy('e.rSize')
->orderBy('e.rSize', 'ASC');
},
)
);
}
if ($bDisplayProduct) {
$builder->add(
'product',
EntityType::class,
array(
'class' => Products::class,
'choice_label' => 'sNumber',
'choice_value' => 'sNumber',
'placeholder' => '',
'label' => 'master.product',
'required' => false,
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('e')
->groupBy('e.sNumber')
->orderBy('e.sNumber', 'ASC');
},
)
);
}
$builder->add(
'submit',
SubmitType::class,
array(
'label' => 'form.submit',
'attr' => array('class' => 'btn btn-primary'),
)
);
}
Other forms use the exact same code with more or less options.
I search a way to debug this on the production server (list/dump of 'blank' fields?).
Any hint will be appreciated, thanks !
Indeed I had some #Assert\NotBlank on several columns of an Entity.
Why the error was only on this server :
An instance (db record) of this Entity was NULL on those columns (which is an anormal behavior).
All the instances where retrieved to populate the form's dropdowns (as 'default' data).
It looks like the validator is checking the submitted 'data' AND those 'default' values since they are part of the form.
There were 4 asserted columns, so that's why I had 4 errors messages.
What I've done to find this out :
Added a dump($this->form->getErrors()) instruction on the callback processing the submitted form. It displayed the 4 entity's columns giving me hard time.
Went into db to see the corrupted record, and deleted it.
To prevent this in the future I might change the default values of these columns from NULL to something else, a basic string or a 0 value, and search the process that led to this corrupted record in db.
Thanks for your hints guys !

Radio button: input was not found in the haystack?

Whenever I submit the form I get this message:
The input was not found in the haystack.
This is for the shipping-method element (radio button). Can't figure out what it means, the POST data for that element is not null.
public function getInputFilter()
{
if (!$this->inputFilter) {
$inputFilter = new InputFilter();
// Some other basic filters
$inputFilter->add(array(
'name' => 'shipping-method',
'required' => true,
'filters' => array(
array('name' => 'StripTags'),
array('name' => 'StringTrim')
),
'validators' => array(
array(
'name' => 'StringLength',
'options' => array(
'encoding' => 'UTF-8',
'max' => 20,
),
),
array(
'name' => 'Db\RecordExists',
'options' => array(
'table' => 'shipping',
'field' => 'shipping_method',
'adapter' => $this->dbAdapter
)
),
),
));
$inputFilter->get('shipping-address-2')->setRequired(false);
$inputFilter->get('shipping-address-3')->setRequired(false);
$this->inputFilter = $inputFilter;
}
return $this->inputFilter;
}
I only keep finding solutions for <select>.
Here's the sample POST data:
object(Zend\Stdlib\Parameters)#143 (1) {
["storage":"ArrayObject":private] => array(9) {
["shipping-name"] => string(4) "TEST"
["shipping-address-1"] => string(4) "test"
["shipping-address-2"] => string(0) ""
["shipping-address-3"] => string(0) ""
["shipping-city"] => string(4) "TEST"
["shipping-state"] => string(4) "TEST"
["shipping-country"] => string(4) "TEST"
["shipping-method"] => string(6) "Ground"
["submit-cart-shipping"] => string(0) ""
}
}
UPDATE:
form.phtml
<div class="form-group">
<?= $this->formRow($form->get('shipping-method')); ?>
<?= $this->formRadio($form->get('shipping-method')
->setValueOptions(array(
'Ground' => 'Ground',
'Expedited' => 'Expedited'))
->setDisableInArrayValidator(true)); ?>
</div>
ShippingForm.php
$this->add(array(
'name' => 'shipping-method',
'type' => 'Zend\Form\Element\Radio',
'options' => array(
'label' => 'Shipping Method',
'label_attributes' => array(
'class' => 'lbl-shipping-method'
),
)
));
The problem lies with when you use the setValueOptions() and the setDisableInArrayValidator(). You should do this earlier within your code as it is never set before validating your form and so the inputfilter still contain the defaults as the InArray validator. As after validation, which checks the inputfilter, you set different options for the shipping_methods.
You should move the setValueOptions() and the setDisableInArrayValidator() before the $form->isValid(). Either by setting the right options within the form itsself or doing this in the controller. Best way is to keep all of the options in one place and doing it inside the form class.
$this->add([
'name' => 'shipping-method',
'type' => 'Zend\Form\Element\Radio',
'options' => [
'value_options' => [
'Ground' => 'Ground',
'Expedited' => 'Expedited'
],
'disable_inarray_validator' => true,
'label' => 'Shipping Method',
'label_attributes' => [
'class' => 'lbl-shipping-method',
],
],
]);
Another small detail you might want to change is setting the value options. They are now hardcoded but your inputfilter is checking against database records whether they exist or not. Populate the value options with the database records. If the code still contains old methods but the database has a few new ones, they are not in sync.
class ShippingForm extends Form
{
private $dbAdapter;
public function __construct(AdapterInterface $dbAdapter, $name = 'shipping-form', $options = [])
{
parent::__construct($name, $options)
// inject the databaseAdapter into your form
$this->dbAdapter = $dbAdapter;
}
public function init()
{
// adding form elements to the form
// we use the init method to add form elements as from this point
// we also have access to custom form elements which the constructor doesn't
$this->add([
'name' => 'shipping-method',
'type' => 'Zend\Form\Element\Radio',
'options' => [
'value_options' => $this->getDbValueOptions(),
'disable_inarray_validator' => true,
'label' => 'Shipping Method',
'label_attributes' => [
'class' => 'lbl-shipping-method',
],
],
]);
}
private function getDbValueOptions()
{
$statement = $this->dbAdapter->query('SELECT shipping_method FROM shipping');
$rows = $statement->execute();
$valueOptions = [];
foreach ($rows as $row) {
$valueOptions[$row['shipping_method']] = $row['shipping_method'];
}
return $valueOptions;
}
}
Just had this happen yesterday.
The select and multi select ZF2+ elements have a built in in_array validator.
Remember filters occur before validators.
You may be doing too much here -- it is very rare to need to filter or add validators ot select and multi select form elements in ZF2 forms. The built in element validator is robust, ZF does a lot of work for us.
Try removing both filter and validator for the element, such as:
$inputFilter->add(array(
'name' => 'shipping-method',
'required' => true,
));
There is another edge case that I have seen: changing the select element's valueOptions somewhere in the controller (or view) resulting in different valueOptions used in view vs form validation (in our case it was replacing the element with a new one before validation).
I think your problem lies in the fact you are adding your value options after the InArray validator has been set, hence the validator has no haystack.
Try this
$this->add(array(
'name' => 'shipping-method',
'type' => 'Zend\Form\Element\Radio',
'options' => array(
'label' => 'Shipping Method',
'label_attributes' => array(
'class' => 'lbl-shipping-method'
),
'value_options' => array(
'Ground' => 'Ground',
'Expedited' => 'Expedited'
),
'disable_inarray_validator' => TRUE,
)
));
and remove setValueOptions and setDisableInArrayValidator from your view.
Hope this works.

zend_form isValid always false

I write code for a site that uses Zend 1.
I want to rewrite the old admin interface in zend, but my form fails to validate.
I'll post the form, the validation code and the debug output here.
Form:
class Form_Admin_Address_Neu extends Zend_Form {
public function init() {
$this->setMethod('post');
$this->addElement('text', '_street', array(
'label' => 'Strasse:',
'size' => 60,
'required' => true,
'filters' => array('StringTrim'),
));
$this->addElement('text', '_zip', array(
'label' => 'PLZ:',
'size' => 6,
'required' => true,
'filters' => array('StringTrim'),
));
$this->addElement('text', '_city', array(
'label' => 'Stadt:',
'required' => true,
'size' => 30,
'filters' => array('StringTrim'),
));
$this->addElement('text', '_lat', array(
'label' => 'Latitude:',
'required' => true,
'size' => 30,
'validators' => array('Float'),
'filters' => array('Stringtrim'),
));
$this->addElement('text', '_lng', array(
'label' => 'Longitude:',
'required' => true,
'size' => 30,
'validators' => array('Float'),
'filters' => array('Stringtrim'),
));
$this->addElement('checkbox', '_hidden', array(
'label' => 'Hidden:',
'size' => 1,
'filters' => array('Int', 'Null'),
));
$this->addElement('submit', 'submit', array(
'ignore' => true,
'label' => 'Senden',
));
$this->addElement('hash', 'csrf', array(
'ignore' => true,
));
$this->setAction('/admin/address/list');
}
}
additional fields (i just post 1 here, 2nd is like it):
class Form_Admin_Elements_CountrySelect extends Zend_Form_Element_Select {
public function init() {
$countrymapper = new Mapper_Country();
$this->addMultiOption(0, 'Please select...');
foreach ($countrymapper->fetchAll() as $country) {
$this->addMultiOption($country->getId(), $country->getName());
}
$this->setLabel("Land:");
}
}
Code:
$addForm = new Form_Admin_Address_Neu();
$regionselect = new Form_Admin_Elements_RegionSelect('region_id');
$regionselect->setRequired(true);
$addForm->addElement($regionselect);
$countryselect = new Form_Admin_Elements_CountrySelect('country_id');
$countryselect->setRequired(true);
$addForm->addElement($countryselect);
if ($addForm->isValid($_POST)) {
...
} else {
print_r($_POST);
print_r($addForm->getErrorMessages());
print_r($addForm->getCustomMessages());
print_r($addForm->getErrors());
}
output:
Array
(
[_street] => sdvdsvsv
[_zip] => 111111
[_city] => sdfgsf
[_lat] => 1.0
[_lng] => 2.1
[_hidden] => 0
[country_id] => 1
[region_id] => 3
[submit] => Senden
[csrf] => d18dfed9d26e28d7a52aa4983b00667e
)
Array
(
)
Array
(
)
Array
(
[_street] => Array
(
)
[_zip] => Array
(
)
[_city] => Array
(
)
[_lat] => Array
(
[0] => notFloat
)
[_lng] => Array
(
[0] => notFloat
)
[_hidden] => Array
(
)
[submit] => Array
(
)
[csrf] => Array
(
)
[region_id] => Array
(
)
[country_id] => Array
(
)
)
As I see it, the validation fails, but i dont know why.
The values are present in the $_POST, but the form doesnt validate.
i even tried with isValidPartial(), but same result.
I think i'm doing something fundamentally wrong.
A hint would be great.
ty in advance
Try to enter a comma instead of a point in Latitude and Longitude.
1,0 and 2,1 instead of 1.0 and 2.1
I think it's a problem about Locale of your validator

how to set value from database in Zend Framework 1 in add form element

I have a form in Zend Framework 1. when I click on edit button I want to display values from databases in the form. but I don't know how to do it.
This is my form code:
// Add an email element
$this->addElement('text', 'orgname', array(
'required' => true,
'filters' => array('StringTrim'),
'style' => array('width:220px'),
'decorators'=>Array(
'ViewHelper','Errors'
)
));
This is my controller:
public function editclientcompanyAction()
$form = new Application_Form_Companyform();
$form->addform();
$this->view->form = $form;
$request = $this->getRequest();
$editid=$request->getParam('id');
$edit_show = new Application_Model_Clientcompany;
$showdetails = $edit_show->editclient($editid);
$this->view->assign('datas', $showdetails);
How do I display database vlaues in my Zend Form?
There are two cases.
1) Populating form which has fields same like the database table fields : If you have the form which has same fields like the database fields, then you can populate them easily.
First you need to get the data from the database and then call the Zend_Form populate function passing the data as an associative array, where keys will be same like form fields names and values will be values for the form fields, as below in case of your form.
This will be in your controller
$data = array("orgname" => "Value for the field");
$form = new Application_Form_Companyform();
$form->populate($data);
Now send will automatically populate the form field orgname. You dont need to modify your form or set the value field in the addElement.
*2)Setting field value manually: * The second case is to set the value manually. First you will need to modify your form and add a constructor to it. Also in your form class you will need to create a property (if you have multiple fields, then you can create an array property or multiple properties for each field. This will be all up to you.). And then set the value key in the addElement. Your form should look like this
class Application_Form_Companyform extends Zend_Form
{
private $orgname;
public function __contruct($orgname)
{
$this->orgname = $orgname;
//It is required to call the parent contructor, else the form will not work
parent::__contruct();
}
public function init()
{
$this->addElement('text', 'orgname',
array(
'required' => true,
'filters' => array('StringTrim'),
'style' => array('width:220px'),
'decorators'=>Array('ViewHelper','Errors'),
'value'=>$this->orgname
)
));
} //end of init
} //end of form
Now your controller, you will need to instantiate the form object passing the value of the orgname field like below
$form = new Application_Form_Companyform("This is the value for orgname");
And thats it.
I used such methods and it works like a charm. For your requirements, you may need to adjust the above sample code, as i did not checked it, but it will run fine for sure i hope :P
Thank you
Ok in either ZF1 or ZF2 just do this.
// Add an email element
$this->addElement('text', 'orgname',
array(
'required' => true,
'filters' => array('StringTrim'),
'style' => array('width:220px'),
'decorators' => Array('ViewHelper','Errors'),
'value' => $showdetails->orgname
)
));
You might want to test first for null/empty values first though, you could use ternary operators for convenience:
// Add an email element
$this->addElement('text', 'orgname',
array(
'required' => true,
'filters' => array('StringTrim'),
'style' => array('width:220px'),
'decorators' => Array('ViewHelper','Errors'),
'value' => empty($showdetails->orgname)? null : $showdetails->orgname
)
));
Please have a look in my edit function at /var/www/html/zend1app/application/controllers/CountryController.php :
public function editAction() {
$data = $this->getRequest()->getParams();
$id = (int)$data['id'];
$options = array();
$country = $this->getCountryModel()->fetchRow("id=$id");
if(!$country)
{
throw new Exception("Invalid Request Id!");
}
$form = new Application_Form_Country();
$form->addIdElement();
if ($this->getRequest()->isPost()) {
if ($form->isValid($this->getRequest()->getPost())){
$data = new Application_Model_Country();
if($data->save($form->getValues()))
{
$message = array("sucess" => "Country has been updated!");
}
else {
$message = array("danger" => "Country could not be updated!");
}
$this->_helper->FlashMessenger->addMessage($message);
return $this->_helper->redirector('index');
}
}
$options = array (
'id' => $country->id,
'name' => $country->name,
'code' => $country->code
);
$form->populate( $options ); // data binding in the edit form
$this->view->form = $form;
}
and form class at /var/www/html/zend1app/application/forms/Country.php :
class Application_Form_Country extends Zend_Form
{
public function init()
{
// Set the method for the display form to POST
$this->setMethod('post');
// Add an email element
$this->addElement('text', 'name', array(
'label' => 'Enter Country Name:',
'required' => true,
'filters' => array('StringTrim'),
'validators' => array(
array('validator' => 'StringLength', 'options' => array(0, 20))
)
));
// Add the comment element
$this->addElement('text', 'code', array(
'label' => 'Enter Country Code:',
'required' => true,
'validators' => array(
array('validator' => 'StringLength', 'options' => array(0, 20))
)
));
// Add the submit button
$this->addElement('submit', 'submit', array(
'ignore' => true,
'label' => 'Save',
));
// And finally add some CSRF protection
$this->addElement('hash', 'csrf', array(
'ignore' => true,
));
}
public function addIdElement()
{
$this->addElement('hidden', 'id');
}
}
HTH

How to add ErrorDescription to Zend Sub Form Element?

I have the following code that makes a sub form from a table, I have the error description saved in the database, I know how to set it for normal elements but not when it is a subform, how can I add a custom error message per subform element?
$subForm = new Zend_Form_SubForm();
foreach($configuration as $config){
$elements[] = array(
new Zend_Form_Element_Text($config->configuration_key, array(
'required' => (($config->is_required == 1) ? true : false),
'label' => $config->configuration_title,
'filters' => array('StringTrim'),
'value' => $config->configuration_value,
'Options' => array('style'=>'width:90%;'),
'Description' => $config->configuration_description,
'errorMessage' => $config->errorMessage,
))
);
}
$subForm->addElements($elements);
$this->addSubForm($subForm, 'configuration');
After playing a lot with the different options and trial/error I found out that I needed to add 'ErrorMessages' as an array, I rewrote my snippet
$elementSettings = array(
'required' => (($config->is_required == 1) ? true : false),
'label' => $config->configuration_title,
'filters' => array('StringTrim'),
'value' => $config->configuration_value,
'Options' => array('style'=>'width:90%;'),
'Description' => $config->configuration_description,
'ErrorMessages' => array($config->errorMessage)
);
$elements[] = array(new Zend_Form_Element_Text($config->configuration_key, $elementSettings));