Symfony programmatic form validation - php

In Symfony 2.5.10, I have multiple form types, each with their own validation groups, calling various class constraints. Each type represents a particular step in a workflow. The final step iterates over all other steps, performs the necessary validation and displays any possible violations to the user.
The data behind each form type (data_class) is always the same: a doctrine entity.
Each displayed violation links to the URL/path representing the step in question, with an additional parameter which, when present, should trigger the validation. The idea is that the form should display the errors immediately, without submitting. In other words, I wish to trigger form submission programmatically (server-side, not JS!).
I've tried various different ways:
Somehow put the entity (which I have) into the request and set the method of the latter to POST, so that Form::handleRequest handles everything automatically. Problem: converting the entity object to an array, as would be returned from a real form submission. Maybe a DataTransformer? PropertyAccessor?
As 1. but call Form::submit instead. See http://symfony.com/doc/2.5/cookbook/form/direct_submit.html#calling-form-submit-manually
Validate myself (in the same way I do in the final step), giving me a ConstraintViolationList. Iterate over the list and call Form::addError for each violation. Problem: I need to add the errors to the correct child elements, not just the parent. Maybe I could do this using the propertyPath of each error, but how to go from that to child form?
Any help would be much appreciated. I've trawled stackoverflow and the web in general, but to no avail.

First way looks good. You just should check the ajax form and them just use something like
$entity = $form->getData()
to retrieve data from form (which could be array, entity, or some other stuff you have in you form).
Then just pass this entity to the twig view to form an request answer. Alongside with entity you might want to provide form errors aquired from $form->createView() FormView object.

Related

Symfony Form: How to replace form class / retrieve all extra data?

The Symfony 2 Form Component really is something. I guess you know that. Trying to understand what works how is just an seemingly impossible task; and I'm quite experienced at browsing through codebases.. But man, the Form component.. OMG
TL;DR
Below are details, this issue tries to ask
Is it possible to replace the class \Symfony\Component\Form\Form?
Or: How to easily get all extra data from all fields of type form of a form?
Or, a related question: How on earth does it work that if i do $form->add('ss', 'form') - obviously the Core\FormType class gets involved but when i retrieve it later, it is an instance of \Symfony\Component\Form\Form? Where does that happen and can this maybe overriden so it uses a different class there?
Details
The situation
Imagine a Controller that receives a n deep JSON payload. This payload gets decoded and validated through Form. Now, most of this JSON structure is mapped by Models (Doctrine ODM Entities). But some sub-properties are just "hashes" - the client is allowed to post whatever he wants there.
Those "hash" subproperties are fields of type form, are compound flagged and can have extra fields.
The problem
Bottom line our problem is, that all "extra fields" are not returned by $form->getData(). We are unable to specify those fields (and their types) as we don't know what will come - so all this data is part of extraData.
The solution?
So i thought - OK - let's modify the FormFactory so it'll return our Custom extension of Form - one that also returns extraData when getData is called. Easy right.? Noo, not easy..
My idea let me inspect FormFactory, the FormBuilders, the ResolvedFormTypes, whatnot.. and at all important places (like formBuilder->getForm()) - the classes I want to override/replace are instanciated static - no usage of the DIC..(!)
I've read somewhere that the Form component in Symfony 2 was written by the devil himself to let we developer go mad. Luckily I'm not so mad to try to replace the entire Form component by myself ;)
Jokes aside, when you define a form (by creating a <entity>Type class, like UserType) you are defining a new type that can be as simple as a single text input or as complex as full-fledged form (our UserType from before). The "real" form (Symfony\Component\Form\Form) is created from your type definition:
return $this->container->get('form.factory')->create(
new UserType(), new User(), $options
);
Have you evaluated the idea to create a custom form type for your "extraData"? Or maybe it can be mapped as a JSON string that's created by a client-side JavaScript before submit.

Symfony Form, ViewTransformer with ResizeformListener and Validation

I'm using Symfony Form standalone (i.e. not the full stack framework) to build an "images" type that allows users to upload 1-x images and give each one a title, change the order of the uploaded images and so on.
I've done this by creating an images type which contains a file and a text field. I've also subclassed the CollectionType to act as a holder (like in the cookbook example). Lastly, I have a dedicated storage class associated with the CollectionType which takes care of persisting the uploaded data (I can't persist the individual images separately since I also need to save the order).
This all works more or less, i.e. I can add/update/delete images normally. The only problem I have is when there is a validation error in some other field in the same form (e.g. if I don't fill out a required field). In this situation, the uploaded file never reaches the storage class, and when the form is rendered again, the view data is in the wrong format. So I've added a ViewTransformer to my custom CollectionType that detects this case and performs the necessary transformation. Which basically works, but unfortunately, it puts the transformed data into the wrong place: The FormView instance corresponding to the CollectionType has the correct (i.e. transformed) data, while the child instance (i.e. the image type) has the untransformed values. So the question is: Can I make it pass the transformed data to the correct child, or shouldn't this happen automatically even?
I can provide some example code if it helps, but it might take a moment to extract only the relevant parts. In the meantime I wanted to ask: Am I even approaching this problem in the right way? The documentation on the inner workings of Symfony Form is a bit wanting, so I'm not sure if there wouldn't be an easier way to do what I am trying to do.
EDIT: After a lot of debugging I found out that my problem is that Transformers are called differently during submit() and setData(): During submit() (i.e. when data is loaded from the storage backend), the view transformer runs on the parent (i.e. CollectionType), and afterwards the converted $viewData is passed to the children. During submit(), it's the other way around: First, the child data is mapped, and afterwards the view transformer is run. I think I'll open a ticket on gh...

Symfony 2.3 validating form with handleRequest is insanely slow

I am submitting a form with a lot of fields and trying to validate it with handleRequest($request) as it is shown in the Symfony documentation.
My entity is very big and has a lot of relations with other entities.
handleRequest($request) is validating each form field submitted and checking for errors.
The problem found is while submitting an id of a related entity of my main entity (in example a person of an office), handleRequest will internally get all objects of the related entity (the full table of the related entity, all persons) and hydrating them as objects.
I think it should just check if the submitted id exists in the other table, get that related entity object and check it for errors (instead of getting all the related table).
If you check and debug the source code of Symfony2 handleRequest, you may easily spot the same problem at this lines:
Form/Form.php
// Normalize data to unified representation
$normData = $this->viewToNorm($viewData);
$value = $transformers[$i]->reverseTransform($value);
How can I still validate the form without dealing with this issue which makes it insanely slow to validate a form with handleRequest($request)?
If I don't use handleRequest to validate it, which automatically add the errors to my form for each field, how could I manually validate each field and later add the errors to my form for each field and show them in the next view?
This question is a little vague, and the answer very much depends on your specific form. Please post the form definition that is giving you the hardest time.
Check to make sure that you are not EAGER fetching associations here.
handleRequest() is going to take the request object and construct the model that your form describes, as your form defined it to.
If the objects are required in order to display data on your initial form to the user, or to validate the data on submit, the "entity" field type will fetch all of the objects you told it to in its definition. If you are displaying a big select list, for example, all of this data is needed.
I had a similar problem in the past and it was because I was using a lot of choice fields that were being used as a series of multiple select checkboxes. My bottleneck was actually in the twig layer while rendering out the thousands of checkboxes I had stored as separate entities.
I switched from a set of checkboxes to a single multi-select box and it increased my speed significantly.
in my case, after having a similar behaviour I detected that it was a problem in my xdebug configuration. By editing php.ini and disabling xdebug I found that everything went much faster. It can be interesting to do this check when all else fails. I'm leaving this message here in case it might be of help to someone else.

Validate field based on another field using additional database

I have a simple form, which contains two fields, the first field is just a select and the second field contains a value, which needs to be checked with the help of the first field.
I have found a similar question Symfony2 form validation based on two fields and tried to use the Callback validator.
I have read the documentation, but I can't figure out how I can use it with access to the database.
It seems that the only way is to call the validation method statically, but in this case I loose the context of my controller. I need it to access for example the database.
In this relation I am using Silex and want to access services provided by it.
The form isn't mapped to any class, so the creation of an own constraint looks wrong for me, because I don't see any way to pass the other fields to the validator.
Is there any way to achieve it?
Or do I need another approach?
I strongly advise you to map the form to a class and create a custom constraint...
I have written a detailed example on how to:
create your own validation constraint
turn it into a service
inject the object-manager
access the database from the constraint
TLDR:
What you need is a custom validator on class level.
A class-level validator is needed because you need to access the whole
object (not only a single property) if you want validate multiple related values...
... or need to fetch something from database using another property as select-criteria.
Here's the the complete answer with example.
Another option could be creating a form-event listener and passing the object-manager to it before adding it to the form.
Then take care of the validation-process (checking the data against the database + eventually adding errors to the form) inside the listener yourself.

Constraint Validator validates Entity before Entity is ready in Symfony 2

My Symfony2 (2.4.2) application has a form, and an entity which I have set some validation constraints on some fields. One of the entity's variables is an array which should not be blank.
The form has a field which is not mapped to the entity directly. The input is a comma-delimited string; this string will be preg_split into an array and saved to the instance variable aforementioned. This operation is triggered by FormEvents::POST_SUBMIT.
However, when I submit the form, even though the input string is not empty, the form shows that the validation fails. I did a bit of debugging and found that the validation actually happens before FormEvents::POST_SUBMIT. I have tried other FormEvents but no luck.
Is there a way to trigger the event before the validation?
Note: The scenario above is shortened, it'd be too long for me to ask a question if the context is my real application.
Use DataTransformers for this sort of operation. Not form events.
The data transformer will kick in before the validation. The transformer will transform whatever the user types into whatever you expect internally. The validation will then operate on the internal data format.
http://symfony.com/doc/current/cookbook/form/data_transformers.html

Categories