2

I want to render a form which has multiple Entities of same Class. I will display 2 fields, Price(type=text) and Enabled(type=checkbox).

I don't know how many I will have of those entities, so form will have to get them dynamically.

I have tried to do the following

public function buildForm(FormBuilderInterface $builder, array $options)
{

        $builder
            ->add('price', 'text', array(
                'label' => 'Price',
                'required' => true
            ))
            ->add('enabled','checkbox',array(
                'label'     => 'Use this currency',

            ))
        ;    
}

public function setDefaultOptions(OptionsResolverInterface $resolver)
{
    $resolver->setDefaults(array(
        'data_class'        => 'Osiris\Entity\Pricing',
        'csrf_protection'   => false
    ));
}

public function getName()
{
    return 'pricingtype';
}

And in my Controller I have created my form like this:

$pricingForm = $this->createFormBuilder($prices)
               ->add('items','collection',array(
                   'required' => false,
                   'prototype' => true,
                   'type'    => new PricingType(),
               ))
                ->getForm()
            ;

In my twig I do:

{% for price in form_pricing %}
    <h2>Price</h2>
    <div class="row">{{ form_widget(price) }}</div>
{% endfor %}

However it comes only with h2 Prices and empty div with class=row. I feel like I am half way there, but I've no idea how to move on. If someone knows how to get fields on submit as well, I will really appreciate it.

2
  • you got the solution you were looking for? Commented Apr 1, 2015 at 13:58
  • Yes I posted it as an answer to my question below. Commented Apr 2, 2015 at 10:56

2 Answers 2

2

I found the solution,

the way I was creating the form in Controller was wrong! I had to do the following:

$pricingForm = $this->createFormBuilder(array('prices'=>$prices))
                ->add('prices','collection',array(
                    'required'       => true,
                    'allow_add'      => true,
                    'type'           => new PricingType(),
               ))
                ->getForm()
            ;

"allow_add => true" is necessary when working with collection, otherwise it will NOT add any of PricingType collection of entities to the form.

Then, because the form is built inside the controller "$this->createFormBuilder(array('prices'=>$prices))" , $prices array must be passed as an array with array keyname same as the one used in "->add('prices','collection',array(...)" , which is 'prices' so Symfony will know what to bind where. $prices is an array of Pricing objects array(0 => new Pricing()).

In my PricingType I have:

class PricingType extends AbstractType {

    public function buildForm(FormBuilderInterface $builder, array $options)
    {

        $builder
            ->add('price', 'text', array(
                'label' => false,
                'required' => true
            ))
            ->add('enabled','checkbox',array(
                'label'     => 'Use this currency',

            ))
        ;

    }

    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(array(
            'data_class'        =>  'XXX\XXX\Entity\Pricing',
            'csrf_protection'   => false
        ));
    }

    public function getName()
    {
        return 'pricingtype';
    }
}

Here I need to have control over label attribute. I could not find the way for it( if anyone knows please post how to). I override my twig template as follows:

On the top we need next line of code:

    {% form_theme form_pricing _self %}

Then override row and widget as follows (it was a nightmare to debug):

{% block _form_prices_entry_row %}
    {% spaceless %}
        {{ form_widget(form) }}
    {% endspaceless %}
{% endblock %}

{% block _form_prices_entry_widget %}
    {% spaceless %}

        {{ form_row(form.price, { 'label' : form.vars.value.getCurrency().getTitle() } ) }}
        {{ form_row(form.enabled) }}

    {% endspaceless %}
{% endblock %}

In the body then, render form elements as follows:

{% for price in form_pricing.prices %}
                    <div class="price-row">{{ form_row(price) }}</div>
                {% endfor %}

I really hope this will help you guyz. It was a real nightmare to debug especially the twig file, I did it thanks to my clever colleague.

Sign up to request clarification or add additional context in comments.

1 Comment

Hi, This is exactly what I trying to do, but I am not able to clone your solution. How do you define your controllers? If you are still on it, please explain to me too.
0

I think you have missed a for loop in your twig file Check this example:

    {# store the prototype on the data-prototype attribute #}
    <ul id="email-fields-list" data-prototype="{{ form_widget(form.emails.vars.prototype)|e }}">
    {% for emailField in form.emails %}
        <li>
            {{ form_errors(emailField) }}
            {{ form_widget(emailField) }}
        </li>
    {% endfor %}
    </ul>

See the loop, I think that you need to add in your twig file.

In addition to loops you need to add JavaScript also.

Check this link:

http://symfony.com/doc/current/reference/forms/types/collection.html#adding-and-removing-items

Check the complete code. It will help you out to generate multiple entity forms from a single entity class using collection field type.

4 Comments

It is a nice example, but I don't give option to user to add more fields to that form.
then don't add JavaScript code, just form and twig code is enough.
I create an array of Pricing and pass it to FormBuilder. When I loop over my form in twig ( {% for price in form_pricing.prices %} <div class="row">{{ form_widget(price) }}</div> ) It does not render anything back. Any idea?
From Symfony2 Example Page: In both cases, no input fields would render unless your emails data array already contained some emails. given in this link: symfony.com/doc/current/reference/forms/types/…

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.