Form symfony multiple choice - forms

I have a classic form in Symfony and there is one input with 4 different values, I would like to render those 4 values in my twig instead of a drop-down box.
I tried to do it with a loop but I can't do it for an array.
Here is my code :
$isAdmin = !empty($this->getSap()->fetchSapQuery('RECAP_AUTHORITY_ADM', []));
$flux = $this->getSap()->fetchSapQuery('RECAP_FLUX_LIST', []);
$form = $this->createFormBuilder()
->add('Secteur', ChoiceType::class, array(
'choices' => $this->getSap()->fetchSapQuery('RECAP_SECTEUR_LIST', []),
'label' => $this->trans('Secteur'),
'choice_label' => 'LibLong',
'choice_value' => 'SecteurId',
'required' => true,
'placeholder' => '',
'attr' => [
//'data-app-auto-select-unique-option' => 1,
'multiple' => 'multiple', // secteur monoselection
],
))
Here is my Twig :
{% block body %}
<div class="row">
<div class="col-sm-12">
<div class="card-box ">
<h2 class="m-t-0 header-title">{{ pageTitle }}</h2>
<form id="searchForm" class="form-horizontal search-form " role="form" action="{{
path('api_post', {id: odata_search}) }}" method="post">
{#{{ dump(form.Secteur) }}#}
{{ form_row(form.Secteur) }}
{{ form_row(form.Flux) }}
{{ form_row(form.Canal) }}
{{ form_row(form.Periode) }}
{{ form_row(form.CodeExterne) }}
{{ form_row(form.EQART) }}
{{ form_row(form.Division) }}
{{ form_row(form.Username) }}
<div class="form-group">
<div class="col-md-offset-3 col-md-4">
<button id="search" type="submit" class="btn btn-success" data-notification-
clear="true">{{ "Rechercher"|trans }}</button>
{% if isAdmin %}
<button id="btnNew" type="button" class="btn btn-success" disabled><i
class="fa fa-plus-circle"></i> {{ "Creer un rapport vierge"|trans }}
</button>
{% endif %}
</div>

Related

How to add wrapper around multiple checkbox on Symfony 4 form

I have a list of checkbox created in a form with the following
->add('ISPreNbStudents', ChoiceType::class, [
'multiple' => false,
'choices' => [
'1' => 1,
'2' => 2,
'3' => 3,
'4' => 4,
'5' => 5,
],
'expanded' => true
])
I can then display this in the twig file with this : {{ form_widget(form.ISOptMonths) }}
The problem is that it now display a list of label and input like this
<input type="radio" id="availability_ISPreNbStudents_0" name="availability[ISPreNbStudents]" value="1">
<label for="availability_ISPreNbStudents_0">1</label>
<input type="radio" id="availability_ISPreNbStudents_1" name="availability[ISPreNbStudents]" value="2">
<label for="availability_ISPreNbStudents_1">2</label>
I need to put wrapper around each label/input like this
<div class="styled-input-single">
<input type="checkbox" name="case-1" id="1" />
<label for="1">1</label>
</div>
<div class="styled-input-single">
<input type="checkbox" name="case-2" id="2" />
<label for="2">2</label>
</div>
How can I achieve this ?
I've finally found the solution :
Here is how you print your list :
{{ form_row(registrationForm.firstRecourse) }}
Now is you want to add wrapper around every option :
<div class="row">
{{ form_label(registrationForm.ISPreNbStudents) }}
{% for checkbox in registrationForm.ISPreNbStudents.children %}
<div class="styled-input-single">
{{ form_widget(checkbox) }}
{{ form_label(checkbox) }}
{{ form_errors(checkbox) }}
</div>
{% endfor %}
</div>
You have to use Symfony Customize Form Rendering.
This way you can access your form field by field, like:
{{ form_start(form) }}
<div class="my-custom-class-for-errors">
{{ form_errors(form) }}
</div>
<div class="row">
<div class="col">
{{ form_row(form.task) }}
</div>
<div class="col" id="some-custom-id">
{{ form_row(form.dueDate) }}
</div>
</div>
{{ form_end(form) }}
You are able to render the fields manually also:
<div class="styled-input-single">
{{ form_widget(form.name_of_form_element) }}
{{ form_label(form.name_of_form_element) }}
<!-- and for errors of this field -->
{{ form_errors(form.name_of_form_element) }}
</div>
For rendering a select or option box manually check this link:
https://symfonycasts.com

Symfony 2 csrf validation token

i want to delete an advert without going to /delete/{id} path and just deleting with a modal pop-in.
In the dev environment, no problem , my form validation works great like that :
form.html.twig :
<div class="well">
{# form_start(form, {'attr': {'class': ''}}) #}
<form name="monext_perfclientbundle_advert" method="post" action="" class="" enctype="multipart/form-data">
{# Les erreurs générales du formulaire. #}
{{ form_errors(form) }}
<div class="form-group">
{# Génération du label. #}
{{ form_label(form.title, "Titre de l'annonce", {'label_attr': {'class': 'col-sm-3 control-label'}}) }}
{# Affichage des erreurs pour ce champ précis. #}
{{ form_errors(form.title) }}
<div class="col-sm-4">
{# Génération de l'input. #}
{{ form_widget(form.title, {'attr': {'class': 'form-control'}}) }}
</div>
</div>
<div class="form-group">
{{ form_label(form.content, "Contenu de l'annonce", {'label_attr': {'class': 'col-sm-3 control-label'}}) }}
{{ form_errors(form.content) }}
<div class="col-sm-4">
{{ form_widget(form.content, {'attr': {'class': 'ckeditor'}}) }}
</div>
</div>
{{ form_row(form.category) }}
<button id="addFile">Ajouter un fichier</button>
<div id="uploads"></div><br>
{{ form_widget(form.save, {'attr': {'class': 'btn btn-primary'}}) }}
{{ form_rest(form) }}
{{ form_end(form) }}
</form>
info.html.twig:
<div class="modal-footer parente">
<div>Annuler</div>
<div>
<form class="nomargin" action="{{ path('info_delete', {'id': advert.id}) }}" method="post">
<input type="hidden" name="_csrf_token" value="{{ csrf_token('authenticate') }}">
<input type="submit" value="Supprimer" class="btn btn-danger"/>{{ form_rest(form) }}
</form>
</div>
</div>
deleteAction() :
$form = $this->createFormBuilder()->getForm();
if ($form->handleRequest($request)->isValid()) {
$em->remove($advert);
$em->flush();
$request->getSession()->getFlashBag()->add('info', "L'annonce a bien été supprimée.");
return $this->redirect($this->generateUrl('info'));
}
return $this->render('MonextPerfclientBundle:Default:delete.html.twig', array(
'advert' => $advert,
'form' => $form->createView()
));
It doesnt work in production environment i dont know why..
my dump :
The CSRF token is invalid. Please try to resubmit the form
i got it thanks to Luax my friend !
{{ form_start(form, {'action': path('info_delete', {'id': advert.id})}) }}
<input type="hidden" name="form[_token]" value="{{ csrf_token('form') }}">
<input type="submit" value="Supprimer" class="btn btn-danger"/>{{ form_rest(form) }}
{{ form_end(form) }}
Issue resolved :)

Anchor error on form - Symfony3/Materialize

Using Symfony 3.2 && PhpStorm.2016.3.2 on Ubuntu 16.04
I have a problem that I partially resolved on the phone number field in this twig form.
I use materialize as the framework css for this project and I use the form of this framework.
I would like to be able to anchor the view directly on the form after an error has been spotted. So far it works great with the phone number section but anyone else doesn't work. I tried to tweak my code as much as I can but still I cannot anchor my view on other fields that get an error. It is pretty much frustrating.
here is the form twig in the view
<form class="col m12" method="POST">
{{ form_start(form) }}
<div class="row" id="radio-button-block">
<div class="col m12">
{{ form_row(form.baptismChoice) }}
</div>
</div>
<div class="row">
<div class="input-field col s6 validate" id="last_name" type="text">
{% if form.vars.errors|length %}
{{ form_row(
form.lastName,
form.lastName.vars|merge({'attr': {'autofocus': null}})
)
}}
{% else %}
{{ form_row(form.lastName) }}
{% endif %}
</div>
<div class="input-field col s6 validate" id="first_name" type="text">
{% if form.vars.errors|length %}
{{ form_row(
form.firstName,
form.firstName.vars|merge({'attr': {'autofocus': null}})
)
}}
{% else %}
{{ form_row(form.firstName) }}
{% endif %}
</div>
</div>
<div class="row">
<div class="input-field col s12 validate" id="email" type="email">
{{ form_row(form.authorEmail) }}
</div>
</div>
<div class="row">
<div class="input-field col s12 validate" id="icon_telephone" type="tel">
{{ form_errors(form) }}
{% if form.vars.errors|length %}
{{ form_row(
form.authorPhone,
form.authorPhone.vars|merge({'attr': {'autofocus': null}})
)
}}
{% else %}
{{ form_row(form.authorPhone) }}
{% endif %}
</div>
</div>
<div class="row">
<div class="input-field col s12 validate" id="city" type="text">
{{ form_row(form.city) }}
</div>
</div>
<div class="row" id="comment-block">
<div class="input-field col s12">
<div id="textarea">
{{ form_row(form.comment) }}
</div>
</div>
</div>
<div class="row">
<div class="input-field col s12 center-align">
{{ form_row(form.submit) }}
</div>
</div>
{{ form_end(form) }}
</form>
and here the Form builder in FomrType.php
->add('firstName', TextType::class, array(
'label' => 'Prénom',
'required' => true,
'attr' => array(
'class' => 'validate',
'id' => 'first_name',
),
'constraints' => array(new Regex(
array(
'pattern' => "#^[a-zéèàêâùïüë' -]{2,}$#i",
'message' => 'Oops ! Ce champ n\'est pas bon.'
)
)),
))
->add('lastName', TextType::class, array('constraints' => new Regex("#^[a-zéèàêâùïüë' -]{2,}$#i"),
'label' => 'Nom',
'required' => true,
'attr' => array(
'class' => 'validate',
'id' => 'last_name',
),
))
->add('authorEmail', EmailType::class, array('label' => 'Adresse mail',
'required' => true,
'attr' => array(
'class' => 'validate',
'if' => 'email'
),
))
->add('authorPhone', TextType::class, array(
'label' => 'Numéro de téléphone',
'required' => true,
'error_bubbling' => true,
'attr' => array(
'class' => 'validate',
'id' => 'icon_telephone'
),
'constraints' => array(new Regex(
array(
'pattern' => '#^0[1-9]([-. ]?[0-9]{2}){4}$#',
'message' => 'Oops ! Ce champ n\'est pas bon.'
)
)),
))
does anyone have an idea?
You're checking if the whole form has the errors set {% if form.vars.errors|length %}, but you should check if the specific field has an error {% if form.field_name.vars.errors|length %}.
In your present code, when an error occurs on any one of the fields, then autofocus is set on all of your fields and browser sets and scrolls to the first one.

This form should not contain extra fields. on multile submite button

I have a two part form in symfony2 of a single table db.
On my edit page i display whole form with two save buttons.
when i edit some field and try to save it that shows the extra field error
my controller code is
$form = $this->createForm(new PatientfullType(), $test, array());
$formadd = $this->createForm(new PatientfulladdType(), $test, array());
.
.
if ($form->get('save')->isClicked()) {
return $this->render('AdminBundle:FormsController:patientformfull.html.twig', array('formadd' => $formadd->createView(),'form' => $form->createView() ));
// probably redirect to the add page again
}
else
{
return $this->render('AdminBundle:FormsController:patientformfull.html.twig', array('formadd' => $formadd->createView(),'form' => $form->createView() ));
}
my twig file has two object
form and formadd
<div class="row" style="margin-bottom: 15px;">
<div class="col-xs-3">
{{ form_label(form.sex) }}
</div>
<div class="col-xs-9 col-sm-4 col-md-3">
{{ form_widget(form.sex, {'attr': {'class': 'form-control'}}) }}
</div>
</div>
<div class="row" style="margin-bottom: 15px;">
<div class="col-xs-3">
{{ form_label(formadd.ChronicDiseases) }}
</div>
<div class="col-xs-9 col-sm-4 col-md-3">
{{ form_widget(formadd.ChronicDiseases, {'attr': {'class': 'form-control'}}) }}
</div>
</div>
</div><div class="col-xs-9 col-sm-5">
{{ form_widget(form.save , {'attr': {'class': 'btn btn-success'}}) }}
{{ form_widget(form.cancel , {'attr': {'onclick': 'cancelRedirect()','class': 'btn btn-default'}}) }}
</div>
</div><div class="col-xs-9 col-sm-5">
{{ form_widget(formadd.save1 , {'attr': {'class': 'btn btn-success'}}) }}
</div>

Symfony2 override collection_widget twice in the same template

I have a contacts form with two collections: functions and comments.
The comments collection is very standard and doesn't require any tweaks (just text field and file upload).
But the functions requires to override collection-item because I render each function in a tab.
My issue is that when I customize collection_item widget for the functions, it also tries to apply to the prototype of the comments collection. It fails as the fields are obviously different.
How can I manage that by for example forcing the comments collection to use the standard collection_item widget? Or maybe by passing a variable to the collection_widget block so that it behaves differently depending if it applies to functions or comments...
--EDIT: add twig templates--
Here is my main edit form :
{% extends 'contactsBundle:templates:edit.html.twig' %}
{% form_theme edit_form 'contactsBundle:forms:fonctions.html.twig' %}
{% block form_body -%}
<div class="row">
<div class="col-md-5">
{{ form_row(edit_form.prefix) }}
{{ form_row(edit_form.nom) }}
{{ form_row(edit_form.prenom) }}
</div>
<div class="col-md-5 col-md-offset-2">
{{ form_row(edit_form.referent) }}
{{ form_row(edit_form.groupe) }}
</div>
</div>
{{ form_label(edit_form.fonctions) }}
{{ form_errors(edit_form.fonctions, {attr: {class: 'fonctions-label'}}) }}
{{ form_widget(edit_form.fonctions, {attr: {class: 'fonctions-widget'}}) }}
<ul class="nav nav-tabs">
</ul>
<div class="tab-content">
{% for fonction in edit_form.fonctions %}
{% include 'contactsBundle:forms:fonctions-prototype.html.twig' with {form: fonction.vars.form, index: loop.index} %}
{% endfor %}
</div>
<div class="row">
<div id="commentaire_prototype">
data-prototype="{{ form_widget(edit_form.commentaires.vars.prototype) | e }}"
</div>
{% for commentaire in edit_form.commentaires %}
{{ form_row(commentaires.commentaire) }}
{{ form_row(commentaires.docFile) }}
{% endfor %}
</div>
{% endblock form_body %}
{% block body_end_before_js %}
{{ parent() }}
<script src="{{ asset('bundles/mopabootstrap/js/mopabootstrap-collection.js') }}"></script>
<script src="{{ asset('bundles/contacts/js/forms.js') }}"></script>
{% endblock body_end_before_js %}
Here is my customized collection_widget
{% extends 'contactsBundle:templates:edit.html.twig' %}
{% form_theme edit_form 'contactsBundle:forms:fonctions.html.twig' %}
{% block form_body -%}
<div class="row">
<div class="col-md-5">
{{ form_row(edit_form.prefix) }}
{{ form_row(edit_form.nom) }}
{{ form_row(edit_form.prenom) }}
</div>
<div class="col-md-5 col-md-offset-2">
{{ form_row(edit_form.referent) }}
{{ form_row(edit_form.groupe) }}
</div>
</div>
{{ form_label(edit_form.fonctions) }}
{{ form_errors(edit_form.fonctions, {attr: {class: 'fonctions-label'}}) }}
{{ form_widget(edit_form.fonctions, {attr: {class: 'fonctions-widget'}}) }}
<ul class="nav nav-tabs">
</ul>
<div class="tab-content">
{% for fonction in edit_form.fonctions %}
{% include 'contactsBundle:forms:fonctions-prototype.html.twig' with {form: fonction.vars.form, index: loop.index} %}
{% endfor %}
</div>
<div class="row">
<div id="commentaire_prototype">
{#data-prototype="{{ form_widget(edit_form.commentaires.vars.prototype) | e }}"#}
</div>
{#{{ form_row(edit_form.commentaires) }}#}
{% for commentaire in edit_form.commentaires %}
{{ form_row(commentaires.commentaire) }}
{{ form_row(commentaires.docFile) }}
{% endfor %}
</div>
{% endblock form_body %}
{% block body_end_before_js %}
{{ parent() }}
<script src="{{ asset('bundles/mopabootstrap/js/mopabootstrap-collection.js') }}"></script>
<script src="{{ asset('bundles/contacts/js/forms.js') }}"></script>
{% endblock body_end_before_js %}
And here is my fonctions.prototype.html.twig:
{% if index is not defined %}
{% set index = "__name__" %}
{% endif %}
<div class="collection-item tab-pane" id="{{ index }}">
<div class="row">
<div class="col-md-5">
{{ form_row(form.fonction, {'attr': {'class': 'fonction'} } ) }}
{{ form_row(form.titre) }}
{{ form_row(form.entite, {'attr': {'class': 'entite'} }) }}
{#Todo Add entity address#}
</div>
<div class="col-md-5 col-md-offset-2">
{% if index is defined %}
<div class="form-group">
<a class="btn btn-xs btn-danger coll-del" href="#">
<span class="glyphicon glyphicon-trash"></span>
Supprimer cette Fonction
</a>
</div>
{% else %}
<div class="form-group">
<a class="btn btn-xs btn-danger coll-del" data-collection-remove-btn=".curuba_contactsbundle_contacts_fonction___name___form_group" href="#">
<span class="glyphicon glyphicon-trash"></span>
Supprimer cette Fonction
</a>
</div>
{% endif %}
</div>
</div>
<div class="row">
<div class="col-md-5">
<h4>Contact Professionel</h4>
{{ form_row(form.persoFixe) }}
{{ form_row(form.persoMobile) }}
{{ form_row(form.persoFax) }}
{{ form_row(form.persoMail) }}
</div>
<div class="col-md-5 col-md-offset-2">
<h4>Secrétariat</h4>
{{ form_row(form.assFixe) }}
{{ form_row(form.assMobile) }}
{{ form_row(form.assFax) }}
{{ form_row(form.assMail) }}
</div>
</div>
</div>
Source: http://symfony.com/doc/current/cookbook/form/create_custom_field_type.html
I would go on about this as follows:
Create two form types (Bundle\Form\Type\FunctionType and Bundle\Form\Type\CommentType)
Note down the return value of getName() for each and register your new form types as services, for the following I assume curuba_comment and curuba_function:
#services.yml
services:
bundle.form.type.function:
class: Bundle\Form\Type\FunctionType
tags:
- { name: form.type, alias: curuba_function }
bundle.form.type.comment:
class: Bundle\Form\Type\CommentType
tags:
- { name: form.type, alias: curuba_comment }
Now in your form theme you can add the blocks curuba_comment_widget and curubu_function_widget in which you can call other blocks, e.g. collection_widget.
Then in your Forms Form type do ->add('somefield', 'comment')
Here is how I managed it in a simple way thanks to this post:
symfony2 multiple nested forms prototype
First I change the prototype_name for each collection :
$builder
->add('prefix')
->add('nom')
->add('prenom')
->add('referent')
->add('groupe')
->add('fonctions', 'collection', array(
'type' => new fonctionsType(),
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false,
'label' => 'Fonctions',
'label_attr' => array('class'=>'sub-form-label'),
'prototype_name'=> '__fon_prot__'
))
->add('commentaires', 'collection', array(
'type' => new commentairesType(),
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false,
'label' => 'Commentaires',
'prototype' => true,
'prototype_name'=> '__com_prot__'
))
->add('submit', 'submit', array('label' => 'Enregistrer', 'attr' => array('class' => 'btn btn-lg btn-success')))
Then I add an if statment in my collection_widget :
{% if prototype.vars.name == '__fon_prot__' %}
{% set prototype_markup = include('contactsBundle:forms:fonctions-prototype.html.twig', { 'form': form.vars.form.vars.prototype }) %}
{% else %}
{% set prototype_markup = form_row(prototype) %}
{% endif %}
And that's it.