Symfony2 how to render all rows of form in twig - symfony

I built a form in symfony with this code:
foreach ( $options as $field => $title ) {
$form->add( $field, 'text', array('label' => $title, 'mapped' => false ) );
}
And I can't find a way to render all the rows of this form in twig.
Is this possible?

Ok. I've found how to do this.
In Twig:
{% for child in form.children|keys %}
{{ form_row(attribute(form.children,child)) }}
{% endfor %}

Are you looking for something like this : ?
{{ form(form) }}
or
{{ form_widget(form) }}
http://symfony.com/doc/current/cookbook/form/form_customization.html

Related

Symfony - Form with multiple entity objects

I'm running Symfony 3.4 LTS and I have an entity Attribute:
<?php
class Attribute
{
private $id;
private $code;
private $value;
private $active;
// My getters and setters
Below the database table:
I would like to get, in a form, all the rows with code == 'productstatus'. I tried:
<?php
$attributes = $this->getDoctrine()->getRepository(Attribute::class)->findBy(['code' => 'productstatus']);
// $attributes contains array with 3 objects 'Attribute'
$form = $this->createFormBuilder($attributes);
$form->add('value', TextType::class);
$output = $form->getForm()->createView();
If I dump() the $output var in Twig:
... I'm unable to make a loop to display the 3 fields with values.
{{ form_start(form) }}
{{ form_widget(form) }}
{{ form_end(form) }}
Result :
My goal is to allow the user to edit all the values of a specific attributes in the same form (or multiple forms, but in the same page). I already tried with CollectionType without success.
I found a solution: create 2 nested forms and use CollectionType. Hope it helps.
<?php
// Controller
$attributes = $this->getDoctrine()->getRepository(Attribute::class)->findBy(['code' => 'productstatus']);
$form = $this->createForm(AttributeForm::class, ['attributes' => $attributes]);
// AttributeForm
$builder
->add('attributes', CollectionType::class, [
'entry_type' => AttributeValueForm::class,
'allow_add' => true,
'by_reference' => false,
'allow_delete' => true,
]);
// AttributeValueForm
$builder
->add('value', TextType::class, ['label' => false, 'required' => false])
->add('active', CheckboxType::class, ['label' => false, 'required' => false])
// Twig view
{{ form_start(form) }}
{% for attribute in form.attributes.children %}
<tr>
<td>{{ form_widget(attribute.value) }}</td>
<td>{{ form_widget(attribute.active) }}</td>
</tr>
{% endfor %}
{{ form_end(form) }}
You an use an if statement in the your AttributeType like in the example below:
$builder
->add('entree', EntityType::class, array('class'=>'cantineBundle:plat',
'choice_label'=>function($plat){
if($plat->getType()=='Entree'&&$plat->getStatus()=="non reserve"){
return $plat->getNomPlat();
}
},
'multiple'=>false)
);

Symfony3.4 Form errors rendered twice

I have a form with author and message fields and NotBlank() validation on both.
In twig, I do this:
{{ form_start(form) }}
{{ form_errors(form.author) }}
{{ form_label(form.author) }}
{{ form_widget(form.author) }}
{{ form_errors(form.message) }}
{{ form_label(form.message) }}
{{ form_widget(form.message) }}
{{ form_end(form) }}
If I press Save button with empty fields I EXPECT to see this:
But I get this:
Somehow the bottom error message comes from the {{ form_label(...) }} I say this, because if I comment the labels out and use static HTML for labels, the output is like on first picture.
I would prefer not to use static HTML for labels, but I don't understand where the second error messages came from.
Below my code:
Form
class TestFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('author', TextType::class, ['required' => false, 'constraints' => [new NotBlank()]])
->add('message', TextType::class, ['required' => false, 'constraints' => [new NotBlank()]])
->add('save', SubmitType::class)
;
}
}
Controller
class TestController extends Controller
{
/**
* #Route("/testing", name="test")
* #param Request $request
* #return RedirectResponse|Response
*/
public function index(Request $request)
{
$form = $this->createForm(TestFormType::class);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid())
{
return $this->redirectToRoute('test');
}
return $this->render('test/index.html.twig', [
'form' => $form->createView(),
]);
}
}
Template
{% extends 'base.html.twig' %}
{% block title %}Hello TestController!{% endblock %}
{% block body %}
<p>This is a test...</p>
{{ form_start(form) }}
{{ form_errors(form.author) }}
{{ form_label(form.author) }}
{{ form_widget(form.author) }}
{{ form_errors(form.message) }}
{{ form_label(form.message) }}
{{ form_widget(form.message) }}
{{ form_end(form) }}
{% endblock %}
For bootstrap theme error block is integrated in label.
So you need either to remove form_errors block in your template or to override form_label block.
You can use form_row (as #Adrien suggests in commentaries) as there is no form_errors call
You've explicitly added form_errors whereas error message already rendered via form_label. either you can remove form_errors or form_label.

Label name is not rendered unless I specify it explictly in form type class

I have this code to render the value and label of fields in a form:
{% for field in filter %}
{{ field.vars.data }}
{{ field.vars.label }}
{% endfor %}
but it is not rendering the name of the labels unless I set them this way:
->add('name', 'autocomplete_q', array('label' => 'A_LABEL_NAME', 'required' => false,'class' => 'ExchangeAdminBundle:Compound'))
so is there any way render just the label name without setting it explictly?
In your template, you can add this kind of information:
{{ form_label(form.name, 'Your Name', {'label_attr': {'class': 'foo'}}) }}

represent a variable in twig using for loop

In my Service I return an array which looks like this:
$array = [
'bible_anagram_word' => $result->getBibleAnagramWord(),
'word_1' => $result->getWord1(),
'word_2' => $result->getWord2(),
'word_3' => $result->getWord3(),
...
'word_22' => $result->getWord22(),
'word_23' => $result->getWord23(),
'word_24' => $result->getWord24(),
'word_25' => $result->getWord25(),
'Bible_verse_reference' => $result->getBibleVerseReference(),
'Bible_verse_text' => $result->getBibleVerseText(),
'Bible_translation' => $result->getBibleTranslation(),
];
How do I represent document.word_## in this for loop?
{% for i in 1..25 %}
{{ document.word_ ~ i|slice(0,1) }}
{% endfor %}
In PHP this code is:
substr ( $this->document['word_'.$i] , 0 , 1 )
My unsuccessful attempts include:
{{ document.word_ ~ i|slice(0,1) }}
{{ (document.word_ ~ i)|slice(0,1) }}
{{ 'document.word_ ~ i'|slice(0,1) }}
This should do the trick
{{ attribute(document, 'word_' ~ i) }}
More on attribute function

Field help not showing in Sonata Admin in Parent Admin when embedded

I don't know whether this is a symphony or a sonata admin bundle issue.
I have my main MultimediaAdmin class which has multiple embedded FileAdmin entries.
class MultimediaAdmin extends Admin
{
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper
->with('Files')
->add('files','sonata_type_collection',
array('label' => 'Multimedia Files',
'btn_add' => 'Add File',
'by_reference' => 'false',
'type_options' => array('delete' => false)
), array(
'edit' => 'inline',
'template' => 'MyMultimediaBundle:Multimedia:horizontal.fields.html.twig'
)
)
->end()
->with('Tags')
->add('tags')
->end()
;
}
}
I have a custom template styling the appearance of the embedded FileAdmin form which among few fields has one that shows preview of the uploaded media when editing.
/* horizontal.fields.html.twig */
<fieldset>
<div class="sonata-ba-collapsed-fields">
{% for nested_group_field_name, nested_group_field in form.children %}
{% for field_name, nested_field in nested_group_field.children %}
<div class="control-group">
<label class="control-label" for="nested_field.vars['sonata_admin'].admin.trans(nested_field.vars.label" {{ nested_field.vars['required'] ? 'class="required"' : '' }}>{{ nested_field.vars['sonata_admin'].admin.trans(nested_field.vars.label) }}</label>
<div class="controls">
{% if sonata_admin.field_description.associationadmin.formfielddescriptions[field_name] is defined %}
{{ form_widget(nested_field, {
'inline': 'natural',
'edit' : 'inline'
}) }}
{% set dummy = nested_group_field.setrendered %}
{% else %}
{{ form_widget(nested_field) }}
{% endif %}
{% if sonata_admin.field_description.help %}
<span class="help-block sonata-ba-field-help">{{ sonata_admin.admin.trans(sonata_admin.field_description.help, {}, sonata_admin.field_description.translationDomain)|raw }}</span>
{% endif %}
</div>
</div>
{% endfor %}
{% endfor %}
</div>
</fieldset>
Here's FileAdmin which has image preview added to it when editing to show the thumbnail
class FileAdmin extends Admin
{
protected function configureFormFields(FormMapper $formMapper)
{
$media = $this->getSubject();
// use $fileFieldOptions so we can add other options to the field
$fileFieldOptions = array('required' => false,'label' => 'Files', 'attr' => array("multiple" => "multiple"), 'by_reference' => false);
if ($media && ($webPath = $media->getWebPath())) {
$fileFieldOptions['help'] = '<img src="'.$webPath.'" class="admin-preview" />';
}
$formMapper
->add('title','text',array('label'=>'Title'))
->add('abstract','textarea',array('label'=>'Abstract'))
->add('language')
->add('format')
->add('file', 'file', $fileFieldOptions)
->add('quality')
;
}
}
The custom form works perfectly in terms of styling but the only problem I have is, it doesn't show the image preview when editing embedded files in the multimedia form. When I go to edit file directly, not under MultimediaAdmin, the image preview works perfectly. Where could I be going wrong?

Resources