I need a simple form for the registration of a user. The form validation works as expected, except for the fact that the validation errors coming from a callback-constraint are shown twice for some reason.
I inserted a random number in an error message to see if the callback was called once or twice. The 2 error messages show the same random number, so the calback is (probably) only called once.
Any help is greatly appreciated!
Extra question: "All the errors from the callback function are shown on the same place in the form (as expected) . Is it ( easily ) possible to specify the field at which a validation error should be dislayed? "
The form type class looks as follows :
class RegisterType extends AbstractType{
static private $em;
static private $translator;
public function __construct( EntityManager $em , TranslatorInterface $translator){
RegisterType::$em = $em;
RegisterType::$translator = $translator;
}
public function buildForm(FormBuilderInterface $builder , array $options){
$builder
->add('email','email',[
'attr'=>['placeholder'=>RegisterType::$translator->trans('your.email')],
'label'=>false ,
'constraints'=>[new NotBlank() , new Assert\Email()]])
->add('password','password',[
'attr'=>['placeholder'=>RegisterType::$translator->trans('your.password')],
'label'=>false ,
'constraints'=>[new NotBlank()]])
->add('confirmPassword','password',[
'attr'=>['placeholder'=>RegisterType::$translator->trans('confirm.password')],
'label'=>false ,
'constraints'=>[new NotBlank()]]);
}
public function getName(){
return 'register';
}
public function configureOptions( OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'constraints' =>array( new Assert\Callback( array('AppBundle\Forms\Type\RegisterType','validateForm' ) )),
));
}
static public function validateForm(array $data, ExecutionContextInterface $context){
if($data['email']){
$user = RegisterType::$em->getRepository('AppBundle:User')->findByEmail($data['email']);
if($user){
$context->buildViolation('email.is.used'.rand())->addViolation();
}
}
if($data['password'] && $data['confirmPassword']){
if($data['password'] != $data['confirmPassword']){
$context->buildViolation('password.confirm.failed')->addViolation();
}
}
}
The form is called in a controller as follows:
public function registerFormAction(Request $request){
$form = $this->createForm('register');
$form->handleRequest($request);
if($form->isValid()){
//TODO
}
return new JsonResponse(['success'=>false,
'message'=>$this->renderView('AppBundle:Security:register.html.twig' , ['form'=>$form->createView()])
]);
}
The twig template used to render the form is :
{% form_theme form _self %}
{%- block form_row -%}
<div class = "form-group">
{{- form_errors(form) -}}
{{- form_widget(form) -}}
</div>
{%- endblock form_row -%}
{{ form(form) }}
You are displaying the form errors and form widgets twice!
You have
<div class = "form-group">
{{- form_errors(form) -}} //displays all form errors
{{- form_widget(form) -}} //displays all form widgets (not the labels!)
</div>
and
{{ form(form) }} //displays the whole form
in your code.
Why don't you just render the form by yourself, then you can also set where the form errors should be shown.
{{ form_start(form) }}
<div class="form-group">
{{ form_errors(form.username) }} //errors for the "username" field of your form
{{ form_label(form.username) }} //labels for the "username" field of your form
{{ form_widget(form.username) }} //formfields for the "username" field of your form
</div>
{{ form_end(form) }}
Also, there's a twig function called form_rest(form) which will display all form fields that aren't manuelly rendered yet!
It turned out that the {% form_theme form _self %} functionality can only be used if your template extends another template.
So i put the form_row block in another file and i used
{% form_theme form "otherfile.htmltwig"%}
Which worked fine.
Related
Trying to make a simple form but i keep getting "Variable "subscriptionForm" does not exist."
enter image description here
Here is Controller method:
public function subscriptionForm(Request $request): \Symfony\Component\HttpFoundation\RedirectResponse|array
{
$subscriptionForm = $this
->getFactory()
->createSubscriptionForm()
->handleRequest($request);
if ($subscriptionForm->isSubmitted() && $subscriptionForm->isValid()) {
// Call the client for e.g. to save the subscriber.
// Redirect to home page after successful subscription
return $this->redirectResponseInternal('home');
}
return $this->viewResponse([
'subscriptionForm' => $subscriptionForm->createView(),
]);
}
Twig file:
{% block body %}
{{ form_start(subscriptionForm) }}
{{ form_widget(subscriptionForm.email) }}
{{ form_errors(subscriptionForm.email) }}
<input type="submit" value="Subscribe" />
{{ form_end(subscriptionForm) }}
{% endblock %}
Spryker assigns all twig variables within a _view object. Try adding these code on top of the twig file:
{% define data = {
subscriptionForm: _view.subscriptionForm
} %}
So I have this simple form:
class CreditType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('credits', EntityType::class, [
'class' => Product::class,
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('product')
->orderBy('product.amount');
},
'expanded' => true,
'choice_label' => function ($key) {
return $key->amount.' credits voor maar '.$key->price;
}
]);
}
}
which I use in the following Controller:
public function credits(Request $request)
{
$form = $this->createForm(CreditType::class);
$form->handleRequest($request);
if ($form->isSubmitted()) {
throw new \Exception('It works!');
}
return $this->render(
'credits/credits_widget.html.twig',
[
'form' => $form->createView(),
]
);
}
And use this in my XXX.html.twig
{{ render(controller('App\\Controller\\DetailsController:credits')) }}
I have been working with symfony for 4 weeks now so not that long.
I want to call some functions when the form is submitted only nothing that I place in the if statement is working, it doesn't throw any exception at the moment. Am I not seeing something or is it not possible to do this when I render a form?
EDIT:
my credits_widget.html.twig
{% block credits %}
{{ form_start(form.credits) }}
{{ form_widget(form.credits) }}
{% if app.user %}
<button type="submit">Koop nu!</button>
{% else %}
<button type="button">Login en koop</button>
<button type="button">Registreer en koop</button>
{% endif %}
{{ form_end(form.credits) }}
{% endblock %}
The problem is that the form is rendered without action and when submitted, it is submitted by default to the root route from which it was rendered. So the credits action is not being called. You should change the action of the form like this:
public function credits(Request $request)
{
// set current url path as form's action
$form = $this->createForm(CreditType::class, null, , [
'action' => $request->getRequestUri()
]);
$form->handleRequest($request);
if ($form->isSubmitted()) {
throw new \Exception('It works!');
}
return $this->render(
'credits/credits_widget.html.twig',
[
'form' => $form->createView(),
]
);
}
EDIT: You are currently rendering subform form.credits when you should really render form as it is passed from your controller. Your credits_widget.html.twig template should look like this:
{% block credits %}
{{ form_start(form) }}
{{ form_widget(form) }}
{% if app.user %}
<button type="submit">Koop nu!</button>
{% else %}
<button type="button">Login en koop</button>
<button type="button">Registreer en koop</button>
{% endif %}
{{ form_end(form) }}
{% endblock %}
I wanted to replace Symfony's default radio button widget with Bootstrap's Radio Button Group. I was trying to achieve that by setting a custom theme for an individual field in Symfony's form created with createNamedBuilder function. I failed, because custom field themes require the ID of the field which is dynamic in my case. I know that having variables in theme names is impossible in TWIG, but maybe there is an alternative approach that I could use to resolve my issue.
CODE
I have a Controller which creates multiple instances of one form type in a loop. The name of the form is dynamically created by concatenating requestform_ and the ID of the form:
public function listAction(Request $request)
{
$entityManager = $this->getDoctrine()->getManager();
$requestForms = $entityManager->getRepository('AppBundle:RequestForm')->findBy(array(), array('id' => 'ASC'));
$forms = array();
foreach ($requestForms as $requestForm) {
$formBuilder = $this->get('form.factory')->createNamedBuilder('requestform_'.$requestForm->getId(), RequestFormType::class, $requestForm);
$form = $formBuilder->getForm()->handleRequest($request);
$forms[] = $form->createView();
}
return $this->render('form/index.html.twig', array('forms' => $forms));
}
buildForm function of the RequestFormType prepares radio button widget and looks like this:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('status', EntityType::class, array(
'class' => 'AppBundle:Status',
'choice_label' => 'name',
'expanded' => true,
'multiple' => false
));
}
The Controller renders the following template:
{% block content %}
{% for form in forms %}
{{ form_start(form) }}
{{ form_widget(form.status) }}
{{ form_end(form) }}
{% endfor %}
{% endblock %}
Which is themed to show Bootstrap's button group selector instead of radio buttons:
{% block _requestform_1_status_widget %}
<div id={{ form.vars.id }} class="btn-group" data-toggle="buttons">
{% for status in form.children %}
<label for={{ status.vars.id }} class="btn btn-primary required custom-button-radio">
<input type="radio" id={{ status.vars.id }} name={{ status.vars.full_name }} required="required" value={{ status.vars.value }}>
{{ status.vars.label }}
</label>
{% endfor %}
</div>
{% endblock _requestform_1_status_widget %}
As you may have noticed this will work only for requestForm ID 1 and will not work for other ID values due to TWIG not allowing variables in block names, i.e. {% block _requestform_VARIABLE_status_widget %}.
Any help much appreciated! Thanks
My gut feeling is that you might want to have only one form that includes a CollectionType field with entry_type = RequestFormType::class, and then in the twig, apply a form to the "outer" form as a whole. You should still be able to manage things the way you wanted.
Hello I want to create form and inserting record to mysql table on clicking on submit button in Symfony.
I am new in symfony. I have created form but it didn't response on submit. Here is my code
DefaultController.php
<?php
namespace Sym\FormBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sym\FormBundle\Entity\TblCust;
use Symfony\Component\HttpFoundation\Request;
use Sym\FormBundle\Form\TblCustType;
class DefaultController extends Controller
{
public function indexAction(Request $request)
{
$tbl = new TblCust();
// $form = $this->createForm(new TblCustType(),$tbl);
$form = $this->createFormBuilder($tbl)
->add('custName','text')
->add('custCity','text')
->add('custAddress','text')
->add('custPhno','text')
->add('save','submit')
->getForm();
$form->handleRequest($request);
if($form->isValid()){
$em = $this->getDoctrine()->getManager();
$em = persist('$tbl');
$em->flush();
return new response('New Customer Added..!');
}
$build['form']=$form->createView();
return $this->render('FormBundle:Default:index.html.twig',array( 'form' => $form->createView(),));
}
}
routing.yml
form_homepage:
pattern: /Form
defaults: { _controller: FormBundle:Default:index }
index.html.twig
{% block gender_widget %}
{% spaceless %}
{% if form %}
<ul {{ block('widget_container_attributes') }}>
{% for child in form %}
<li>
{{ form_widget(child) }}
{{ form_label(child) }}
</li>
{% endfor %}
</ul>
{% else %}
{# just let the choice widget render the select tag #}
{{ block('choice_widget') }}
{% endif %}
{% endspaceless %}
{% endblock %}
{% block container %}
{% endblock%}
I have created form using doctrine:generate:bundle symfony commend
TblCustType.php
<?php
namespace Sym\FormBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class TblCustType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('custName')
->add('custCity')
->add('custAddress')
->add('custPhno')
;
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Sym\FormBundle\Entity\TblCust'
));
}
/**
* #return string
*/
public function getName()
{
return 'sym_formbundle_tblcust';
}
}
My output is looking like below image
My problem is when i click on submit button it doesn't return response and i can not save my record. Please help my out this.
I believe you need to put the form tag in twig:
{{ form_start(form) }}
//all form widgets
//submit button
{{ form_rest(edit_form) }} <-- so it renders the hidden ones like the csrf token
</form>
You need to use form_start, form_rest and form_end like that :
{% block gender_widget %}
{% spaceless %}
{% if form %}
{{ form_start(form) }}
<ul {{ block('widget_container_attributes') }}>
{% for child in form %}
<li>
{{ form_widget(child) }}
{{ form_label(child) }}
</li>
{% endfor %}
</ul>
{{ form_rest(form) }}
{{ form_end(form) }}
{% else %}
{# just let the choice widget render the select tag #}
{{ block('choice_widget') }}
{% endif %}
{% endspaceless %}
{% endblock %}
I want to validate my form using server side validation only. However, if the browser supports HTML5 it validates using the HTML5 attributes added to the form by symfony2 so I need to prevent HTML5 validation.
Just add novalidate to your <form> tag:
<form novalidate>
If you are rendering the form in TWIG, you can use following.
{{ form(form, {'attr': {'novalidate': 'novalidate'}}) }}
I know its old question but with SF2.6 in FormType, you can do:
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'attr'=>array('novalidate'=>'novalidate')
));
}
While googling for a solution to this I found one, that seems the most elegant if you want to disable html5 validation in your whole app, so I thought i'd share it here. Credits go to the author of this blog article.
The idea is to create an extension for the "form" form type like this:
<?php
// src/AppBundle/Form/Extension/NoValidateExtension.php
namespace AppBundle\Form\Extension;
use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormView;
class NoValidateExtension extends AbstractTypeExtension
{
public function buildView(FormView $view, FormInterface $form, array $options)
{
$view->vars['attr'] = array_merge($view->vars['attr'], [
'novalidate' => 'novalidate',
]);
}
public function getExtendedType()
{
return 'form';
}
}
?>
Then you just register it in your services.yml like this:
app.no_validation_form_extension:
class: AppBundle\Form\Extension\NoValidateExtension
tags:
- {name: form.type_extension, alias: form}
and you're done. All your forms automatically have a novalidate attribute now.
Symfony 3.3
As of Symfony 3.3 the configuration is slightly different, but still possible.
Slight update to the getExtendedType method to return the FormType class.
// src/AppBundle/Form/Extension/NoValidateExtension.php
namespace AppBundle\Form\Extension;
use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormView;
use Symfony\Component\Form\Extension\Core\Type\FormType;
class NoValidateExtension extends AbstractTypeExtension
{
public function buildView(FormView $view, FormInterface $form, array $options)
{
$view->vars['attr'] = array_merge($view->vars['attr'], [
'novalidate' => 'novalidate',
]);
}
public function getExtendedType()
{
return FormType::class;
}
}
Plus some a minor addition of the extended_type tag, which is now required in your service declaration:
app.no_validation_form_extension:
class: AppBundle\Form\Extension\NoValidateExtension
tags:
- {name: form.type_extension, alias: form, extended_type: Symfony\Component\Form\Extension\Core\Type\FormType}
Alternatively if for some reason you don't want to do it in twig as in the answer above...
{{ form(form, {'attr': {'novalidate': 'novalidate'}}) }}
or you create your from manually with createFormBuilder then you could simply use createFormBuilder as a second parameter to define form attribute:
//someAction
$form = $this->createFormBuilder(null, ['attr'=>['novalidate'=>'novalidate']])
->add(...)
->add(...)
->add(...)
->getFrom();
return $this->render("-----:----:----.html.twig", [
'form'=>$form->createView()
]);
If you are using Symfony 3 (or 2) and want to turn off validation for a specific field only you can do this.
$form = $this->createFormBuilder($task)
->add('task', TextType::class, array('required' => false))
->add('dueDate', DateType::class)
->add('save', SubmitType::class, array('label' => 'Create Task'))
->add('saveAndAdd', SubmitType::class, array('label' => 'Save and Add'))
->getForm();
In this sample form notice the array('required' => false), you can add this to any element that you want to disable validation for without disabling validation for the others. Very useful if you want to temporarily disable only one element instead of the entire form.
Note this ONLY disables the HTML5 validation! This does not disable server side validation.
Reference: http://symfony.com/doc/current/book/forms.html#field-type-options
If you actually need to remove the validation attributes (if you are using a validation library want to keep all of your validation constraints in one place for example), you can overwrite the widget_attributes block in twig.
If you are already using custom form templates in app/Resources/views/form.html.twig for example (and have enabled it in your config.yml) you can just add a block for
{% block widget_attributes %}
{% spaceless %}
id="{{ id }}" name="{{ full_name }}"{% if read_only %} readonly="readonly"{% endif %}{% if disabled %} disabled="disabled"{% endif %}
{% for attrname, attrvalue in attr %}{% if attrname in ['placeholder', 'title'] %}{{ attrname }}="{{ attrvalue|trans({}, translation_domain) }}" {% else %}{{ attrname }}="{{ attrvalue }}" {% endif %}{% endfor %}
{% endspaceless %}
{% endblock widget_attributes %}
All I have done here is remove the attributes related to validation:
{% if required %} required="required"{% endif %}{% if max_length %} maxlength="{{ max_length }}"{% endif %}{% if pattern %} pattern="{{ pattern }}"{% endif %}
To disable Regex validation for specific field using formType class:
->add('foo',null,array=>('attr'=>('pattern'=>'/[^~,]/'))
Use form theming:
First create form theme template, e.g app/Resources/views/form/fields.html.twig:
{% extends 'form_div_layout.html.twig' %}{# or some other base layout #}
{% block form_start %}
{% if attr.novalidate is not defined %}
{% set attr = attr|merge({'novalidate':'novalidate'}) %}
{% endif %}
{{ parent() }}
{% endblock %}
Then use that form theme in your template:
{% form_theme form with 'form/fields.html.twig' %}
{{ form_start(form) }} <-- now renders with novalidate attribute
...
{{ form_end(form) }}
Or, apply theme globally (app/config/config.yml):
twig:
form_themes:
- ':form/fields.html.twig'