not passing validation - Symfony2 Form without a Class - php

After a thorough search im not seeing the problem.
I have a form im submitting, which isnt tied to an object, its just an emailer form. I want to validate the data. according to the docs the alternative way is to do this.
use Symfony\Component\Validator\Constraints\Length;
use Symfony\Component\Validator\Constraints\NotBlank;
$builder
->add('firstName', 'text', array(
'constraints' => new Length(array('min' => 3)),
))
->add('lastName', 'text', array(
'constraints' => array(
new NotBlank(),
new Length(array('min' => 3)),
),
))
;
now thats done, but i CANNOT call $form->isValid on this in any shape or form (pun intended), because even though it passes the constraint violations, something seemingly invisible is still causing it to return invalid.
i feel like i might need to extract the form from the post first, and pass that through isValid() but i cant be sure.
heres my method code
/**
* #Route("/share", name="email_share")
* #Method({"POST"})
* #return \Symfony\Component\HttpFoundation\Response
*/
public function shareAction( Request $request, array $lessons){
if(!$lessons || !is_array($lessons))
{
throw new HttpException(404, "Whoops! shareAction didnt get the lessons");
}
//set up our form defaults here
$defaultData = array('comment' => 'Type your comment here');
//build the form here
$form = $this->createFormBuilder($defaultData)
->setAction($this->generateUrl('email_share'))
->add("emails", 'email', array(
'label' => "Recipient's email address (separate with a comma)",
'constraints' => array(
new Length(array('min' => 6, 'max' => 2040)),
new NotBlank(),
),
))
->add('comment', 'textarea', array(
'label' => "Leave a comment",
))
->add('name', 'text', array(
'label' => "Your name",
'constraints' => array(
new Length(array('min' => 3), 'max' => 254)),
new NotBlank(),
),
))
->add('email', 'email', array(
'label' => "Your email address",
'constraints' => array(
new Length(array('min' => 6, 'max' => 254)),
new NotBlank(),
),
))
->add('copy', 'checkbox', array(
'label' => "Send me a copy",
'required' => false,
))
->add('cancel', 'submit', array(
'label' => "Cancel",
))
->add('save', 'submit', array(
'label' => "Email Resources",
))
->getForm();
if ($this->getRequest()->isMethod('POST')) {
//data is already validated by constraints added when the form was created since we are not attaching this particular form to any object
$form->handleRequest($request);
//alternatively (makes no differene from the former)
//$form->submit($request->request->get($form->getName()));
if ($form->isValid())
{
//have YET to see this
echo 'valid';
exit;
}
else{
//echo 'not fuckin valie, WHY?';
//exit;
// get a ConstraintViolationList
$errors = $this->get('validator')->validate( $form );
$result = '';
//nothing returns here when form is valid against constraints, its just empty
echo $errors;
// iterate on it
foreach( $errors as $error )
{
$error->getPropertyPath() : the field that caused the error
$error->getMessage() : the error message
}
}
$data = $form->getData();
return $this->emailUser($data);
}
return $this->render('ResourceBundle:Default:resources.html.twig', array(
'form' => $form->createView(),
));
}
This is how im posting the data
function postForm($form, callback) {
/*
* Get all form values
*/
var values = {};
$.each($form.serializeArray(), function (i, field) {
values[field.name] = field.value;
});
/*
* Throw the form values to the server!
*/
$.ajax({
type: 'POST',
url: '/share',
data: values,
success: function (data) {
callback(data);
}
});
}
$(document).ready(function () {
//bind an event to submit on 'email resources' button
$('div#share form').submit(function (e) {
//disable symfonys default submit button hehaviour
e.preventDefault();
postForm($(this), function (response) {
//replace html here
// Is this where im going wrong? Do i need to replace the form here?
});
});
});
EDIT: Here is the pertinent portion of the main template code that calls the action in the first place
<div id="share" class="hidden" >
<h2>Share Resources</h2>
{% render url('email_share') %}
</div>
here is the form template code thats rendered in the shareAction (in its entirety currently)
{{ form(form) }}
was
{% if form | default %}
{{ form(form) }}
{% endif %}
{% if mail_response | default %}
{{ dump(mail_response) }}
{% endif %}
The hidden token input portion of the form
<input id="form__token" class="form-control" type="hidden" value="8QWLo8xaPZFCKHBJbuc6CGNIcfmpWyT-yFdWScrsiJs" name="form[_token]">
The two underscores worry me a bit (form__token)
EDIT2:
The problem is in the CSRF token somewhere. it could be the form input name, the token itself is already expired, or something else.
I pretty much narrowed it down by constructing my own form module like this
//set up our form defaults here
$defaultData = array('comment' => 'Type your comment here');
$session = new Session();
$secret = '123xyz';
$vendorDir = realpath(__DIR__ . '/../vendor');
$vendorFormDir = $vendorDir . '/symfony/form/Symfony/Component/Form';
$vendorValidatorDir =
$vendorDir . '/symfony/validator/Symfony/Component/Validator';
// create the validator - details will vary
$validator = Validation::createValidator();
$formFactory = Forms::createFormFactoryBuilder()
->addExtension(new HttpFoundationExtension())
//->addExtension(new CsrfExtension(new SessionCsrfProvider($session, $secret)))
->addExtension(new ValidatorExtension($validator))
->getFormFactory();
//build the form here
$form = $formFactory->createBuilder('form', $defaultData)
->setAction($this->generateUrl('email_share'))
->setMethod('POST')
->add("emails", 'email', array(
//......
//same as above for the rest......
The form FINALLY passes validation like this, and when i uncomment the line
->addExtension(new CsrfExtension(new SessionCsrfProvider($session, $secret)))
i get the same error as i did before, that the CSRF token is invalid.
To me, this is pretty much pointing to somewhere in this module, or im not calling something, or extending something right, or the javascript is returning a form that is older than the CSRF module is expecting, or the hidden token form input has a name that is other than that what the CSRF module is looking for. I dont know enough about symfony's internals to diagnose this, this is why i come here for help. Does anybody see a potential issue?
EDIT:3 i feel like i shouldnt be using isValid(), as mentioned, i am not passing an object, im passing an array. see this URL http://symfony.com/doc/current/book/validation.html#validating-values-and-arrays. Im trying to figure out how to properly check against the constraints, and im thinking isValid() is NOT the way to go after all, or else im missing something fundamental.. I just cant figure if i only check against constraint errors, how can i use the CSRFprotection still, or is that only for objects or something?? Do i need to pass this in manually since im not using an object?
EDIT 4:
It looks like i might have uncovered the crux of the problem, yet i cant figure out how to solve it yet.
on this file Symfony\Component\Form\Extension\Csrf\CsrfProvider\DefaultCsrfProvider
i put some output to track the token, and it appears that the token is regenerating for the comparison, which seems like IT SHOULD NOT be the case I would expect that it would compare the passed down token to one in memory, but in fact, its being generated twice, once for the form, then once again for the comparison.
At first i suspected it was possible that the browser and the ajax are running from two different sessions, and a mismatch can be caused by this because i was using SessionCsrfProvider(), but after switching to ->addExtension(new CsrfExtension(new DefaultCsrfProvider($secret))) i had the same problem.
Is this a bug, am i going crazy, or am i missing something as simple as the form id in the building of the form or something?
heres the code, and the results i found from that code.
//Symfony\Component\Form\Extension\Csrf\CsrfProvider\DefaultCsrfProvider
public function isCsrfTokenValid($intention, $token)
{
echo '<pre>Warning, Symfony\Component\Form\Extension\Csrf\CsrfProvider\isCsrfTokenValid';
echo'<br>, here is out token handed down to compare<br>';
var_dump($token);
echo '<br>the new generated token thats being compared to is<br>';
var_dump($this->generateCsrfToken($intention));
echo '</pre>';
return $token === $this->generateCsrfToken($intention);
}
returns
//the form
array(6) { ["emails"]=> string(19) "email#email.net"
["comment"]=> string(2) "yo" ["name"]=> string(5) "me"
["email"]=> string(19) "email#email.net" ["copy"]=>
string(1) "1" ["_token"]=> string(40)
"a11e10eb323f7a4d19577e6d07e68be951ceb569" }
Warning,
Symfony\Component\Form\Extension\Csrf\CsrfProvider\isCsrfTokenValid ,
here is out token handed down to compare string(40)
"a11e10eb323f7a4d19577e6d07e68be951ceb569"
the new generated token thats being compared to is string(40)
"e83cdf94b15e63e822520b62402eb66e0b1f03d3"
The CSRF token is invalid. Please try to resubmit the form.
Blockquote
EDIT 5:
the problem has been traced to here, look at this code in the DefaultCsrfProvider
public function generateCsrfToken($intention)
{
return sha1($this->secret.$intention.$this->getSessionId());
}
public function isCsrfTokenValid($intention, $token)
{
return $token === $this->generateCsrfToken($intention);
}
The token can never be valid during an ajax call, unless a param is set in the generateCsrfToken() token method to allow passing of the session, to which you would want to pass that via ajax, like this
public function generateCsrfToken($intention, $session)
{
if(!$session)
{
$session = $this->getSessionId()
}
return sha1($this->secret.$intention.$session);
}
which i would think would completely reduce teh security of the whole idea of the CSRF in the first place.
is there another provide i can use for ajax calls in within the symfony framework? If were depending on the sesion, this pretty much leaves out both the SessionCsrfProvider class and the DefaultCsrfProvider class to process this, unless im missing something very obvious... should i just grab, pass, then reset the session on the ajax call????
Ok, after i figured this out thus far, i just found this post Symfony CSRF and Ajax ill see if i can make heads or tails from it.

To see errors you should render that form. In your code when form is invalid the method returns $this->emailUser($data); but it should render the form. Try this:
if ($this->getRequest()->isMethod('POST')) {
//data is already validated by constraints added when the form was created since we are not attaching this particular form to any object
$form->handleRequest($request);
//alternatively (makes no differene from the former)
//$form->submit($request->request->get($form->getName()));
if ($form->isValid())
{
//have YET to see this
echo 'valid';
//this is the place to process data
//$data = $form->getData();
//return $this->emailUser($data);
exit;
}
}
return $this->render('ResourceBundle:Default:resources.html.twig', array(
'form' => $form->createView(),
));
That should render invalid form again on submit and show error
You can also try this
if ($form->isValid())
{
//have YET to see this
echo 'valid';
exit;
}else{
echo '<pre>';
\Doctrine\Common\Util\Debug::dump($form->getErrorsAsString(), 9);
echo '</pre>';
}
To show errors in debug style if form is invalid.

Related

Symfony2 create filled Form based on two diffrent types (editAction)

I was looking for a answer, but i never found something. .
My starting Position:
This classes / files are included
Controller, DataDFu1Controller.php
Form DataAPatientType.php ,DataDFu1Type.php
Entity DataAPatient, DataDFu1
views/DataDfu1/ form.html.twig
The DataDFu1Controller contains (to the overview) the indexAction, newAction and
editAction and so on.
Both Formtypes (DataAPatientType.php ,DataDFu1Type.php) comes in one Form (look Method) this form goes to be rendered later in the form.html.twig file for the newAction and the editAction
For the newAction i did it so:
private function createNewForm(DataAPatient $entity)
{
$form = $this->createForm($this->get('data_livebundle.form.dataapatienttype'), $entity, array(
'action' => $this->generateUrl('dataapatient_new'),
'method' => 'POST',
));
return $form->add('dFu1', new DataDFu1Type());
}
later the form comes rendered. . .
So first i create "DataAPatientType.php" Form and then i add the "DataDFu1Type.php" to the form.
In the view -> form.html.twig it looks like that.
for DataDFu1Type:
{{ form_widget(form.dFu1.fu1Examiner1)}}
for DataAPatientType:
{{ form_label(form.pSnnid, 'SNN-ID (if known)', {'label_attr':{'style':'margin-top:3px'}})}}
So i can get a variable or a function with the suffix 'dfu1' after the form.
Everything works so fine. I hope the condition are understandible till now..
Now my Problem:
I have to create also an editAction which opend of course the same view-> form.html.twig with the filled values from a dataset (entity). In this process i don't understand how i can create the Form Object based also (DataAPatientType, DataDFu1Type) with the corresponding data. -> I'm trying to be more specific
private function createEditForm(DataDFu1 $entity)
{ /*
* This function shoud create the editform which insists
* DataAPatientType.php ,DataDFu1Type.php included the data from
* $entity. I have the opportunity to get the entity for DataDFu1Type
* easy directly with the Primary Key and the data for DataAPatientType
* over a Foreign Key which is safed in the $entity
*
*/
}
So i only dont understand how i can create a Form based on two types (DataAPatientType.php ,DataDFu1Type.php) with the corresponding Data inside, that i can render it like in the newAction.
For one Form i did it everytime like so and it works.. but for two types i tried a lot things which didnt worked. Have somebody a experiance? or a Solution for this Problem?
the syntax of the form.html.twig isnt changeable so the form has to be rendered equivalent like in the newAction
Example for creating a form based only on one Type and not two
private function createEditForm(Event $entity)
{
$form = $this->createForm($this->get('qcycle_eventbundle.form.eventtype'), $entity, array(
'action' => $this->generateUrl('event_edit', array('id' => $entity->getId())),
'method' => 'POST'
));
$form->add('preview', 'button', array('label' => 'Preview', 'attr' => array('data-preview' => 'preview')))
->add('submit', 'submit', array('label' => 'Save Changes'))
->add('sendAndSave', 'submit', array('label' => 'Send Mail & Save'));
return $form;
}
i really hope, that my problem and Question understandable
thanks
mjh
If i understand you have this form:
class DataAPatientType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('dFu1', new DataDFu1Type());
$builder->add('pSnnid', 'text');
[...]
}
}
Then in create
$form = $this->createForm(DataAPatientType(), new DataAPatient());
And in edit you can simply do something like
private function createEditForm(DataDFu1 $entity)
{
$form = $this->createForm(DataAPatientType(), entity); //here the form will be "populated" by the entity data
So if you want to set some default value or overwrite an existing value for example, you would use
private function createEditForm(DataDFu1 $entity)
{
entity->setPSnnid('whatever')
$form = $this->createForm(DataAPatientType(), entity); //here the form will be "populated" by the entity data

How to use nested form handlers in Symfony 3

I'm learning Symfony to move an old website from flat-PHP to a framework.
In the old website I have a page with two levels of forms: the first one is just a button with which the user accepts some conditions, while the second one sends a message to the website admin. When the user clicks the button in the first form, the page is refreshed and the second form appears. The important thing is that the user can't get access to the second form without pushing the first button.
Right now, the action method is this one:
/**
* #Route("/ask-consultation", name="ask_consultation")
*/
public function askConsultationAction(Request $request)
{
$em = $this->getDoctrine()->getManager();
$form = $form = $this->createFormBuilder()
->add('submit', SubmitType::class, array('label' => 'Confermo'))
->getForm();
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$consultation = new Consultation;
$form = $this->createForm(AskConsultationType::class, $consultation);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$consultation = $form->getData();
$em = $this->getDoctrine()->getManager();
$em->persist($consultation);
$em->flush();
return $this->redirectToRoute('homepage');
}
return $this->render('visitor/consultation/ask_step2.html.twig', [
'base_dir' => realpath($this->getParameter('kernel.root_dir').'/..').DIRECTORY_SEPARATOR,
'sidebars' => $em->getRepository('AppBundle:Sidebar')->findAllOrderedBySortingPosition(),
'form' => $form->createView(),
]);
}
When I go to /ask-consultation it shows me the button to accept certain conditions; when I click the button, it shows me the form to send the message, but when I send it, I don't get redirected to homepage, but again to the first page of /ask-consultation.
I understand why this code doesn't work, but I can't understand how to make it work. One solution could be some sort of modal dialog for the first form, but if possible I'd prefer to handle all the passages in PHP. Is it possible to split the form handling without changing the route?
The most important thing in my case is that the user can't get to the second form without first having clicked on the first button.
Render everything in a single form but use javascript (jQuery) to hide the ask-consultation part of the form. When the user clicks "Confirm", then unhide the form. This avoids the controller altogether for the confirmation step. You don't seem to be recording this confirmation anyway, so just let the client side handle things. You can still check (on form submission) to see that the "confirmation" was accepted, for example by setting a hidden variable through javascript.
It turns out the answer was quite simple.
In my mind, I had to handle the second form inside the first if ($form->isSubmitted() && $form->isValid()), but of course that's not the path taken by PHP. Everytime the route gets loaded, askConsultationAction() is run from its first instruction. I simply had to initialize both forms (with different variable names, obviously), and handle the forms one after the other, rather than one inside the other.
This code works like a charm:
/**
* #Route("/ask-consultation", name="ask_consultation")
*/
public function askConsultationAction(Request $request)
{
$em = $this->getDoctrine()->getManager();
$consultation = new Consultation;
$conditions_form = $this->createFormBuilder()
->add('submit', SubmitType::class, array('label' => 'Confermo'))
->getForm();
$consultation_form = $this->createForm(AskConsultationType::class, $consultation);
$consultation_form->handleRequest($request);
$conditions_form->handleRequest($request);
if ($conditions_form->isSubmitted() && $conditions_form->isValid()) {
return $this->render('visitor/consultation/ask_step2.html.twig', [
'base_dir' => realpath($this->getParameter('kernel.root_dir').'/..').DIRECTORY_SEPARATOR,
'sidebars' => $em->getRepository('AppBundle:Sidebar')->findAllOrderedBySortingPosition(),
'form' => $consultation_form->createView(),
]);
}
if ($consultation_form->isSubmitted() && $consultation_form->isValid()) {
$consultation = $consultation_form->getData();
$em = $this->getDoctrine()->getManager();
$em->persist($consultation);
$em->flush();
return $this->redirectToRoute('homepage');
}
return $this->render('visitor/consultation/ask_step1.html.twig', [
'base_dir' => realpath($this->getParameter('kernel.root_dir').'/..').DIRECTORY_SEPARATOR,
'sidebars' => $em->getRepository('AppBundle:Sidebar')->findAllOrderedBySortingPosition(),
'form' => $conditions_form->createView(),
]);
}
Maybe, it would be even better if I used an else... if....

Add a required form field based on submitted data in Symfony2

I have a form with a status select. If a certain status is selected and the form is submitted it should reload and require an additional field.
I have read Dynamic generation for submitted Forms and almost every other post on the internet and about this topic and tried different event combinations (and got different errors) but I still struggle to make this to work correctly.
This is what I have so far:
FormType
private function addProcessAfterField(FormInterface $form)
{
$form->add('processAfterDate', 'date', array('required' => true));
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('status', 'entity', array(
'class' => 'Acme\Bundle\ApplicationBundle\Entity\LeadStatusCode',
'choices' => $this->allowedTypes
));
$builder->addEventListener(FormEvents::PRE_SET_DATA, function(FormEvent $event){
$form = $event->getForm();
$data = $event->getData();
if ($data->getStatus()->getId() == LeadStatusCode::INTERESTED_LATER) {
$this->addProcessAfterField($form);
}
});
$builder->get('status')->addEventListener(FormEvents::POST_SUBMIT, function(FormEvent $event){
$data = $event->getData();
if ($data == LeadStatusCode::INTERESTED_LATER && !$event->getForm()->getParent()->getData()->getProcessAfterDate()) {
$this->addProcessAfterField($event->getForm()->getParent());
}
});
$builder->add('comment', 'textarea', array('mapped' => false));
$builder->add('Update', 'submit');
}
Error:
ContextErrorException: Catchable Fatal Error: Argument 1 passed to Proxies\__CG__\Acme\Bundle\ApplicationBundle\Entity\Lead::setProcessAfterDate() must be an instance of DateTime, null given, called in /var/www/application.dev/vendor/symfony/symfony/src/Symfony/Component/PropertyAccess/PropertyAccessor.php on line 360 and defined in /var/www/application.dev/app/cache/dev/doctrine/orm/Proxies/__CG__AcmeBundleApplicationBundleEntityLead.php line 447
As already mentioned I tried different event combinations, one was almost working but then the date was never persisted to the entity so I added the \DateTime type-hint to the setProcessAfterDate() method. I am not sure if I don`t understand the event system correctly or if the error lies somewhere else.
Well, it might not be the best way to solve it, but to make long story short:
$form->handleRequest($request);
if($form->isValid()) // check if the basic version of the form is ok
{
$form = $this->createForm(new XXXXForm(), $form->getData()); // you recreate the form with the data that was submitted, so you rebuild the form with new data
if($form->isValid())
{
// ok
}
// not ok
}
Then inside buildForm function, you base the "required" attribute value of fields based on what you want:
'required' => $this->getCheckRequired($options)
private function getCheckRequired($options) // checks whether field should be required based on data bound to the form
{
if($options && isset($options['data'])
{
switch $options['data']->getStatus():
// whatever
;
}
return false;
}
As I said, this is not the best solution, and it doesn't fix your approach, but rather proposes a different one, but it does the job

Symfony2 invalid dynamic form with no error

I got a problem with a dynamic form on symfony2. I'm trying to generate some fields for a submitted form. In others words, the user enters some values, submits the form, and according to these values, my dynamics fields are added to this same form (which is, obviously, displayed a second time). To do that, I used this example from the cookbook : http://symfony.com/doc/current/cookbook/form/dynamic_form_modification.html#cookbook-form-events-submitted-data
So, here is my FormationType class
class FormationType extends AbstractType
{
private $em;
private $context;
public function __construct($em, $context) {
$this->em = $em;
$this->context = $context;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name')
->add('date')
->add('type', 'choice', array(
'mapped' => false,
'choices' => Formationlist::getTypeTypes(false),
'empty_value' => false,
))
->add('cost')
->add('travelCost')
->add('maximum')
->add('location')
->add('schedule')
;
$formModifier = function(FormInterface $form, $type) {
$formationList = $this->em->getRepository('CoreBundle:FormationList')->findBy(array("year" => 1, "type" => $type));
$form->add('formationList', 'entity', array(
'label'=> 'Titre formation',
'choices' => $formationList,
'class' => 'CoreBundle:FormationList',
'property' => 'title',)
);
};
$builder->addEventListener(
FormEvents::PRE_SET_DATA,
function(FormEvent $event) use ($formModifier) {
$data = $event->getForm();
$type = $data->get('type')->getData();
$formModifier($event->getForm(), $type);
}
);
$builder->get('type')->addEventListener(
FormEvents::POST_SUBMIT,
function(FormEvent $event) use ($formModifier) {
$type = $event->getForm()->getData();
$formModifier($event->getForm()->getParent(), $type);
}
);
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'EXAMPLE\CoreBundle\Entity\Formation'
));
}
public function getName()
{
return 'example_corebundle_formationtype';
}
}
So, the two addEventListener work pretty well. The first time my form is displayed, the field in formModifier is not loaded, as expected. My controller class is the following one :
public function createAction(Request $request)
{
$em = $this->getDoctrine()->getManager();
$contextSrv = $this->get('example.service.context');
$context = $contextSrv->getContext();
$entity = new Formation();
$form = $this->createForm(new FormationType($em, $context), $entity);
$form->bind($request);
if ($form->isValid()) {
$em->persist($entity);
$em->flush();
return $this->redirect($this->generateUrl('formation_show', array('id' => $entity->getId())));
}
return array(
'entity' => $entity,
'form' => $form->createView(),
);
}
Since one of my dynamic field can't be null, the first time the form is submitted, it can't be valid. So, the FormationType is loaded a second time. That means, if the field "type" was filled, my formModifier() function can load the dynamic field (formationList). Until there, everything works pretty well, and I got my new field.
But, after a second "submit" on the form...nothing happen. The page is just reloaded, and no errors are displayed.
I checked the form content with
var_dump($request->request->get('example_corebundle_formationtype'));
-> Every fields (including the dynamic one) are filled with valid values.
I also try this :
foreach($form->all() as $item) {
echo $item->getName();
var_dump($item->getErrors());
}
-> These lines don't show any error. But, the form is never valid.
var_dump($form->isValid());
-> It returns false. So the form is invalid.
Finally, if I remove the whole dynamic part, my form works.
I don't understand what's wrong. There is no errors displayed by the form, and the csrf token seems right. Did I miss something ? Thanks for your help.
I know this is a bit outdated but comes up quite high on Google.
The getErrors() metod returns only Form's global errors not error messages for the underlying fields, you need either getErrors(true) or more sophisticated method when using embeded forms in a form. Please see: https://knpuniversity.com/blog/symfony-debugging-form-errors for more information.
There is probably a validation error lying somewhere in your form.
Instead of your complicated calls to Form::getErrors() - which is not fully recursive, as the errors of any field deeper than the 2nd level will not be displayed - you should use Form::getErrorsAsString().
This is a debug method created by the Symfony guys for developers such as you, trying to understand where a validation error could lie in complex forms.
If no error is displayed although it should, this may be a form theming error. When creating a custom form theme, it is possible that a developper overrides or forgets to display the error block of a field.
Another possible source of the problem lies is the general display of the form. If you display your form using {{ form_widget(form) }}, then any error that bubbles to the top form will never be displayed. Make then sure that you use {{ form_row(form) }} instead.
I also encountered this problem a few times.
In my case I posted data in JSON format, so I had to do a request listener with a high priority which transforms json data into normal POST data, which is available in $request->request.
One scenario where the $form is invalid and there is no errors in also when the post data is empty, try to make a dump of $request->request->all() to see if you have the data.

Using Zend_Validate_Identical in Form Validation

I'm trying to use the 'Identical' validator to validate whether two passwords are the same in my registration form, but it keeps trying to validate against the actual word I enter for the token rather than the form element that I want to validate against. Code looks like this: (This is my form model constructor..)
$password = new Zend_Form_Element_Password('password');
$password->addValidator('Regex',false,array('pattern' => '/^.*(?=.{6,20})(?=.*[\d])(?=.*[a-zA-Z])/'))
->addValidator('StringLength',false,array('max'=>20))
->setRequired(true);
$password2 = new Zend_Form_Element_Password('password2');
$password2->setRequired(true);
$password2->addValidator('Identical',false,array('token'=>'password'));
$register = new Zend_Form_Element_Submit('register');
$this->setDecorators(array(
array('ViewScript',
array('viewScript' => '_form_registration.phtml'))
)
);
$this->addElements(array($firstName,$lastName,$email,$city,$password,$password2,$register));
Instead of validating against the form element called 'password' it keeps trying to match against the actual string 'password'
The work around I have is that I create a validator after the data has been posted to the controller, and validate against the post data, but if there is any more modular way to do this (AKA leaving the logic within the form constructor) I would love to know.
Thank you in advance
Are you outputting your form correctly?
I see that the decorator you're using is ViewScript so I'm guessing that you are coding the form's html yourself in some other script.
If so, are you following the Zend way of assigning names and id values to your elements? If you aren't, when you pass in the values to your form the context might not be set up correctly and it won't find the 'password' element that you need to check against.
My suggestion right now is to ouput the form using the form default decorators and look at how the ids and names look for the elements. Then, try to copy those names in the form.phtml that you're using.
After add the Identical Validator on your 'password2' element.
Try to overload isValid() function into your Form Object like this:
public function isValid ($data)
{
$this->getElement('password2')
->getValidator('Identical')
->setToken($data['password'])
->setMessage('Passwords don\'t match.');
return parent::isValid($data);
}
I have been having the exact same issue.
It was fixed by rewriting the code with an outside function to validate identical as such.
<?php
class RegisterForm extends Zend_Form
{
/**
* create your form
*/
public function init()
{
$this->addElements(array(
new Zend_Form_Element_Password('password',
array( 'label' => 'Password:',
'required' => true,
'filters' => array('StringTrim', 'StripTags'),
'validators' => array(array(StringLength', false, array(5, 25)))
)
),
new Zend_Form_Element_Password('pass_twice',
array('label' => 'Pass Twice',
'required' => true,
'filters' => array('StringTrim', 'StripTags'),
'validators' => array('Identical')
)
)
);
}
public function isValid($data)
{
$passTwice = $this->getElement('pass_twice');
$passTwice->getValidator('Identical')->setToken($data['password']);
return parent::isValid($data);
}
}
?>
Solution from: http://emanaton.com/node/38

Categories