Let's start this off with a short code snippet I will use to demonstrate my opinion:
$title = new Zend_Form_Element_Text('title', array(
'label' => 'Title',
'required' => false,
'filters' => array(
'StringTrim',
'HtmlEntities'
),
'validators' => array(
array('StringLength', false, array(3, 100))
),
));
This important line is:
'required' => false,
Which means that the input field is not required and you can submit the form without filling it. However, this also means that any filters and validators won't apply to it if you choose to fill in this field.
Common sense tells me that is an irrational behavior. The way I understand the word 'required' in relation with HTML input fields: an input field that is not required should return NULL if it is not filled in but if user decides to fill it both filters and validators should apply to it. That's what makes sense to me. Do you agree with me or is my common sense not so common?
Now more practical question, because this is how Zend_Form behaves, how can I achieve not required fields which would work as I described above (if nothing is typed in by user it returns NULL otherwise filters and validators normally apply).
Not really a complete answer to your question, but since comments don't have syntax formatting; here's a filter you can use to make your field values null if empty.
class My_Filter_NullIfEmpty implements Zend_Filter_Interface
{
public function filter( $value )
{
// maybe you need to expand the conditions here
if( 0 == strlen( $value ) )
{
return null;
}
return $value;
}
}
About the required part:
I'm not sure really. You could try to search the ZF mailinglists on Nabble:
http://www.nabble.com/Zend-Framework-Community-f16154.html
Or subscribe to their mailinglist, and ask them the question. Either through Nabble, or directly via the addresses on framework.zend.com:
http://tinyurl.com/y4f9lz
Edit:
Ok, so now I've done some tests myself, cause what you said all sounded counter intuitive to me. Your example works fine with me. This is what I've used:
<?php
class Form extends Zend_Form
{
public function init()
{
$title = new Zend_Form_Element_Text('title', array(
'label' => 'Title',
'required' => false,
'filters' => array(
'StringTrim',
'HtmlEntities',
'NullIfEmpty' // be sure this one is available
),
'validators' => array(
array('StringLength', false, array(3, 100))
),
));
$this->addElement( $title );
}
}
$form = new Form();
$postValues = array( 'title' => '' ); // or
$postValues = array( 'title' => ' ' ); // or
$postValues = array( 'title' => 'ab' ); // or
$postValues = array( 'title' => ' ab ' ); // or
$postValues = array( 'title' => '<abc>' ); // all work perfectly fine with me
// validate the form (which automatically sets the values in the form object)
if( $form->isValid( $postValues ) )
{
// retrieve the relevant value
var_dump( $form->getValue( 'title' ) );
}
else
{
echo 'form invalid';
}
?>
Actually, what you describe as your expectations are exactly how Zend_Form works. If you mark the element as not required, then the following happens: (a) if no value is passed, it skips validation, but if (b) a value is passed, then it must pass all validators in order to be valid.
BTW, best place to ask ZF questions is on the ZF mailing lists: http://framework.zend.com/archives
Related
I'm having trouble formulating a solution for 'editing' a field of my form in my controller.
Here's what I have:
I have a symfony2 form registered as a service that I call in a function in my controller. I am removing a bunch of fields that aren't necessary for this other form I am directing my users to and then adding a few others.
(I realize I could create another form and create another service and such but for my purpose this would be a bit overkill. I'm doing it this way because the form functions the same, however some fields are not needed and a few new specific ones are.)
I would now like to essentially 'edit' one field in this form... The 'occupation' field. This field is a choice field acting as radio buttons populated by an array of choices. It's required and has no empty_value requirement in its original state.
I would like to edit it in my controller function to have the same exact values however with a required value of false and an empty_value of null.
With the commented out code below the result is a dissapearance of the occupation field in my 'new' form and it is replaced by an empty drop down. I realize it's because I'm overriding the whole field below, but I cannot figure out how to simply edit it.
Code:
/**
* Explanation of addAndRemoveFieldsInRegisterForm function:
* The function gets the 'registration' form and removes any
* fields not needed for the 'in_registration' form
* and then adds the necessary fields to the form.
*/
private function addAndRemoveFieldsInRegisterForm($user)
{
$form = $this->createForm('user_registration', $user);
// http://stackoverflow.com/questions/10920006/pass-custom-options-to-a-symfony2-form ---
// --- use that to help. Look at changing the value of array.
$form->remove('title');
$form->remove('company');
$form->remove('username');
$form->remove('city');
$form->remove('state');
$form->remove('country');
$form->remove('gender');
$form->remove('age');
$form->remove('roles');
// $form->remove('occupation');
// $pr = $form->get('occupation');
// $pr->set('required' => false);
// $form->get('occupation')->add('required'=>false, 'empty_value'=>null);
// $form->add('occupation','choice', array(
// 'required' => false,
// 'empty_value' => null,
// ));
// echo "<pre>";
// var_dump(get_class_methods($form));die;
$form->add('occupation','choice', array(
'required' => false,
'empty_value' => null,
));
$form->add('canEmail', 'checkbox', array(
'label' => 'Can Email?',
'required' => false,
));
$form->add('sendEmail', 'choice', array(
'label' => 'Send Welcome Email? ',
'required' => true,
'mapped' => false,
'expanded' => true,
'choices' => array(
"yes" => "Yes",
"no" => "No"
),
));
return $form;
}
Original Form (the one that's used as a service)
private $requireOccupation;
$this->requireOccupation = true;
->add('occupation','choice', $options['occupation'])
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$occupation = array(
"label" => "Which of these currently describes you best? (Occupation):",
"expanded" => true,
'required'=> $this->requireOccupation,
"choices" => array(
"X" => "X",
"B" => "B",
"C" => "C",
"J" => "J",
),
'constraints' => array(
new NotBlank()
));
$resolver->setDefaults(array(
'occupation' => $occupation,
));
}
I think it is better to create another form. It can herit from your already defined form to change only the field you want
class SomeFormType extends OriginalFormType {
public function buildForm(FormBuilderInterface $builder, array $options) {
parent::buildForm($builder, $options);
$builder
->remove('someField')
->add('someField', 'choice', [
"expanded" => true,
"choices" => $yourArray
]);
}
It has the advantage to be mapped on different object
Firstly, I realize the way I wanted to solve this issue is odd when considering I could have created another form with either the fields I wanted to use or just the one field that needed to change and register the form as a service to use it elsewhere, but I was tasked to complete it this way.
Second, my solution is quite simple. I pass values into my form with a default value set in the form.
In the form:
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$requireOccupation = true;
$emptyValue = null;
//whatever other values you want to set here
$resolver->setDefaults(array(
'requireOccupation' => $requireOccupation,
'emptyValue' => $emptyValue,
));
}
and then on the field's properties:
$builder->add('occupation', 'choice', array(
"label" => "Some sort of label",
"required" => $options['requireOccupation'],
"empty_value" => $options['emptyValue'],
...
));
Now in the controller:
$form = $this->createForm('registration', $user, array(
'requireOccupation' => false, 'emptyValue' => null
));
call that where you want to generate your form while passing in the values you want to use for that form.
I am by no means an expert on Symfony and this solution would probably generate some issue with those who are. But it works for me.
A Zend_Form like this:
class Application_Form_Registration extends Zend_Form
{
public function init()
{
/* Form Elements & Other Definitions Here ... */
$$this->setMethod('post');
//first name
$this->addElement('text', 'email', array(
'label' => 'First name',
'required' => true,
'filters' => array('StringTrim'),
));
//last name
$this->addElement('text', 'lastname', array(
'label' => 'Last name',
'required' => true,
'filters' => array('StringTrim')
));
$this->addElement('submit', 'submit', array(
'ignore' => true,
'label' => 'Submit'
));
$this->addElement('hash', 'csrf', array(
'ignore' => true,
));
}
}
I read through the ZF1 1.12 API and reference document, but I can't find the meaning of the flag "ignore" in the Zend_Form::addElement() configure options.
The api doc is just this:
Surely I googled it and find it but this is not the way to work. How to I find the meaning of certain specific stuff. I don't suppose that I need to read the source code?
Just take this addElement() as an example, am I missing somewhere to look further? Nothing in Zend_Config class that I can find about ignore flag either.
As I know ignore flag defines if form values ($form->getValues()) will contain element value. If ignore is set to true for some element than form values ($form->getValues()) will not contain this element value.
ZF Documentation can be...lacking sometimes. The API docs for the ignore flag state:
getIgnore( ) : bool Get ignore flag (used when retrieving values at form level)
Which hints that the ignore flag has something to do with the behavior of Zend_Form GetValues() but it's not really spelled out.
In these cases I like to go straight to the source code so I can see for myself:
public function getValues($supressArrayNotation = false)
{
...
foreach ($this->getElements() as $key => $element) {
if (!$element->getIgnore()) {
...
}
You can see that the getValues() function in Zend_Form will check the ignore flag on each element before adding the value to the return array. If the flag is true, the value won't be included.
I want to disable the validation on one of the zend form element based on the input on another element on the same form. I need to achieve this using javascript/jquery. Something very common but very surprisingly couldn't find it over the internet.
e.g.
In the controller:
$oFormMassEdit = new Account_Form_Returns_Return();
$this->view->form = $oFormMassEdit;
In Account_Form_Returns_Return's constructor:
$oNoteElement = new Zend_Form_Element_Textarea('option', array(
'required' => true,
));
$this->addElemet($oNoteElement)
$oReasonElement = new Zend_Form_Element_Select('note', array(
'multiOptions' => array (
'return' => 'return',
'defect' => 'Kaput',
'other' => 'Anderer Grund'
),
'required' => true,
));
$this->addElement($oReasonElement);
$this->addDisplayGroup(array('note', 'option'), 'main', array('legend' => 'Retouren'));
$this->addElement('button','send', array(
'type' => 'submit',
'label' => 'Methode speichern',
));
and finally in the view,
<?= $this->form; ?>
Javascript can't (not in a sensible way) switch Zend_Form configuration. What you can do is changing the 'required' param for certain form fields on validation. For example; If you want to allow fieldTwo to be empty if fieldOne has 'desiredValue' as value you can achieve this using the following function in your form:
public function isValid($data) {
if('desiredValue' == $data['fieldOne'])) {
$this->getElement('fieldTwo')->setRequired(false);
}
return parent::isValid($data);
}
So I created a input filter (see below) but I have to test 24 fields to make sure they are all valid (only 1 listed below to keep this simple). In this case, the input is coming from an e-mail server, not a user, so I need accept the input, and not send an error back. However, I still need to check the data to ensure no one is messing with the headers / fields trying to mess everything up.
So my question is, how can I sent a default value for each input? For example blow, if the mailbox is length 0, something is wrong, so I just want to set the value to be something like 'InvalidMailbox' so I can still store this in the database,
public function getInputFilter()
{
if (!$this->inputFilter) {
$inputFilter = new InputFilter();
$factory = new InputFactory();
$inputFilter->add($factory->createInput(array(
'name' => 'mailbox',
'required' => true,
'filters' => array(
array('name' => 'StripTags'),
array('name' => 'StripNewLines'),
array('name' => 'StringToLower'),
array('name' => 'StringTrim'),
),
'validators' => array(
array(
'name' => 'StringLength',
'options' => array(
'encoding' => 'UTF-8',
'min' => 1,
'max' => 200,
),
),
),
)));
$this->inputFilter = $inputFilter;
}
return $this->inputFilter;
}
Calling it...
$mail = new SMail();
$inputFilter = $mail->getInputFilter;
$inputFilter->setData($data);
if ($inputFilter->isValid()) {
//echo "The form is valid\n";
} else {
// Maybe set the default here?
// but with 24 different fields, how can I know which one caused the error?
//echo "The form is not valid\n";
}
Okay, sorry that i didn't get the quest right first time. Going by the Source of the BaseInputFilter, there is a function called getInvalidInput(), so my assumption is, that you can do the following:
$defaultData = array(
'elementName' => 'Default Value'
);
$returnData = array();
if (false === $inputFilter->isValid()) {
$falseInputs = $inputFilter->getInvalidInput();
foreach ($falseInputs as $input) {
$returnData[$input->getName()] = $defaultData[$input->getName()];
}
}
$goodInputs = $inputFilter->getValidInput();
$finalData = array_merge($goodInputs, returnData);
This however is no tested code. I'm not sure if $input->getName() is available. You may need to adjust that part accordingly. I'm quite certain though that this should be able to get you started, hopefully ;)
I am slowly building up my Zend skills by building some utility websites for my own use. I have been using Zend Forms and Form validation and so far have been happy that I have been understanding the Zend way of doing things. However I am a bit confused with how to use Zend_Validate_Db_NoRecordExists() in the context of an edit form and a field that maps to database column that has to be unique.
For example using this simple table
TABLE Test
(
ID INT AUTO_INCREMENT,
Data INT UNIQUE
);
If I was simply adding a new row to the Table Test, I could add a validator to the Zend Form element for the Data field as such:
$data = new Zend_Form_Element_Text('Data');
$data->addValidator( new Zend_Validate_Db_NoRecordExists('Test', 'Data') )
At form validation this validator will check that the contents of the Data element does not already exist in the table. Thus the insert into Test can go ahead without violating the Data fields UNIQUE qualifier.
However the situation is different when editing an existing row of the Test table. In that case the validator needs to check that the element value meets one of two mutually exclusive conditions conditions:
The user has changed the element value, and the new value does not currently
exist in the table.
The user has Not changed the element value. Thus the value does currently exist in the table (and this is OK).
The Zend Validation Docs talk about adding a parameter to the NoRecordExists() validator for the purpose of excluding records from the validation process. The idea being to "validate the table looking for any matching rows, but ignore any hits where the a field has this specific value". Such a use case is what is needed for the validating the element when editing a table. The pseudo code to do this in 1.9 is like so (actually I got this from the 1.9 source code - I think the current docs may be wrong):
$data = new Zend_Form_Element_Text('Data');
$data->addValidator( new Zend_Validate_Db_NoRecordExists('Test', 'Data',
array ('field'=>'Data', 'Value'=> $Value) );
The problem is that the value that is to be excluded ($Value) is bound to the validator at the time it is instantiated (also when the form is instantiated). But when the form is editing a record, that value needs to be bound to the contents of the $data field when the form was initially populated with data - IE the Data value initially read from the Test table row. But in typical Zend patterns a form is instantiated and populated in two separate steps which precludes binding the exclude value to the desired element value.
The following Zend psuedo code marks where I would like the binding of $Value to the NoRecordExists() validator to occur (and note that this is a common Zend controller pattern):
$form = new Form()
if (is Post) {
$formData = GetPostData()
if ($form->isValid($formData)) {
Update Table with $formData
Redirect out of here
} else {
$form->populate($formData)
}
} else {
$RowData = Get Data from Table
$form->populate($RowData) <=== This is where I want ('value' => $Value) bound
}
I could sub-class Zend_Form and override the populate() method to do a one-shot insertion of the NoRecordExists() validator on initial form population, but that seems like a huge hack to me. So I wanted to know what other people think and is there some pattern already written down that solves this problem?
Edit 2009-02-04
I've been thinking that the only decent solution to this problem is to write a custom validator and forget about the Zend version. My form has the record ID as hidden field, so that given the table and column names I could craft some SQL to test for uniqueness and exclude the row with an ID of such an such. Of course this started me thinking about how I would be tying the form to the dB layer that the Model is supposed to hide!
This is how it's done:
I your FORM, you add this validator (for example email field):
$email->addValidator('Db_NoRecordExists', true, array('table' => 'user', 'field' => 'email'));
Don't add custom error message for this since after that it didn't work for me, e.g.:
$email->getValidator('Db_NoRecordExists')->setMessage('This email is already registered.');
In your Controller add this:
/* Don't check for Db_NoRecordExists if editing the same field */
$form->getElement('email')
->addValidator('Db_NoRecordExists',
false,
array('table' => 'user',
'field' => 'email',
'exclude' => array ('field' => 'id', 'value' => $this->request->get('id'))));
And after this you do verifications, e.g.:
if ($this->getRequest()->isPost())
{
if($form->isValid($this->getRequest()->getPost()))
{
....
That's it!
This will also work :
$this->addElement('text', 'email', array(
'label' => 'Your email address:',
'required' => true,
'filters' => array('StringTrim'),
'validators' => array(
'EmailAddress',
array('Db_NoRecordExists', true, array(
'table' => 'guestbook',
'field' => 'email',
'messages' => array(
'recordFound' => 'Email already taken'
)
)
)
)
));
After reviewing the overwhelming response I've decided that I'm going with a custom validator
Look at this one:
Answer raised by me and well-solved by Dickie
private $_id;
public function setId($id=null)
{
$this->_id=$id;
}
public function init()
{
.....
if(isset($this->_id)){
$email->addValidator('Db_NoRecordExists', false, array('table' => 'user', 'field' => 'email','exclude' => array ('field' => 'id', 'value' => $this->_id) ));
$email->getValidator('Db_NoRecordExists')->setMessage('This email is already registered.');
}
Now u can use:
$form = new Form_Test(array('id'=>$id));
You could just call $form->getElement('input')->removeValidator('Zend_Validator_Db_NoRecordExists'); instead of supplying the exclusion.
I have just tried this example for email address uniqueness and it works perfectly with below stuffs :
1] In my form:
// Add an email element
$this->addElement('text', 'email', array(
'label' => 'Email :',
'required' => true,
'filters' => array('StringTrim'),
'validators' => array(
'EmailAddress',
)
));
Here's something special that I needed to add for unique email address to work:
$email = new Zend_Form_Element_Text('email');
$email->addValidator('Db_NoRecordExists', true, array('table' => 'guestbook', 'field' => 'email'));
2] In my controller:
$form->getElement('email')
->addValidator('Db_NoRecordExists',
false,
array('table' => 'guestbook',
'field' => 'email',
'exclude' => array ('field' => 'id', 'value' => $request->get('id'))));
if ($this->getRequest()->isPost()) {
if ($form->isValid($request->getPost())) {
Hope it helps you people !
Thanks