Zend_Form not displaying error message with calling addError - zend-framework

I am implementing an updatePasswordAction and its not displaying an error with an invalid current password. I could not implement this with a Zend_Validate class to use with a supplied record->password, so i just validated for now in my controller action and if failed then i add the error message to the form element. this is just before i run $form->isValid. In any case, its working. but when the validation fails, its not displaying the error message on this on the element. any help would be greatly appreciated.
FYI: When I submit a blank current password, it shows the validation
class Admin_Form_UserPassword extends Katana_Form
{
public function init()
{
$element = $this->createElement('hidden', 'id');
$this->addElement($element);
$element = $this->createElement('password','password');
$element->setLabel('Current Password:');
$element->setRequired(true);
$this->addElement($element);
$element = $this->createElement('password','new_password');
$element->setLabel('New Password:');
$element->addValidator('StringLength', false, array(6,24));
$element->setRequired(true);
$element->addValidator('NotEmpty');
$this->addElement($element);
$element = $this->createElement('password','new_password_confirm');
$element->setLabel('Confirm:');
$element->addValidator('StringLength', false, array(6,24));
$element->addValidator('IdenticalField', false, array('new_password', 'Confirm Password'));
$element->setRequired(true);
$this->addElement($element);
$this->addElement('submit', 'submit', array('label' => 'Submit'));
}
}
public function updatePasswordAction()
{
$resourceModel = new Core_Model_Resource_User();
$form = new Admin_Form_UserPassword();
$form->setMethod(Katana_Form::METHOD_POST);
$form->setAction($this->getActionUrl('update-password'));
if($this->getRequest()->isPost()){
$id = $this->getRequest()->getParam('id');
$record = $resourceModel->find($id)->current();
$currPassword = $record->password;
$typedPassword = md5($this->getRequest()->getParam('password'));
if($currPassword !== $typedPassword){
$form->getElement('password')->addError('Current password is incorrect.');
}
if($form->isValid($_POST)){
$data = $form->getValues();
$result = $resourceModel->updatePassword($id, $data['new_password']);
if($result){
$this->redirectSimple('list');
}
}
} else {
$id = $this->getRequest()->getParam('id');
$recordData = array(
'id' => $id
);
$form->populate($recordData);
}
$this->getView()->form = $form;
}

Adding an error to the element doesn't cause the form itself to then be invalid.
There are at least 2 methods I use to get around this:
if($currPassword !== $typedPassword){
$form->getElement('password')->addError('Current password is incorrect.');
$form->markAsError();
}
// or
if ($form->isValid($_POST) && 0 == sizeof($form->getMessages()) {
// form was valid, and no errors were set on elements
}
To clarify, when you add the error to the form ELEMENT, there is an error attached to that element, but Zend_Form::isValid only runs the validators and sets appropriate errors, it doesn't check to see if you had set an error on a particular element.
You can however call $form->getMessages() to get all the error messages attached to the form or its child elements. If this is 0 and you have validated your form, then it means there were no errors. If your form passed isValid but you added an error to an element, it will include the error message you added.

I made it work this way.
Controller:
if ($trial->getKind() != 'debt' && $_POST['kind'] == 'debt')
{
$editForm->getElement('kind')->markAsError();
}
if ($editForm->isValid($_POST)) { ... }
Form:
public function isValid($data)
{
$valid = parent::isValid($data);
if ($this->getElement('kind')->hasErrors()) {
$this->getElement('kind')->addError($this->_translate->translate('You can\'t change trial kind to debt.'));
$valid = false;
}
return $valid;
}
And this comment helped me.

Related

symfony 2 "This form should not contain extra fields." validation error and issues in binding REST request data

I am trying to use Symfony forms to handle REST requests.
I was facing issue with binding request data in symfony form. While going through Symfony's code, I found it uses
HttpFoundationRequestHandler->handleRequest
In that, for POST,
if ('' === $name) {
$params = $request->request->all();
$files = $request->files->all();
} elseif ($request->request->has($name) || $request->files->has($name)) {
$default = $form->getConfig()->getCompound() ? array() : null;
$params = $request->request->get($name, $default);
$files = $request->files->get($name, $default);
} else {
// Don't submit the form if it is not present in the request
return;
}
If you see above code, if there is form name in Our form type class, it tries to get values from request as
$request->request->get($name) // $name is form name.
This may work if we render form as HTML but if we post request through REST, it doesnt find data.
To make it work we either have to namespace it like below in request (I haven't tested personally this but have read it somewhere)
{
'form_name': {
'field1' => 'value1'
}
}
Only below requst doesnt't bind data
{
'field1' => 'value1'
}
or we will have to return form name as null from getName from form type so that it use root namespace.
public function getName()
{
return null;
}
If we return null, then it creates other problem, if there are any extra parameters in request other than form fields it gives validation errors.
"This form should not contain extra fields."
Ideally form should just fetch the feild values from request which are configured in form and should ignore any other request parameters which are unknowne to it.
e.g. if this is the form
class SampleFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('field1', 'text', array('constraints' => new NotBlank()))
->add('field2', 'password', array('constraints' => new NotBlank()));
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'csrf_protection' => false,
));
}
public function getName()
{
return null;
}
}
but in request if I am sending
{
'field1' => 'value1',
'field2' => 'value2',
'field3' => 'value3'
}
It gives above validation errors. It should ideally simply ignore field3 and fetch feild1 & field2 from request.
If we compare code in NativeRequestHandler & HttpFoundationRequestHandler,
NativeRequestHandler seem to have handled it properly
if ('' === $name) {
$params = $_POST;
$files = $fixedFiles;
} elseif (array_key_exists($name, $_POST) || array_key_exists($name, $fixedFiles)) {
$default = $form->getConfig()->getCompound() ? array() : null;
$params = array_key_exists($name, $_POST) ? $_POST[$name] : $default;
$files = array_key_exists($name, $fixedFiles) ? $fixedFiles[$name] : $default;
} else {
// Don't submit the form if it is not present in the request
return;
}
where as HttpFoundationRequestHandler doesn't
if ('' === $name) {
$params = $request->request->all();
$files = $request->files->all();
} elseif ($request->request->has($name) || $request->files->has($name)) {
$default = $form->getConfig()->getCompound() ? array() : null;
$params = $request->request->get($name, $default);
$files = $request->files->get($name, $default);
} else {
// Don't submit the form if it is not present in the request
return;
}
So either we need to be able to use NativeRequestHandler or we need fix in HttpFoundationRequestHandler ?
I see below works though only in case of content-type text request (doesnt work with application/json),
$form->submit(array('field1' => $request->request->get('field1'), 'field2' => $request->request->get('field2')))
But using $form->handleRequest($request) looks more simpler and abstract.
If there is any other better and simpler solution please do suggest.

How can I replace this _forward() with something that can exit?

I use the following code over and over in my zend framework application. It is used in action() to check if an article exists. If not, the user shall see an error message:
$article = ArticleQuery::create()->findOneByUrl($this->_getParam('url', ''));
if (!$article) {
$this->getResponse()
->setRawHeader('HTTP/1.1 404 Not Found');
return $this->_forward('error', null, null, array(
'message' => 'Article not found',
));
}
I was wondering how to factor this out into an own method to reduce the code load in all actions.
I came to something like this:
protected function myAction() {
$article = $this->getArticleIfExists($this->_getParam('url', ''));
if ($article == null) {
return;
}
}
protected function getArticleIfExists($url) {
$article = ArticleQuery::create()->findOneByUrl($this->_getParam('url', ''));
if ($article) {
return $article;
} else {
$this->getResponse()
->setRawHeader('HTTP/1.1 404 Not Found');
$this->_forward('error', null, null, array(
'message' => 'Article not found',
));
return nulL;
}
}
I still would like to get rid of the if case in myAction(), but _forward() does not allow to exit the execution (of course, because it still needs to execute the other actions).
Another possibility (I have implemented in some other controllers) is this:
protected function myAction() {
$article = ArticleQuery::create()->findOneByUrl($this->_getParam('url', ''));
if (!$article) {
return $this->notFound('Article does not exist');
}
}
protected function notFound($message) {
$this->getResponse()
->setRawHeader('HTTP/1.1 404 Not Found');
return $this->_forward('error', null, null, array(
'message' => $message,
));
}
Again we have this if check in the action. It’s already better than before, but can we make it even better?
How can I circumvent this? Is there a possibility to do it without losing the current URL? With a Redirector I can of course exit, but then I would lose the current URL (/controller/myaction/url/hello -> /error/error/message/Article%20not%20found)
A possible approach would be to throw an Exception. Because of the Zend_Controller_Plugin_ErrorHandler this will automatically redirect you to the ErrorController without any further code being executed.
If you don't want to get to the ErrorController but only to the current controller's error actions, you can simply modify the plugin in the controller's init method:
public function init()
{
$plugin = Zend_Controller_Front::getInstance()->getPlugin('Zend_Controller_Plugin_ErrorHandler');
$plugin->setErrorHandlerModule('default')
->setErrorHandlerController('MyController')
->setErrorHandlerAction('error');
}
But of course you can also write your own ErrorHandler plugin for a more fine grained error handling. This is described in the Zend Framework Manual on Plugins
For something as simple as just displaying a "* does not exist" against a user request I prefer to leave the user in the application and just hit them with a flashmessenger notice and leave them on the page to make another request (if appropriate):
public function indexAction() {
//get form and pass to view
$form = new Form();
$this->view->form = $form;
try {
//get form values from request object
if ($this->getRequest()->isPost()) {
if ($form->isValid($this->getRequest()->getPost())) {
$data = $form->getValues();
// Do some stuff
}
}
} catch (Zend_Exception $e) {
$this->_helper->flashMessenger->addMessage($e->getMessage());//add message to flashmessenger
$this->_redirect($this->getRequest()->getRequestUri());//perform redirect to originating page so the messenger will flash
}
}
This is simple and works well when the possibility for incorrect user input exists.

PHPunit Dispatch controller action with POST and form

I have a PHPunit test like this:
public function testUsersCanRegisterWhenUsingValidData()
{
$this->request->setMethod('POST')
->setPost(array(
'username' => 'user123',
'zip_code' => '43215',
'email' => 'me1#something.com',
'password' => 'secret',
'confirm_pswd' => 'secret',
));
$this->dispatch('/account/register');
$this->assertRedirectTo('/account/login');
}
and a User controller action called register like this :
public function registerAction()
{
// Instantiate the registration form model
$form = new Application_Model_FormRegister();
// Has the form been submitted?
if ($this->getRequest()->isPost()) {
// If the form data is valid, process it
if ($form->isValid($this->_request->getPost())) {
// Does an account associated with this username already exist?
$account = $this->em->getRepository('Entities\Account')
->findOneByUsernameOrEmail($form->getValue('username'), $form->getValue('email'));
if (! $account)
{ // do something
.............
..............
} else {
$this->view->errors = array(
array("The desired username {$form->getValue('username')} has already been taken, or
the provided e-mail address is already associated with a registered user.")
);
}
} else {
$this->view->errors = $form->getErrors();
}
}
$this->view->form = $form;
}
I get an error in this line :
$account = $this->em->getRepository('Entities\Account')
->findOneByUsernameOrEmail($form->getValue('username'), $form->getValue('email'));
It's caused by $form->getValue('username') being NULL because the form has not actually been submitted, instead PHPunit has dispatched the action and set up the POST variables.
How can I get this working?
Sorry everyone. I had commented out this line to try and study my problem:
// If the form data is valid, process it
if ($form->isValid($this->_request->getPost())) {
and it turns out that my input test input was not valid and you can't use $form->getValue to get the value of an invalid form.
I didn't get any answers because this line was not commented out in my post and would have worked. Slap head............MODS feel free to delete this post if you think it is no help to anybody.

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; ?>

Validate a set of fields / group of fields in Zend Form

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.