Symfony: Как избежать автоматического включения пользовательских типов форм в div?

Форма типа пользователя:

class UserType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('email', 'email', ['label' => 'EMail']);
        // various other fields....
    }

    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(array(
            'validation_groups' => array('registration'),
            'data_class' => 'Vendor\Model\Entity\User',
        ));
    }

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

Форма TutorType:

class TutorType extends Translate
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('user', new UserType(), ['label' => false]);

        $builder->add('school', 'entity', [
            'class' => 'Model:School',
            'property' => 'name',
            'label' => 'Label'
        ]);

        // Various other fields
        $builder->add('save', 'Submit');
    }

    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(array(
            //'validation_groups' => array('registration'),
            'data_class' => 'Vendor\Model\Entity\Tutor',
            'cascade_validation' => true,
        ));
    }

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

При рендеринге UserType отображается внутри div, я не могу найти способ преодолеть это.

Форма отображается как

результат

<form name="form_tutor"
      method="post"
      action=""
      novalidate="novalidate"
      class="form-horizontal form-horizontal"
      id="form_tutor">
    <div id="form_tutor"
         novalidate="novalidate"
         class="form-horizontal">
        <div class="form-group">
            <div class="col-lg-10">
                <div id="form_tutor_user">
                    <div class="form-group">
                        <label class="col-lg-2 control-label aaaa required"
                             for="form_tutor_user_email">EMail</label>

                        <div class="col-lg-10">
                            <input type="email"
                                 id="form_tutor_user_email"
                                 name="form_tutor[user][email]"
                                 required="required"
                                 class="form-control" />
                        </div>
                    </div>
                </div>
            </div>
        </div>

        <div class="form-group">
            <label class="col-lg-2 control-label aaaa required"
                 for="form_tutor_tutorType">Type</label>

            <div class="col-lg-10">
                <select id="form_tutor_tutorType"
                     name="form_tutor[tutorType]"
                     class="form-control">
                    </select>
            </div>
        </div>

        <div class="form-group">
            <div class="col-lg-offset-2 col-lg-10">
                <button type="submit"
                     id="form_tutor_save"
                     name="form_tutor[save]"
                     class="btn btn-default">Speichern</button>
            </div>
        </div><input type="hidden"
             id="form_tutor__token"
             name="form_tutor[_token]"
             class="form-control"
             value="s6i6zPxJs7KU5CiEe8i6Ahg_ca8rc2t5CnSk5yAsUhk" />
    </div>
</form>

form_tutor_user заключен в собственный div группы форм. Я пытался перезаписать form_tutor_user_widget, но это на один уровень глубже. (И только быстрое исправление, оно должно быть глобально применено ко всем типам форм - Классы)

Как я могу изменить тему, чтобы все пользовательские типы не были обернуты шаблоном form_row по умолчанию?

Или как я узнаю в ветке, когда отображается «подформа»? поэтому я могу решить напечатать <div class="form-group">, когда дочерний узел не является подчиненной формой, или пропустить его, если это так.

ТИА


person Rufinus    schedule 25.02.2014    source источник
comment
Какой bootstrap-bundle вы там используете? Или это ваша собственная тема формы Boostrap? Стандартная версия не отображает div с классом form-group.   -  person Nicolai Fröhlich    schedule 03.03.2014
comment
я использую themeforest.net/item/   -  person Rufinus    schedule 04.03.2014
comment
и github.com/braincrafted/bootstrap-bundle (но файл темы немного изменен, чтобы соответствовать мой хтмл)   -  person Rufinus    schedule 04.03.2014


Ответы (6)


По умолчанию в теме базовой формы:

{% block form_row %}
{% spaceless %}
    <div>
        {{ form_label(form) }}
        {{ form_errors(form) }}
        {{ form_widget(form) }}
    </div>
{% endspaceless %}
{% endblock form_row %}

И, для пользовательских составных форм:

{% block form_widget_compound %}
{% spaceless %}
    <div {{ block('widget_container_attributes') }}>
        {% if form.parent is empty %}
            {{ form_errors(form) }}
        {% endif %}
        {{ block('form_rows') }}
        {{ form_rest(form) }}
    </div>
{% endspaceless %}
{% endblock form_widget_compound %}

Если вы что-то здесь не изменили, DIV, который вы видите, должен исходить либо из одного, либо из другого фрагмента шаблона.

Однако в вашем конкретном примере, если определен form_tutor_user_row, первый бит никогда не используется, а если определен form_tutor_user_widget, последний бит никогда не используется.

Вернемся к вашему вопросу. Ваш вопрос: "Как я могу изменить тему, чтобы все пользовательские типы не были обернуты шаблоном form_row по умолчанию?"

Вот проблема, как я ее вижу: вы хотите, чтобы все ваши ТОП-формы (форма, в которую включены все подформы) имели общий способ рендеринга в разделах. Каждый раздел будет включен в DIV с class="form-group". Вы можете добавить некоторые дополнительные операции рендеринга, но я ограничусь этим, чтобы не усложнять ситуацию.

Что вам нужно сделать, так это создать определенный тип формы и сделать так, чтобы все ваши ТОП-формы наследуются от этого нового типа формы. Например:

class TopType extends AbstractType
{
    public function getName()
    {
        return 'top_form';
    }
}

... и унаследованная форма:

class MyFormType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        ...
    }

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

    public function getParent()
    {
        return 'top_form';
    }
}

Как видите, нет необходимости делать наследование PHP для работы наследования тем форм.

С точки зрения темы шаблона (могу я даже сказать это?), если для my_form не задана конкретная тема формы, Symfony поймет, что используемая здесь тема формы по умолчанию — это тема формы top_form, которую вы можете определить как таковую:

{% block top_form_widget %}
{% spaceless %}
    {% for child in form %}
        <div class="form-group">
            {{ form_widget(child) }}
        </div>
    {% endfor %}
    {{ form_rest(form) }}
{% endspaceless %}
{% endblock top_form_widget %}

Я должен добавить, что это проблема, с которой я уже столкнулся и решил. Расскажите мне, как это работает для вас.

Изменить:

Подводя итог, вам нужно сделать следующее:

  • Создайте тип формы TopType,
  • Добавьте блок top_form_widget в тему формы,
  • Для всех ваших корневых форм (т. е. форм верхнего уровня, форм, у которых нет родителя) добавьте метод getParent(), который будет возвращать имя вашей формы TopType ("top_form").
person Zephyr    schedule 03.03.2014
comment
я попробую, но, похоже, в файле темы у меня будет много блоков виджетов формы. пока лучшее решение. Спасибо друг. - person Rufinus; 04.03.2014
comment
Но я считаю только 1 блок темы формы и 1 класс для всего решения? Два первых блока темы в моем посте взяты из темы формы по умолчанию SF2, они здесь только для пояснения. - person Zephyr; 04.03.2014
comment
может быть, я неправильно понял, но my_form и top_form должны быть уникальными строками, не так ли? у меня есть ›20 форм/подформ в этом приложении. - person Rufinus; 05.03.2014
comment
В SF2 у вас должно быть одно имя формы (top_form, my_form, т.е. строки, возвращаемые getName()) по типу формы, разные для каждого типа формы. Сообщение отредактировано для более четких указаний. - person Zephyr; 05.03.2014
comment
да, это то, что я имел в виду ... но неважно, это все еще лучшее решение на данный момент. спасибо зефир! - person Rufinus; 06.03.2014

Теоретически, если вы переопределите блок form_widget_compound в теме глобальной формы таким образом, он должен работать так, как вы хотите:

// app/Resources/views/form.html.twig

{% block form_widget_compound %}
    {% if form.parent is empty %}
        <div {{ block('widget_container_attributes') }}>
        {{ form_errors(form) }}
    {% endif %}
    {{ block('form_rows') }}
    {{ form_rest(form) }}
    {% if form.parent is empty %}
        </div>
    {% endif %}
{% endblock %}

И зарегистрируйте свою тему формы:

// app/config/config.yml
twig:
    form:
        resources:
            - "::form.html.twig"
person egeloen    schedule 27.02.2014
comment
во-первых, спасибо за ваш ответ, к сожалению, это не работает. Соединение тоже вложенное. (составной код в составном коде) - person Rufinus; 28.02.2014

Я обычно решаю эту проблему, отрисовывая поля вложенной формы вручную:

{{ form_row(form.tutor.school) }}
{{ form_row(form.tutor.user.email) }}

Возможно, это не самое элегантное решение, но оно работает для меня, а я еще не искал элегантного.

person Elnur Abdurrakhimov    schedule 28.02.2014
comment
спасибо за Ваш ответ. это мой текущий обходной путь, но мне нужно много форм, которые сильно изменятся (проект Agil), чтобы менять представление каждый раз, когда я меняю форму, это не то, что я хочу. - person Rufinus; 28.02.2014
comment
Если вы найдете лучший способ, я буду рад узнать об этом. Просто это никогда не было моим главным приоритетом. - person Elnur Abdurrakhimov; 28.02.2014
comment
это решение простое, эффективное, и его будет легко понять, даже когда я вернусь к коду несколько месяцев спустя — идеально! - person Ben; 12.06.2014

Bootstrap 3.3.4 В итоге я сделал это.

Они являются ключевой частью этого:

 <div class="{% if form.parent.parent is empty %}form-group{% endif %}{% if (not compound or force_error|default(false)) and not valid %} has-error{% endif %}">

Полный шаблон.

{% block form_row -%}
    <div class="{% if form.parent.parent is empty %}form-group{% endif %}{% if (not compound or force_error|default(false)) and not valid %} has-error{% endif %}">
        {% if form.parent.parent is null and label is not empty %}
            {{- form_label(form) -}}
        {% elseif label is empty %}
            {{- form_label(form) -}}
        {% endif %}
        {% if compound is empty %}<div class="{{ block('form_group_class') }}">{% endif %}
            {{- form_widget(form) -}}
            {{- form_errors(form) -}}
        {% if compound is empty %}</div>{% endif %}
    </div>
{%- endblock form_row %}
person tlorens    schedule 23.10.2019
comment
тем временем я пришел к тому же решению, но забыл его прокомментировать. спасибо за Ваш ответ. - person Rufinus; 24.10.2019

Возможно, это не элегантное решение, но мне оно подходит, потому что я тоже пытался найти решение.

Например:

{% for custom_field in form.custom_fields %}
<div class="edit_custom">
{{ form_row(custom_field.name) }}
{{ form_row(custom_field.value) }}
</div>
{% endfor %}

<script>
$('.edit_custom').find('input').unwrap();
</script>
person Alice    schedule 01.12.2015

Попробуйте использовать form_themes.

Во-первых, в родительском шаблоне определите тему формы:

{% form_theme form with ['BundleName:ControlerName:somesubform_form.html.twig'] %}

Кстати, замените BundleName, ControllerName и somesubform на правильные имена.

затем сделайте это с помощью:

{{ form_row(form.somesubform) }}
person enterx    schedule 03.03.2014