Symfony2 form theme override collection entry row with loop? - php

I have a problem with my form collection entries.
For example my form type.
// ProfileFormType
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name')
->add('emails', 'collection', array(
'type' => new EmailType(),
'allow_add' => true,
'allow_delete' => true
))
;
}
Thats the email type for the collection above.
// EmailType
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('email', 'email')
->add('publicAccess')
;
}
And now the form theme for that collection entry row.
{%- block _fos_user_profile_form_emails_entry_row %}
<div class="email form-group{% if form.email.vars.errors | length %} has-error{% endif %}">
<div class="input-group col-xs-3">
<span class="input-group-addon">{{ form_widget(form.publicAccess) }}</span>
{{ form_widget(form.email) }}
{#% if loop is defined %}{{ dump(loop) }}{% endif %#}
<span class="input-group-btn"><a class="btn btn-success"><span class="glyphicon glyphicon-plus glyphicon-xs"></span></a></span>
</div>
{{ form_errors(form.email) }}
</div>
{% endblock -%}
But the problem is that i have no loop or other things for the entries...
How can i check if the entry is the last entry of the collection?
The reason is that i want to add the "minus" button instead the "glypicon-plus" for all entries which not the last one.

You can access the last entry with :
{% if loop.last %}
{# your custom code for the last entry #}
{% else %}
{# the others entries #}
{% endif %}
So you can use it to choose the good button :
{%- block _fos_user_profile_form_emails_entry_row %}
<div class="email form-group{% if form.email.vars.errors | length %} has-error{% endif %}">
<div class="input-group col-xs-3">
<span class="input-group-addon">{{ form_widget(form.publicAccess) }}</span>
{{ form_widget(form.email) }}
<span class="input-group-btn"><a class="btn btn-success"><span class="glyphicon {{ (loop.last) ? 'glypicon-plus' : 'glyphicon-minus' }} glyphicon-xs"></span></a></span>
</div>
{{ form_errors(form.email) }}
</div>
{% endblock -%}

I think you should compare the entry with the parent's last element.
Like this:
{% if form == form.parent | last %}
it's the last
{% else %}
it's not the last
{% endif %}

Related

count key in twig symfony

i try to simply count object into a city
enter image description here
in paris, i have 5 object, i don't want show 11111 but just 5,
that my code
{% set newAnnounceCity = [] %}
{% for item in announceCity %}
{% if item.infoCity is not null %}
{% if item.infoCity.city not in newAnnounceCity %}
<span class=" font-semibold mr-2 text-left flex-auto">
<a href="{{path('app_city_show',{slug: item.infoCity.slug})}}">
<button class=" m-2 p-2 pl-5 pr-5 bg-transparent border-2 border-red-500 text-red-500 text-lg rounded-lg hover:bg-gradient-to-b hover:from-red-600 hover:to-pink-500 hover:text-gray-100 focus:border-4 focus:border-red-300">
{{ (item.infoCity.city) }}
{% for key in item.infoCity.announce|keys %}
{{key|length }}
{% endfor %}
{% set newAnnounceCity = newAnnounceCity|merge([item.infoCity.city]) %}
</button>
</a>
</span>
{% endif %}
{% else %}
{% endif %}
{% endfor %}
someone can help me
i try to count an keys object
It's a bit hard to guess without a sample structure of your input array, but maybe try replacing
{% for key in item.infoCity.announce|keys %}
{{key|length }}
{% endfor %}
with simply
{{ item.infoCity.announce|length }}

Symfony forms override field appearance

I'm trying to override field appearance.
I created my custom form type:
namespace AppBundle\Form\Type\Frontend\Search;
/**/
class TripTypeType extends AbstractType
{
/**
* {#inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'choices' => [
'One-way',
'Round-trip',
],
'multiple' => false,
'expanded' => true,
'required' => false,
'empty_value' => false,
]);
}
/**
* {#inheritdoc}
*/
public function getParent()
{
return ChoiceType::class;
}
}
After that i added this field to parent form:
namespace AppBundle\Form\Type\Frontend\Search;
/***/
class FlightSearchType extends AbstractType
{
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('tripType', TripTypeType::class)
->add('airports', AirportsType::class)
->add('flightAt', FlightDatesType::class)
->add('flightClass', FlightClassType::class);
}
}
And rendering this field like that:
{% for type in form.tripType %}
{% if loop.index == 1 %}
{% set way_id = 'one-way' %}
{% else %}
{% set way_id = 'round-trip' %}
{% endif %}
{{ form_widget(type, {
'id': way_id,
'attr': {'class': 'tabs-way__radio'},
'label_attr': {'class': 'tabs-way__label', 'for': way_id}
}) }}
{% endfor %}
So what i want to get is a:
<div class="tabs-way__item">
<input type="radio" id="one-way" name="flight_search[tripType]" class="tabs-way__radio" value="0">
<label class="tabs-way__label" for="one-way">One-way</label>
</div>
For that i'm trying to override widget for this field:
{# app/Resources/views/Form/fields.html.twig #}
{% block trip_type_widget %}
<div class="tabs-way__item">
<input type="radio" {{ block('widget_attributes') }}{% if value is defined %} value="{{ value }}"{% endif %}{% if checked %} checked="checked"{% endif %} />
{{ block('form_label') }}
</div>
{% endblock trip_type_widget %}
And i include that file in config.yml in twig section. But all i get it's nothing :(
If you need to override custom field, you have to prefix it with underscore, e.g.:
{% block _trip_type_widget %}
<div class="tabs-way__item">
{{- form_label(form) -}}
{{- form_widget(form) -}}
{{- form_errors(form) -}}
</div>
{% endblock _trip_type_widget %}

Partial User Registration on Home Page (index)

I am using Symfony2 2.6.4 with the FOS User Bundle (master) and I am having a hard time trying to pass a partial registration from the home page to the registration page.
The Goal
I want a user to start on the home page and fill out some info and press submit. I want the user to be passed to the registration page and pre fill the items that they have already filled out on the front page.
Things I have tried:
I am unsure what is feasable and have already spend too much time on this. I tried extending the registration controller but I am getting errors before I can even start on my issue so I stopped. My last thought was to pass the variables in a session to the overridden registration form type and see if I can pre-populate it there.
Has anyone done anything like this before? They sure don't make it easy.
Credit goes to #NawfalSerrar Thank you for the idea. I was close but not close enough!
So what I ended up doing was this.
This is the controller action that does that work.
/**
* #Route("/", name="Home")
*/
public function indexAction(Request $request)
{
$form = $this->createForm(new RegisterFormType());
$form->handleRequest($request);
if ($form->isValid()) {
$data = $form->getData();
$user = new User();
$session = $this->get('session');
$registration = array();
$registration['firstName'] = $data['first'];
$registration['lastName'] = $data['last'];
$registration['middleInit'] = $data['middle'];
$registration['email'] = $data['email'];
$session->set('registration', $registration);
return $this->redirect($this->generateUrl("fos_user_registration_register"));
}
return $this->render('Bundle:Page:index.html.twig', array(
'form' => $form->createView(),
));
}
Then the form on the front end.
{{ form_start(form,{'attr': {'class': 'reg-page'}}) }}
<div class="reg-header">
<h1>Get Started!</h1>
<p>Already Signed Up? Click Sign In to login
your account.</p>
</div>
{{ form_errors(form) }}
<div class="row">
<div class="col-sm-6">
{{ form_label(form.first) }}
{{ form_widget(form.first, {'attr': {'class': 'form-control margin-bottom-10'}}) }}
</div>
<div class="col-sm-6">
{{ form_label(form.middle) }}
{{ form_widget(form.middle, {'attr': {'class': 'form-control margin-bottom-10'}}) }}
</div>
<div class="col-sm-6">
{{ form_label(form.last) }}
{{ form_widget(form.last, {'attr': {'class': 'form-control margin-bottom-10'}}) }}
</div>
</div>
{{ form_label(form.email) }}
{{ form_widget(form.email, {'attr': {'class': 'form-control'}}) }}
<div class="row">
<div class="col-lg-6 text-right">
{{ form_widget(form.save, {'attr': {'class': 'btn-u'}}) }}
</div>
</div>
{{ form_end(form) }}
Registration page looks like this.
{% extends '::base.html.twig' %}
{% block body %}
{% set tokens = app.session.get('registration') %}
{% if tokens['firstName'] is defined %}
{% set firstName = tokens['firstName'] %}
{% endif %}
{% if tokens['middleInit'] is defined %}
{% set middleInit = tokens['middleInit'] %}
{% endif %}
{% if tokens['lastName'] is defined %}
{% set lastName = tokens['lastName'] %}
{% endif %}
{% if tokens['email'] is defined %}
{% set email = tokens['email'] %}
{% endif %}
{{ form_start(form) }}
{{ form_errors(form) }}
{{ form_label(form.firstName, 'First Name', {label_attr: {class: 'foo'}}) }}
{% if firstName is defined %}
{{ form_widget(form.firstName, { attr: {class: 'TestClass'}, value : firstName }) }}
{% else %}
{{ form_widget(form.firstName, { attr: {class: 'TestClass'} }) }}
{% endif %}
{{ form_label(form.middleInitial) }}
{% if middleInitial is defined %}
{{ form_widget(form.middleInitial, { attr: {class: 'TestClass'}, value : middleInit }) }}
{% else %}
{{ form_widget(form.middleInitial, { attr: {class: 'TestClass'}}) }}
{% endif %}
{{ form_label(form.lastName) }}
{% if lastName is defined %}
{{ form_widget(form.lastName, { attr: {class: 'TestClass'}, value : lastName }) }}
{% else %}
{{ form_widget(form.lastName, { attr: {class: 'TestClass'}}) }}
{% endif %}
{{ form_label(form.email) }}
{% if email is defined %}
{{ form_widget(form.email, { attr: {class: 'TestClass'}, value : email }) }}
{% else %}
{{ form_widget(form.email, { attr: {class: 'TestClass'}}) }}
{% endif %}
{{ form_label(form.plainPassword.first) }}
{{ form_widget(form.plainPassword.first) }}
{{ form_label(form.plainPassword.second) }}
{{ form_widget(form.plainPassword.second) }}
<div>
<input type="submit" value="{{ 'registration.submit'|trans }}"/>
</div>
{{ form_end(form) }}
{% endblock %}
Hope this helps!

Symfony2 Form Builder - Remove label, make it placeholder

I am playing with Symfony's form builder, and I can't find a way to not display a label. Further, I am interested in actually setting a placeholder for each input box. Is this possible? I have researched a bit and found nothing.
My form:
<form action="{{ path('searchPeople') }}" method="post" class="form-inline">
{{ form_errors(form) }}
{{ form_row(form.first_name) }}
{{ form_row(form.last_name) }}
{{ form_rest(form) }}
<br />
<button type="submit" class="btn btn-primary" /><i class="icon-search"></i>Search</button>
</form>
I know it's already answered, but might help somebody who is looking for a different solution for placeholders, if you don't want to change anything in your twig template:
$builder->add(
'name',
'text',
array(
'attr' => array(
'placeholder' => 'Your name',
),
'label' => false,
)
);
If you're outputting the field with form_rest you'll have to set the label for the the field to false in the form builder with something like
$builder->add('first_name', 'text', array(
'label' => false,
));
If you output the fields individually, you can omit the form_label for that field in the twig template, or set it to an empty string.
{{ form_label(form.first_name, '') }}
Convert label to placeholder
{% use 'form_div_layout.html.twig' with widget_attributes as base_widget_attributes %}
{% block widget_attributes %}
{% set attr = {'placeholder': label|trans({}, translation_domain)} %}
{{- block('base_widget_attributes') -}}
{% endblock widget_attributes %}
I did this recently! :) You'll want to create a new fields template, for form_row and one for form_widget. Then remove the form_label part, and add your placeholder.
http://symfony.com/doc/current/cookbook/form/form_customization.html
You can do it per field, or set it for all of them.
Or you can also skip the removing the form_label from the form_row template, and just do form_widget() where you're currently calling form_row()
for other that come across this label-question:
you could use form theme to override the form_row tag for every form you want. However I recommend to just set it invisible for page reader optimization. my example with bootstrap:
{% block form_row %}
{% spaceless %}
{{ form_label(form, null, {'label_attr': {'class': 'sr-only'}}) }}
{{ form_errors(form) }}
{{ form_widget(form) }}
{% endspaceless %}
{% endblock form_row %}
don't forget to include your formtheme in config.yml and template.
For those NOT using form_row, you can always add the placeholder as an attribute directly when adding the input to the builder. Like so:
$task = new Task();
$form = $this->createFormBuilder($task)
->add('first_name', 'text', array(
'required' => true,
'trim' => true,
'attr' => array('placeholder' => 'Lorem Ipsum')
)->getForm();
Symfony 2.8 & above
Remove form_label
{% block form_row %}
<div>
{{ form_errors(form) }}
{{ form_widget(form) }}
</div>
{% endblock form_row %}
Add placeholder attribute
{% block form_widget_simple %}
{% set type = type|default('text') %}
<input placeholder="{{ translation_domain is same as(false) ? label : label|trans({}, translation_domain) }}" type="{{ type }}" {{ block('widget_attributes') }} {% if value is not empty %}value="{{ value }}" {% endif %}/>
{% endblock form_widget_simple %}
Expanding on Léo's answer:
{% use 'form_div_layout.html.twig' %}
{% block widget_attributes %}
{% spaceless %}
{% set attr = attr|merge({'placeholder': label}) %}
{{ parent() }}
{% endspaceless %}
{% endblock widget_attributes %}
trans filter has been removed because it is included in the parent.
You must render the form manually.
Here's an example:
<form id="form-message" action="{{ path('home') }}" method="post" {{ form_enctype(form) }}>
{{ form_label(form.name) }}
{% if form_errors(form.name) %}
<div class="alert alert-error">
{{ form_errors(form.name) }}
</div>
{% endif %}
{{ form_widget(form.name) }}
{{ form_row(form._token) }}
<input type="submit" class="btn" value="Submit">
</form>
Related documentation
To sums it up:
Titi's answer is the most simple ;
Mick, Léo & Quolonel's answers are the most effective but are incomplete (for symfony > 2.6) :
If you use the label_format option in your *Type::configureOptions, their solution does not work. You need to add the content of the form_label block to handle all the label possibilities.
The full & most effective answer (code used w/ symfony 3.3) :
Remove form_label
{% block form_row %}
<div>
{{ form_errors(form) }}
{{ form_widget(form) }}
</div>
{% endblock form_row %}
Edit the widget_attribute block
{% block widget_attributes %}
{% spaceless %}
{% if label is not same as(false) -%}
{% if label is empty -%}
{%- if label_format is not empty -%}
{% set label = label_format|replace({
'%name%': name,
'%id%': id,
}) %}
{%- else -%}
{% set label = name|humanize %}
{%- endif -%}
{%- endif -%}
{% set attr = attr|merge({'placeholder': label}) %}
{%- endif -%}
{{ parent() }}
{% endspaceless %}
{% endblock widget_attributes %}
Notes :
Do not translate the labels into the widget_attributes block, otherwise they will appear as missing translations.
The solution does not work for checkboxes or radio buttons, you'll want to add something like :
{%- block checkbox_widget -%}
{{ parent() }}
{{- form_label(form) -}}
{%- endblock checkbox_widget -%}
Bootstrap Forms
In my case best is mix aswers of #Cethy and #Quolonel Questions
{% form_theme form _self %}
{% use 'bootstrap_4_layout.html.twig' %}
{% block widget_attributes %} {# set placeholder #}
{% spaceless %}
{% set attr = attr|merge({'placeholder': label}) %}
{{ parent() }}
{% endspaceless %}
{% endblock widget_attributes %}
{% block form_row %} {# remove label #}
<div class="form-group">
{{ form_errors(form) }}
{{ form_widget(form) }}
</div>
{% endblock form_row %}
It looks the following
It works with translations
You can also copy the labels into the placeholder attribute before rendering the form:
$formView = $form->createView();
foreach($formView->getIterator() as $item) {
/** #var $item FormView */
if ($item->vars['label']) {
$item->vars['attr']['placeholder'] =$item->vars['label'];
}
}

How to retrieve the number of errors in a subform (FormType) in Symfony2

I have a ProfileType Form in my UserBundle (extending from SonataUserBundle), in the ProfileType Form I added a sub form type (AddressType()).
ProfileType
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('gender')
->add('firstname')
->add('lastname')
->add('middlename')
->add('dateOfBirth', 'birthday', array('required' => false))
->add('phone')
->add('address', new AddressType(), array('required' => false));
;
}
/**
* {#inheritdoc}
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => $this->class,
'validation_groups' => array('Profile', 'Address'),
'cascade_validation' => true,
));
}
AddressType
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('postalcode')
->add('houseNumber')
->add('houseNumberAddition')
->add('street')
->add('city')
->add('country')
;
}
In the edit_profile_html.twig I like to know if there are any errors specifically in the AddressType. But form.address.vars.errors does not return the number of invalid elements in AddressType. If I specifically check the errors for a element in AddressType it does return the correct count.
So: form.address.vars.errors.length is 0, but form.address.postalcode.var.errors.length work. But I do not want to check for all elements one by one.
<fieldset {% if form.address.vars.errors|length>0 %} class="warning" {% endif %} >
<legend>{% trans %}vg.userbundle.form.address.legend{% endtrans %}</legend>
{{ form_rest(form.address) }}
</fieldset>
edit_profile_html.twig
{% block subtitle %}{{ "title_user_account" | trans({}, 'SonataUserBundle') }} - {{ "title_user_edit_profile" | trans({}, 'SonataUserBundle') }}{% endblock %}
{% block content %}
{% block fos_user_content %}
<form novalidate action="{{ path('sonata_user_profile_edit') }}" method="POST">
<fieldset {% if form.vars.errors|length>0 %} class="warning" {% endif %} >
<legend>{% trans %}vg.userbundle.form.profile.legend{% endtrans %}</legend>
{{ form_row(form.gender) }}
{{ form_row(form.firstname) }}
{{ form_row(form.lastname) }}
{{ form_row(form.middlename) }}
{{ form_row(form.dateOfBirth) }}
{{ form_row(form.phone) }}
</fieldset>
<fieldset {% if form.address.vars.errors|length>0 %} class="warning" {% endif %} >
<legend>{% trans %}vg.userbundle.form.address.legend{% endtrans %}</legend>
{{ form_rest(form.address) }}
</fieldset>
<fieldset class="submit">
<ul>
<li><input type="submit" name="submit"
value="{{ 'sonata_user_submit'|trans({}, 'SonataUserBundle') }}"/></li>
</ul>
</fieldset>
</form>
{% endblock %}
{% endblock content %}
So what is the correct way to retrieve the number of invalid elements of an 'embedded' FormType?
You can check if a form and all of its elements are valid (=have no errors) using the "valid" variable (which I guess is what you want to do):
<fieldset {% if not form.address.vars.valid %} class="warning" {% endif %} >
I suppose there is no way round to iterate through the object address and count the errors of child items.
{% for child in address %}
{{ ...count errors... }}
{% endfor %}
Note that every form item is an object which means that every child of "address" is an object as well.

Categories