I am using the same form to "preview" an object as I am to "edit/update" the same object.
In my showAction() for the controller I have the following code:
$form = $this->createForm(new SalesEntityType($entity), $entity, array('read_only' => true) );
This code works great for the primary form but there are a number of subforms that are made part of this by inclusion. One example in the show.html.twig is:
{% include 'TargetCommonBundle:Hours:hoursForm.html.twig' with { form: hours } %}
Unfortunately, the read_only setting on the parent form does not seem to cascade to the included subforms. Is there a way to handle this?
Try:
$form = $this->createForm(
new SalesEntityType($entity),
$entity,
[ 'disabled' => true ]
);
See: vendor/symfony/symfony/src/Symfony/Component/Form/CHANGELOG.md, first line
// It is the way more fast to disabled a form
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->setDisabled(true);
}
Related
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
I want to create a Survey that consists of multiple Questions (different implementation classes).
I would love to represent the Survey creation as well as all the Questions as FormType to make it easy to have Validation and all the good stuff from the Symfony Form Component.
It is very easy to nest forms like described here.
But now comes the tricky part:
Every Question has its own FormType and a survey should be created as well as answered (filled out by the survey taker) on one page. So all questions on one page. More or less this is like Google Forms, being able to add new Questions on one page quickly as well make it easy for the user to see all questions at once.
My 2 Question would be:
How can I add a FormType whose nested Types are known at runtime (admin can select which Question Type he wants to add)?
How can I validate and store all the Forms on one page when the survey taker fills out a survey?
I would love to hear some ideas from you.
Thanks,
Lukas
Use the power of Listeners. You can use almost the same flow that CollectionType uses with ResizeListener.
public function preSetData(FormEvent $event)
{
$form = $event->getForm();
$data = $event->getData();
...
foreach ($data as $name => $value) {
$form->add($name, $this->getTypeByClass($value), array_replace(array(
'property_path' => '['.$name.']',
), $this->options));
}
}
...
public function preSubmit(FormEvent $event)
{
$form = $event->getForm();
$data = $event->getData();
...
if ($this->allowAdd) {
foreach ($data as $name => $value) {
if (!$form->has($name)) {
// put special value into sub-form to indicate type of the question
$type = $value['type'];
unset($value['type']);
$form->add($name, $type, array_replace(array(
'property_path' => '['.$name.']',
), $this->options));
}
}
}
}
Try to implement the very similar flow with allowDelete, allowAdd features.
There should be another classes like SurveyData.{items, survey, ...} with n-1 relation to Survey, SurveyItem.{answer, ...} with n-1 association to QuestionAnswer. On the base of the your structure there should be written validators.
Cascade validation can be triggered with Valid constraint.
http://symfony.com/doc/current/reference/constraints/Valid.html
UPDATE
What to do to form mutable part.
public function buildForm(FormBuilderInterface $builder, array $options)
{
if ($options['allow_add'] && $options['prototyped']) {
// #var ['prototype_name' => '__name__', 'type' => 'question_type']
foreach ($options['prototypes'] as $prototype) {
$prototype = $builder->create($prototype['prototype_name'], $options['type'], $options['options']);
$prototype->add('type', 'hidden', ['data' => $options['type'], 'mapped' => false]);
$prototypes[$options['type']] = $prototype->getForm();
}
$builder->setAttribute('prototypes', $prototypes);
}
...
}
public function buildView(FormView $view, FormInterface $form, array $options)
{
$view->vars = array_replace($view->vars, array(
'allow_add' => $options['allow_add'],
'allow_delete' => $options['allow_delete'],
));
if ($form->getConfig()->hasAttribute('prototypes')) {
$view->vars['prototypes'] = $form->getConfig()->getAttribute('prototypes')->createView($view);
}
}
Now are able to use prototypes in the form block in twig.
{% for key, prototype in prototypes %}
{% set data_prototypes[key] = form_row(prototype) %}
{% endfor %}
{% set attr = attr|merge({'data-prototypes' : data_prototypes|json_encode })
Now you don't need ajax requests in JS - just use prototypes.
(var collection = $('your_collection')).append(collection.data('prototypes')[question_type].replace(/__name__/g, counter+1));
You added the element to collection, now admin can fill it and submit the form. Rest of the work (mapping data to class) will be done by Symfony.
I'm new with Symfony2 (2.4.4).
I want to create a HTML layout which shows always a form on top (searchbar). I send the form via post and would like to redirect to another controller, which should pass the user input and generate an output. I created a new function like this:
public function searchFormAction(Request $request)
{
//$defaultData = array('sstring' => 'Suche');
$form = $this->createFormBuilder()
->add('fnr', 'hidden')
->add('sstring', 'search', array('label' => false))
->add('submit', 'submit', array('label' => 'suchen'))
->getForm();
$form->handleRequest($request);
if($request->isMethod('POST'))
{
return $this->redirect('SchmanEmployeeBundle:Employee:search', array(
'sstring' => $form->get('sstring')->getData();
));
}
return $this->render('SchmanEmployeeBundle:Employee:searchForm.html.twig', array(
'form' => $form->createView()
));
}
I extended my base layout (base.html.twig) and include the form with the render function
{% render(controller('SchmanEmployeeBundle:Employee:searchForm')) %}
This works fine and the form is always present in my layout. The given HTML looks like this:
<form name="form" method="post" action="/app_dev.php/">
<div><input type="search" id="form_sstring" name="form[sstring]" required="required"></div>
<div><button type="submit" id="form_submit" name="form[submit]">suchen</button></div>
Now I have 3 questions:
If I submit the form, I don't want to be redirected to the searchAction Controller. This is because the $request->isMethod is always GET. Why? The form actions is post?
In the Symfony Webtool the form section is also empty. I see all form fields (sstring) and the data is always null. Where's the user input?
First off, your form is set to be POST by default, so you should be good. Second, you don't pass any data to be filled by your form, and I think you should. Third, you don't check if the form is valid, which includes the test if it's submitted. You should do this:
$defaultData = array(); // No need for a class object, array is enough
$form = $this->createFormBuilder($defaultData)
->add('fnr', 'hidden')
->add('sstring', 'search', array('label' => false))
->add('submit', 'submit', array('label' => 'suchen'))
->getForm();
$form->handleRequest($request);
if($form->isValid())
{
// Happens if the form is submitted
return $this->redirect('SchmanEmployeeBundle:Employee:search', array(
'sstring' => $form->get('sstring')->getData(); // TODO: This will probably produce an error, fix it
));
}
return $this->render('SchmanEmployeeBundle:Employee:searchForm.html.twig', array(
'form' => $form->createView()
));
Also, I think you shouldn't worry about the form method because you don't have different implementations for other methods. This is the usual way the forms are handled in Symfony. You should read on forms in detail before proceeding, the article is quite informative.
I guess its because you didnt specified, in your routing configuration, that the method of this function is POST.
Because the form never submitted to your function (your function want GET, but send POST)
Where is the last question?
There is a great code to make your search function, it should work (sorry if you dont use annotation).
One good point, you can now use your searchType everywhere in your project, you should make your form like that instead of formbuilder into your controller. Easier to read and to use.
Controller:
/**
* To search something
*
* #Route("/search", name="search")
* #Template()
*/
public function searchAction()
{
$form = $this->createForm(new searchType());
$request = $this->get('request');
if ($request->getMethod() == 'POST')
{
$form->bind($request);
if ($form->isValid())
{
$informations = $form->get('search')->getData();
//make things here
}
}
}
And here is the searchType class:
class searchType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('fnr', 'hidden')
->add('sstring', 'search', array('label' => false))
->add('submit', 'submit', array('label' => 'suchen'));
}
/**
* #return string
*/
public function getName()
{
return 'yournamespace_searchType';
}
}
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.
I have a form with one default value:
class GearType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('options')
->add('model', 'choice', array('choices' => $this->getModelChoices(), 'data' => 2));
}
one of the requirements is form can be pre-populated by re-sellers by passing parameters in URL. It is also nice feature for potential customers to copy and paste link to email, communicators, etc.
I did it this way:
/**
* #Route("/car/gear")
* #Template()
*/
public function gearAction(Request $request)
{
$form = $this->createForm(new GearType());
if ($request->isMethod('POST')) {
$form->bind($request);
if ($form->isValid()) {
return 'is valid';
}
} else {
$get = $this->getRequest()->query->all();
if (!empty($get)) {
$normalizer = new GetSetMethodNormalizer();
$form->setData($normalizer->denormalize($get, new Gear())); # look here
}
}
return array('form' => $form->createView());
}
unfortunately field 'options' has always default value, instead value passed as a parameter.
I have tried to change line # look here into
$gear = $normalizer->denormalize($get, new Gear());
$form = $this->createForm(new GearType(), $gear);
but no result.
It seems that solution is passing additional parameter to GearType object. I do not like this solution. Does anyone know better way?
Add this snippet, and modifiy between the [ ] as appropriate
$form->bind($request);
if ( [ passed parameters from querystring ] ){ //// New Code
$form->getData()->setOptions( [ processed parameter ]); //// New Code
} //// New Code
if ($form->isValid()) {
return 'is valid';
}
The reason for the field options always having default value may be the actual query. Instead of denormalizing and setting the data directly, modify else fragment to:
} else {
$form = $this->createForm(new GearType(), new Gear(), array(
'validation_groups' => array('not-validating')
));
$form->bind($request);
}
The form will validate only against validations associated with the not-validating group, which will avoid showing the common required alerts if the form is built form GET.
Docs about 'validations-groups': http://symfony.com/doc/current/book/forms.html#validation-groups
The question is similar to: Entity form field and validation in Symfony2?