Validate a set of fields / group of fields in Zend Form - zend-framework

Is there any good solution for the following requirements:
A form with one field for zip code and default validators like number, length, etc.
After submission, the form is checked against a database.
If the zip code is not unique we have to ask for an city.
Examples:
Case 1: Submited zip code is unique in database. Everything is okay. Process form
Case 2: Submited zip code is not unique. Add a second field for city to the form. Go back to form.
We want to handle this in an generic way (not inside an controller). We need this logic for
a lot of forms. First thought was to add it to isValid() to every form or write a
validator with logic to add fields to the form. Subforms are not possible for us, because we need this for different fields (e.g. name and street).

Currently I'm using isValid method inside my forms for an User Form to verify the password and confirm password field. Also, when the form is displayed in a New Action, there are no modifications, but when displayed in an Edit Action, a new field is added to the form.
I think that is a good option work on the isValid method and add the field when the validation return false, and if you want something more maintainable, you should write your own validatator for that purpose.
Take a look at my code:
class Admin_Form_User extends Zf_Form
{
public function __construct($options = NULL)
{
parent::__construct($options);
$this->setName('user');
$id = new Zend_Form_Element_Hidden('id');
$user = new Zend_Form_Element_Text('user');
$user->setLabel('User:')
->addFilter('stripTags')
->addFilter('StringTrim')
->setAllowEmpty(false)
->setRequired(true);
$passwordChange = new Zend_Form_Element_Radio('changePassword');
$passwordChange->setLabel('Would you like to change the password?')
->addMultiOptions(array(1 => 'Sim', 2 => 'Não'))
->setValue(2)
->setSeparator('');
$password = new Zend_Form_Element_Password('password');
$password->setLabel('Password:')
->addFilter('stripTags')
->addFilter('StringTrim')
->setRequired(true);
$confirm_password = new Zend_Form_Element_Password('confirm_password');
$confirm_password->setLabel('Confirm the password:')
->addFilter('stripTags')
->addFilter('StringTrim')
->addValidator('Identical')
->setRequired(true);
$submit = new Zend_Form_Element_Submit('submit');
$submit->setLabel('Save');
$this->addElements(array($id,$name,$lastname,$group,$user,$passwordChange,$password,$confirm_password,$submit));
$this->addDisplayGroup(array('password','confirm_password'),'passwordGroup');
$this->submit->setOrder(8);
$this->setDisplayGroupDecorators(array(
'FormElements',
array('HtmlTag', array('tag' => 'div','id' => 'div-password'))
)
);
$passwordChange->clearDecorators();
}
public function addPasswordOption()
{
$this->changePassword->loadDefaultDecorators();
$this->getDisplayGroup('passwordGroup')
->addDecorators(array(
array('HtmlTag', array('tag' => 'div','id' => 'div-password'))
)
);
$this->password->setRequired(false);
$this->confirm_password->setRequired(false);
}
public function setPasswordRequired($flag = true)
{
$this->password->setRequired($flag);
$this->confirm_password->setRequired($flag);
}
public function isValid($data)
{
$confirm = $this->getElement('confirm_password');
$confirm->getValidator('Identical')->setToken($data['password']);
return parent::isValid($data);
}
}
So, in my controller:
public function newAction()
{
$this->view->title = "New user";
$this->view->headTitle($this->view->title, 'PREPEND');
$form = $this->getForm();
if($this->getRequest()->isPost())
{
$formData = $this->_request->getPost();
if($form->isValid($formData))
{
$Model = $this->getModel();
$id = $Model->insert($formData);
$this->_helper->flashMessenger('The user data has beed updated.');
$this->_helper->redirector('list');
}
}
$this->view->form = $form;
}
public function editAction()
{
$this->view->title = "Edit user";
$this->view->headTitle($this->view->title, 'PREPEND');
$id = $this->getRequest()->getParam('id');
$form = $this->getForm();
// Add yes or no password change option
$form->addPasswordOption();
$Model = $this->getModel();
if($this->getRequest()->isPost())
{
$formData = $this->getRequest()->getPost();
// Change password?
if($formData['changePassword'] == 2) $form->setPasswordRequired(false);
if($form->isValid($formData))
{
$Model->update($formData);
$this->_helper->flashMessenger('The user data has beed updated.');
$this->_helper->redirector('list');
}
}
$data = $Model->getById($id)->toArray();
$form->populate($data);
$this->view->form = $form;
}

You will probably need a Javascript form validator for that. In the submit function perform an AJAX call to check if the zipcode is unique. If not, show an extra city field.
But you still have to perform the validation server side: never trust user input, even if it's validated on the client side.

Related

Compare two fields to see if they match

I'm trying to think of the best way to approach validating two emails fields for a form: I have an email field and a retype email field, and I want to compare them to see if they match. If not then display an error message saying so until the user enters in their email correctly for both fields.
I know there can be custom fields classes made to allow for custom validation but I'm using 2 separate fields, not one, and I'm not sure a custom class will work in this case since I'm not validating just one field.
I followed the example found here which involves adding custom validation to the form's submit function: https://docs.silverstripe.org/en/3/developer_guides/forms/validation/
However, I don't want to redirect anywhere -- I want to stay on the form.
I'd like to make this as simple as possible if I can help it.
Here is what I have so far. Other than the issue I mentioned, everything else works fine:
public function DemoForm() {
$fieldsArr = array();
$firstName = new TextField('first_name', 'First Name');
$firstName->setAttribute('placeholder', 'First Name');
array_push($fieldsArr, $firstName);
$lastName = new TextField('last_name', 'Last Name');
$lastName->setAttribute('placeholder', 'Last Name');
array_push($fieldsArr,$lastName);
$companytName = new TextField('company_name', 'Company Name');
$companytName->setAttribute('placeholder', 'Company Name');
array_push($fieldsArr,$companytName);
$email = new EmailField('email', 'Email');
$email->setAttribute('placeholder', 'Email');
array_push($fieldsArr, $email);
$retypeemail = new EmailField('retype_email', 'Retype Email');
$retypeemail->setAttribute('placeholder', 'Retype Email');
array_push($fieldsArr, $retypeemail);
$fields = new FieldList($fieldsArr);
$actions = new FieldList(
new FormAction('submit', 'Submit')
);
$validator = new RequiredFields('first_name', 'last_name', 'email', "retype_email");
return new Form($this, 'DemoForm', $fields, $actions, $validator);
}
public function submit($data, $form) {
if($data['email'] != $data['retype_email']){
$form->addErrorMessage('Email', 'The emails do not match.', 'bad');
return $this->redirectBack();
}
else{
$demoRequest = new DemoFormSubmission();
$demoRequest->FirstName = $data['first_name'];
$demoRequest->LastName = $data['last_name'];
$demoRequest->EmailAddress =$data['email'];
$demoRequest->CompanyName = $data['company_name'];
$demoRequest->write();
$email = new Email();
$email->setTo('sara.dejaneiro#innismaggiore.com');
$email->setFrom($data['email']);
$email->setSubject("Demo request submission");
$messageBody = "
<p><strong>First Name:</strong> {$data['first_name']}</p>
<p><strong>Last Name:</strong> {$data['last_name']}</p>
<p><strong>Email: </strong> {$data['email']}</p>
<p><strong>Company Name:</strong> {$data['company_name']}</p>
";
$email->setBody($messageBody);
$email->send();
return $this->redirect("demo-request-submission-thank-you-page");
}
}
Given your reply to my comment, I think you would probably be best to either:
Create your own form field that extends CompositeField and then add a custom validate function (as per https://docs.silverstripe.org/en/3/developer_guides/forms/validation/#form-validation)
Creating a custom validator, by extending RequiredFields (https://github.com/silverstripe/silverstripe-framework/blob/3.6.2/forms/RequiredFields.php) and then overloading the PHP method (https://github.com/silverstripe/silverstripe-framework/blob/3.6.2/forms/RequiredFields.php#L82).
Number 2 might be the less fiddly approach. You can write your own custom logic and then if the validation fails simply return "false" and then SilverStripe will cancel form submission and return the user to the form with their data still in the form fields.

Zend Form Registration with

I am new in Zend.
I have tried to create registration form in Zend.
I have get an array of form but it return false.
It returns me every time bye .
I don't know why???
It's Simple:
First, create your registration form under application/forms or use zend tool
zf enable form
zf create form registration
this will create a file under application/forms entitled Registration.php
class Application_Form_Registration extends Zend_Form
{
public function init()
{
$firstname = $this->createElement('text','firstname');
$firstname->setLabel('First Name:')
->setRequired(false);
$lastname = $this->createElement('text','lastname');
$lastname->setLabel('Last Name:')
->setRequired(false);
$email = $this->createElement('text','email');
$email->setLabel('Email: *')
->setRequired(false);
$username = $this->createElement('text','username');
$username->setLabel('Username: *')
->setRequired(true);
$password = $this->createElement('password','password');
$password->setLabel('Password: *')
->setRequired(true);
$confirmPassword = $this->createElement('password','confirmPassword');
$confirmPassword->setLabel('Confirm Password: *')
->setRequired(true);
$register = $this->createElement('submit','register');
$register->setLabel('Sign up')
->setIgnore(true);
$this->addElements(array(
$firstname,
$lastname,
$email,
$username,
$password,
$confirmPassword,
$register
));
}
}
This is a simple form with limited validation (only validation for fields that are required!)
Then you have to render the form in a view using the corresponding action:
as example in your UserController add action called
public function registerAction() {
//send the form to the view (register)
$userForm = new Application_Form_Registration();
$this->view->form = $userForm;
//check if the user entered data and submitted it
if ($this->getRequest()->isPost()) {
//check the form validation if not valid error message will appear under every required field
if ($userForm->isValid($this->getRequest()->getParams())) {
//send data to the model to store it
$userModel = new Application_Model_User();
$userId = $userModel->addUser($userForm->getValues());
}
}
}
The last two, is to render the form in the view called register
using
<?= $this->form ?>
and add method in your model called addUser() to handle the insertion of data into the database

Symfony 2 Choice Field Default Value

I have this field in my form:
->add('taskOwner', null, array(
'label' => $this-> translator ->trans( 'tasks.index.responsible' , array() , 'crm' )))
Symfony recognize it as Choice Type (it's have foreign key to another table, with users). Now i want to set the default value on the logged user. How I can do that ? I tried in my controller create new entity of my type, set taskOwner into it and then by SetData put in into form, like this:
$entity = new Tasks();
$tasksForm = $this->createForm(new TasksType($translator), $entity);
$userId = $this->get('security.context')->getToken()->getUser()->getId();
$user = $this->getDoctrine()->getRepository('CloudAdmBundle:AdmUser')->find($userId);
$task = new Tasks();
$task->setTaskOwner($user);
$tasksForm->setData($task);
To clear everything, definition of setter:
public function setTaskOwner(\Cloud\AdmBundle\Entity\AdmUser $taskOwner = null)
{
$this->taskOwner = $taskOwner;
return $this;
}
Do it before you create the form:
$userId = $this->get('security.context')->getToken()->getUser()->getId();
$user = $this->getDoctrine()->getRepository('CloudAdmBundle:AdmUser')->find($userId);
$entity = new Tasks();
$entity->setTaskOwner($user);
$tasksForm = $this->createForm(new TasksType($translator), $entity);

File not found after calling receive on form file element and file transfer adapter

As the title says, I tried calling the receive() function either of Form Element and the Adapter Object(not one after another of course). I printed the returned value - was 1 in both cases - which means receive() returned true.
The file was not found on the server though. I tried setting encrypt type of zend form to multipart/form-data - didn't help.
I'm totally clueless so any info is welcomed.
Calling receive() on transfer adapter: file location and upload name are constants.
$this->uploadName = $uploadName;
$this->upload = new Zend_File_Transfer_Adapter_Http();
$this->upload->setDestination($this->fileLocation);
...
$val = $this->upload->receive();
$quoteName = $this->upload->getFileName($this->uploadName);
$size = $this->upload->getFileSize($this->uploadName);
calling receive on form element:
//form creation - my form extends zend form
$staticForm = Srm_Form::getForm(my form,null,null,
my config);
$staticForm->setEnctype('multipart/form-data');
$staticForm->getElement(my file element name)->setDestination(my dest);
//calling receive
$form = Srm_Form::getForm(my form,null,null,my config);
$form->setEnctype('multipart/form-data');
if(!$form->isValid($_POST)){
print_r($form->getMessages());
}
// echo $form->getElement(my file element)->getValue();
$val = $form->getElement(my file element)->receive();
echo "bbbbbb".$val;
I should add that this code works when it is called after the file element is added to the form manually and not through use of a config file.
Okay, the problem was found -
The destination was not set for the file element (it was set manually for the transfer adapter in other place)when handling the submitted form.
I define a simple form with the Zend_Form_Element_File element
<?php
class Form_UploadForm extends Zend_Form
{
public function __construct($options = null)
{
parent::__construct($options);
$this->setMethod('post');
$this->setAttrib('enctype', 'multipart/form-data');
$decors = array(
array('ViewHelper'),
array('HtmlTag'),//array('tag'=>'table')),
array('Label', array('separator' => ' ')), // those unpredictable newlines
array('Errors', array('separator' => ' ')), // in the render output
);
$file = new Zend_Form_Element_File('file');
$file->setDestination('/a/b/c/upload');
$file->setLabel('Document File Path')
->setRequired(true)
->addValidator('NotEmpty');
$this->addElement($file);
$submit = new Zend_Form_Element_Submit('submit');
$submit->setLabel('Upload File');
$this->addElement($submit);
}
}
?>
My action method in the Controller is
function uploadAction()
{
$this->view->pageTitle = "Zend_Form File Upload Example";
$this->view->bodyCopy = "<p>Please fill out this form.</p>";
$form = new Form_UploadForm();
if ($this->_request->isPost())
{
$formData = $this->_request->getPost();
if ($form->isValid($formData))
{
try
{
$form->file->receive();
}
catch (Zend_File_Transfer_Exception $e)
{
throw new Exception('unable to recieve : '.$e->getMessage());
}
$uploadedData = $form->getValues();
//Zend_Debug::dump($form->file->getFileName(), 'tmp_file');
$this->processFile($form->file->getFileName());
}
else
{
$form->populate($formData);
}
}
$this->view->form = $form;
}
Note - i don't call the Zend_File_Transfer_Adapter_Http directly
The final piece is the view
<?php echo $this->form; ?>

Extending Zend_View_Helper_FormElement

I have created this file at My/View/Helper/FormElement.php
<?php
abstract class My_View_Helper_FormElement extends Zend_View_Helper_FormElement
{
protected function _getInfo($name, $value = null, $attribs = null,
$options = null, $listsep = null
) {
$info = parent::_getInfo($name, $value, $attribs, $options, $listsep);
$info['id'] = 'My new ID';
return $info;
}
}
How can i get the normal form elements to use this instead?
Why i want this?
Say that i use the same form multiple times on a page, the 'id='-tag of the formelements will apear multiple times, this is not w3c-valid. So initially i want to prefix the id with the id of the form.
Any better ideas or ways to do this is much apreciated.
Update: Just realized it's the same problem with the decorators :( Don't think this is the right path i've taken.
Create new form class extending Zend_Form and in the init() method use variable $ns to add prefix/suffix to all elements. You can set $ns variable through form constructor.
class Form_Test extends Zend_Form
{
protected $ns;
public function init()
{
$this->setAttrib('id', $this->ns . 'testForm');
$name = new Zend_Form_Element_Text('name');
$name->setAttrib('id', $this->ns . 'name');
$name->setLabel('Name: *')->setRequired(true);
$submit = new Zend_Form_Element_Submit('submit');
$submit->setAttrib('id', $this->ns . 'submitbutton');
$submit->setLabel('Add')->setIgnore(true);
$this->addElements(array($name, $submit));
}
public function setNs($data)
{
$this->ns = $data;
}
}
In the controller or wherever you are calling this forms specify each form instance:
$form1 = new Form_Test(array('ns' => 'prefix1'));
$this->view->form1 = $form1;
$form2 = new Form_Test(array('ns' => 'prefix2'));
$this->view->form2 = $form2;
// Validation if calling from the controller
if ($form1->isValid()) ...
Using multiple instances of same forms on a page can be validated if used as subform.
SubForms prefix all id's with the name/identifier of the subform.