Can I change a Form Class action on build? - php

On a form classe's buildForm method (AbstractType derived) can I set the action of that form?
What I want to do is similar to the setAction method that I can use when building an embedded form:
$form = $this->createFormBuilder()
->setAction($this->generateUrl('my_action'))
->add('field', 'text')
->add('button', 'submit');
I mean, is the setAction an equivalent to form classes?

You get access to the same form builder in the buildForm method, so just calling the setAction on it will work:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->setAction($path);
}
The question is how you pass the $path to your form type. One of the ways would be to pass it as an option when creating the form. But if you're passing the $path anyway, why not just set the action itself?
$form = $this->createForm(new MyType(), $object, array(
'action' => $this->generateUrl('my_action'),
));
Another way would be to inject the router to the form type and use it to generate the URL, but I don't think it's a good idea to make that kind of decisions in a form type.

Related

Add custom form type only when user is not authorized

I have created a custom form type - RecaptchaType. I added it into many forms using $builder->add('recaptcha', RecaptchaType::class). For view I have created a macro, so in twig I render it as follows {{ forms.recaptcha(form.recaptcha) }}.
But now I want to display recaptcha only for users who are not logged in. In twig macro I can simple add if condition. But how can I achieve not adding (or removing) recaptcha input in form type if I want to edit only RecaptchaType and don't want to touch form types, which are using this.
I had two ideas, but neither of them did work.
In RecaptchaType use function buildForm and do $builder->remove('recaptcha') when condition is met (user is logged in)
Pass option 'render' to $resolver->setDefaults(), access it in form and depend on value do ->add or not. But I can't access the value.
How would you do that? I can add code example if needed.
TL;DR; To remove a form type conditionally encapsulating this logic within itself, you can do the follows:
RecaptchaType:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {
if ($this->isAuthenticated()) {
$form = $event->getForm();
$form->getParent()->remove($form->getName());
}
});
}
Now, if you are customizing the form theme make sure to check the existence of the field before render it:
{% if form.recaptcha is defined %}
{{ forms.recaptcha(form.recaptcha) }}
{% endif %}
It should work as long as your RecaptchaType is embedded into another form.

Symfony 3: http_method_override=true prevents form validation

Bug #23254 was fixed in Symfony 3.3.3. Its effect is to actually add the hidden _method to the form input fields when the original form method is not POST or GET. _method contains the original form method so that it can be recovered when the form is submitted.
But I have a subform containing a non POST method (setMethod('PUT') to be precise). Therefore, it adds that _method to its parameters and that _method overrides the POST method from the parent form.
As a form is validated only if the request method matches the form method, changing the request method fails to validate the parent form.
Here is a trimmed down sample code:
class PartnerType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->setMethod('POST')
->add('partnerRoles', CollectionType::class, array(
'entry_type' => PartnerRoleType::class,
));
}
}
class PartnerRoleType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->setMethod('PUT')
;
}
}
Since upgrading to Symfony 3.3.5, the PartnerType form isn't validated any more as $request->method() returns "PUT" due to the _method field added by ParterRoleType and the PartnerType form is expecting a "POST" method.
I fixed it by configuring http_method_override to false to keep the POST method but it breaks forms that really use PUT. With those forms, the method is replaced by POST in the web page but _method isn't processed to get back to the real PUT when the form is submitted.
I would expect the _method input field not to be added if the form is part of a parent form. Is this a Symfony bug or am I missing something?
This is actually a bug in Symfony. A child form should never add its hidden _method when its form_rest() is called.
Symfony starting with versions 2.7.30, 2.8.23, 3.2.10 and 3.3.3 are affected (see https://github.com/symfony/symfony/pull/23254).
A patch is being processed at the time of writing.

How to render chosen fields in form symfony/twig?

I try to render a few fields in my form, not all, but twig always render all fields. It is my twig code:
{{ form_start(form) }}
{{form_widget(form.subject)}}
{{form_widget(form.name)}}
{{form_widget(form.parent,{'value' : ''})}}
{{form_widget(form.save)}}
{{ form_end(form) }}
Set type for field you don't want visible to hidden. your form type would look something like this:
class ConfigurationFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('email', HiddenType::class)
// more fields
}
// other configurations
}
The form_end function renders the elements that have not been rendered yet. Read here: http://symfony.com/doc/current/reference/forms/twig_reference.html#form-end-view-variables.
This helper also outputs form_rest() unless you set render_rest to
false
You can disable that behaviour as you can see in the docs.
Anyway you should render all fields for validation to work, for example, and for security reasons (as it will add validation for CSRF automatically).

Symfony2 tutorial deprecated function

I am learning Symfony2 Framework, and to start things of I found this tutorial: Tutorial. Everything has been going well until I hit this issue:
in Tutorial Part2. In Creating the form in the controller section i discover that the getRequest() function is deprecated and bindRequest() is not found in class.
These two have held me back with the tutorial and learning progress. I there any other way of constructing this controller without using these functions or are there other functions that do exactly the same thing.
See this part of the Symfony Documentation. It shows that you should use handleRequest, like so:
// Top of page:
use Symfony\Component\HttpFoundation\Request;
...
// controller action
public function newAction(Request $request)
{
$form = $this->createFormBuilder()
// ...
->getForm();
$form->handleRequest($request);
if ($form->isValid()) {
// perform some action...
return $this->redirect($this->generateUrl('task_success'));
}
return $this->render('AcmeTaskBundle:Default:new.html.twig', array(
'form' => $form->createView(),
));
}
You may also find this link useful: Handling Form Submissions.
The Request as a Controller Argument
Getting the request object in this way might be a little confusing at first, to quote the documentation:
What if you need to read query parameters, grab a request header or
get access to an uploaded file? All of that information is stored in
Symfony's Request object. To get it in your controller, just add it as
an argument and type-hint it with the Request class:
use Symfony\Component\HttpFoundation\Request;
public function indexAction($firstName, $lastName, Request $request)
{
$page = $request->query->get('page', 1);
// ...
}
This information can be found on the Controller Documentation.

Multiple Profile forms with FOSUserBundle

I'm creating an application with Symfony 2 and the FOSUserBundle. I have my own UserBundle configured correctly. My User Entity has a lot of attributes of different categories (For example location settings, personal settings, ...) so I want to make different forms for each category so I can show each setting category on a different page.
I know how to make a custom profile form (it's in the FOSUserBundle Documentation), I already made multiple FormTypes and an additional controller but I just can't get it populated with the current user data. I tried the following
$user = $this->container->get('security.context')->getToken()->getUser();
// Try 1
$form = $this->createForm(new InnerCharacteristicsType());
// Try 2
$form = $this->createForm(new InnerCharacteristicsType($user));
// Try 3
$form = $this->createForm(new InnerCharacteristicsType(), $user);
// Try 4
$form = $this->createForm(new InnerCharacteristicsType(), array('user' => $user);
return $this->render('AcmeUserBundle:EditProfile:inner.html.twig', array('form' => $form->createView()));
(Of course, I didn't try them at the same time)
I also tried to make the new forms a service by adding my InnerCharacteristics Form to the services.yml but I couldn't find any documentation on this. The profile.xml in the FOSUserBundle itself also isn't very helpful because I couldn't find where the parameters are pointing at. Also the XML to YAML conversion isn't very easy since the documentation about this is not very helpful (this is needed because the FOSUserBundle configuration is in XML and my UserBundle configuration in YAML).
This is what i have in my services.yml now:
acme_user.inner_characteristics_profile.form:
class: Symfony\Component\Form\Form
factory_service: form.factory
factory_method: createNamed
I hope someone can give me some pointers where to go next.
This is the correct way to create the form:
$form = $this->createForm(new InnerCharacteristicsType(), $user);
Inside your form you should do something like this:
public function buildForm(FormBuilderInterface $builder, array $options)
{
// Add fields
$builder->add('name', 'text', array('label' => 'Name:'));
}
This should load up the name property from the user object inside your form. Is the form successfully loaded?

Categories