0

I would want a form for my entity « X ». This entity own a relationship OneToMany with many entities of type « X ». It's a relationship parent <-> children.

When I create my form simply, it works.

class XType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add("name",     "text",       array("label" => "Nom"))
            ->add("children", "collection", array(
                "type"         => new XType(),
                "by_reference" => false));
    }
}

Then, I would like add easily new entity in my collection with the option « allow_add », and used the prototype to add in javascript. This is my form with the « allow_add » option.

public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder
        ->add("name",     "text",       array("label" => "Nom"))
        ->add("children", "collection", array(
            "type"         => new XType(),
            "allow_add"    => true,
            "by_reference" => false));
}

When I execute with or without called the prototype, I have an webserver error. It's XDebug which kick my request because the recursive call is too big. There are cascade call.

What's the best solution to resolve my problem ?

2 Answers 2

2

I can't really say where your problem is, but should be somewhere around javascript and the collection prototype. Also, when you allow items to be added, you usually allow to allow_delete too.

Take a look at my example:

$builder->add('book',   'collection',   array(
                                                'type' => new BookType(),
                                                 'allow_add' => true,
                                                 'allow_delete' => true,
                                                 'prototype_name' => '__prototype__',
                                                 'by_reference' => false,
                                                 'error_bubbling' => false
                                               );

To the template where you render form, include this:

{% block javascript %}
    {{ parent() }}
    {% javascripts
    '@ProjectSomeBundle/Resources/public/js/form/collection.js'
    %}
    <script type="text/javascript" src="{{ asset_url }}"></script>
    {% endjavascripts %}
{% endblock %}

And the collection.js file holds this:

$(function($) {
    $addButton = $('button.add');
    $collection = $addButton.parent().children().first();

    $addButton.click(function () {
        var prototype = $($collection.data('prototype').replace(/__prototype__/g, function() { return (new Date()).getTime(); }));
        prototype.appendTo($collection.children('ul'));
        });

    $('body').on('click', 'button.remove', function () {
        $(this).parent().remove();
    });
});

Also, you should be using those customised collection widgets, which you pass to twig. Nothing fancy, it will be rendered as unordered list with items, to make items easily accessible.

config.yml:

twig:
    form:
        resources:
            - 'ProjectSomeBundle:Form:fields.html.twig'

fields.html.twig:

{% extends 'form_div_layout.html.twig' %}

{% block collection_widget %}
    {% import  "ProjectSomeBundle:Macro:macro.html.twig" as macro %}
    {% spaceless %}
        <div class="collection">
            {% if prototype is defined %}
                {% set attr = attr|merge({'data-prototype': block('prototype_widget') }) %}
            {% endif %}
            <div {{ block('widget_container_attributes') }}>
                <ul>
                    {% for row in form %}
                        {{ macro.collection_item_widget(row) }}
                    {% endfor %}
                </ul>
                {{ form_rest(form) }}
            </div>
            <div class="clear"></div>
            <button type="button" class="add">{% trans %}Add{% endtrans %}</button>
        </div>
        <div class="clear"></div>
    {% endspaceless %}
{% endblock collection_widget %}

{% block prototype_widget %}
    {% spaceless %}
        {{ macro.collection_item_widget(prototype) }}
    {% endspaceless %}
{% endblock prototype_widget %}

You can notice it uses macro, so here it is:

macro.html.twig

{% macro collection_item_widget(fields) %}
    <li>
        {% set fieldNum = 1 %}
        {% for field in fields %}
            <div class="field_{{ fieldNum }}">
                {{ form_label(field) }}
                {{ form_errors(field) }}
                {{ form_widget(field) }}
            </div>
            {% set fieldNum = fieldNum + 1 %}
        {% endfor %}
        <button type="button" class="remove">{% trans %}Delete{% endtrans %}</button>
        <div class="clear"></div>
    </li>
{% endmacro %}

This is a full example, I hope you find it useful and works for you.

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

Comments

1

I ran into the same issue (my web server crashes, as well, due to too many recursive calls). My quick workaround was to simply create a dummy (cloned) type that doesn't contain the recursive field (this works for me since I was interested only into the 1st level children).

So, if this scenario is applicable to you, as well, you could change your code as follows:

class XType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add("name",     "text",       array("label" => "Nom"))
            ->add("children", "collection", array(
                "type"         => new XTypeWithoutChildren(),
                "by_reference" => false));
    }
}

class XTypeWithoutChildren extends XType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add("name",     "text",       array("label" => "Nom"));
    }
}

Comments

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.