【问题标题】:Symfony Render a Collection Form Type PrototypeSymfony 渲染一个集合表单类型原型
【发布时间】:2021-10-08 14:51:48
【问题描述】:

我尝试使用嵌入集合表单呈现我创建的集合类型原型。 我阅读了 Symonfy、Github 上的文档......但没有成功理解使用块呈现表单的方式。 在这里,我有一个基于 User 实体的表单 (RegistrationFormType),它具有 Adress (AdressFormType) 的嵌入集合表单。 我成功地添加了按钮并在表单上生成了新地址,但我希望我的原型具有与我的第一个地址相同的布局(从数据库中检索)。 你能帮我理解我做错了什么吗?

这是我的 RegistrationFormType

<?php

namespace App\Form;

use App\Entity\User;
//use Doctrine\DBAL\Types\TextType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\TelType;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints\IsTrue;
use Symfony\Component\Validator\Constraints\Length;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Form\CallbackTransformer;


class RegistrationFormType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('roles',ChoiceType::class,[
                'choices'=>[
                'Specialist'=>'Role_Specialist',
                'Parent'=>'Role_Parent',
                'Center'=>'Role_Center',],
                'label'=>"Je m'inscris en tant que"])
            ->add('name')
            ->add('firstname',TextType::class, [
                'label' => 'Firstname',
                'row_attr' => [
                    'id' => 'firstname'
                ],])
            ->add('email')
            ->add('phone',TelType::class)
            ->add('NISS',TextType::class, [
                'label' => 'NISS',
                'row_attr' => [
                    'id' => 'NISS'
                ]
                ,
                'required'=>'false',
                ])
            ->add('BCE',TextType::class, [
                'label' => 'BCE',
                'row_attr' => [
                    'id' => 'BCE'
                ],
                'required'=>'false'])
            ->add('agreeTerms', CheckboxType::class, [
                'mapped' => false,
                'constraints' => [
                    new IsTrue([
                        'message' => 'You should agree to our terms.',
                    ]),
                ],
            ])
            ->add('plainPassword', PasswordType::class, [
                // instead of being set onto the object directly,
                // this is read and encoded in the controller
                'mapped' => false,
                'attr' => ['autocomplete' => 'new-password'],
                'constraints' => [
                    new NotBlank([
                        'message' => 'Please enter a password',
                    ]),
                    new Length([
                        'min' => 6,
                        'minMessage' => 'Your password should be at least {{ limit }} characters',
                        // max length allowed by Symfony for security reasons
                        'max' => 4096,
                    ]),
                ],
            ])
            //imbrication de adress dans le formulaire user afin de retrouver toutes les adresses qui lui sont référées
            ->add('adress',CollectionType::class,[
                'entry_type' => AdressType::class,
                'entry_options' => ['label' => false],
                'allow_add' => true,
                'block_name' => 'adress'

            ])
        ;
        // Data transformer
        $builder->get('roles')
            ->addModelTransformer(new CallbackTransformer(
                function ($rolesArray) {
                    // transform the array to a string
                    return count($rolesArray)? $rolesArray[0]: null;
                },
                function ($rolesString) {
                    // transform the string back to an array
                    return [$rolesString];
                }
            ));
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => User::class,
        ]);
    }
}

地址类型

<?php

namespace App\Form;

use App\Entity\Adress;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class AdressType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('type',ChoiceType::class,[
                'choices' =>[
                    'Adresse Principale' => 'Principale',
                    'Adresse Secondaire' => 'Secondaire'
                    ]
            ])
            ->add('housenumber',TextType::class,[
                'label'=>'N°'
            ])
            ->add('additional_info',TextType::class,[
                'label'=>'Apt/Etage/...',
                'required'=>false
            ])
            ->add('street', TextType::class, [
                'label' => 'Rue',
                'row_attr' => [
                    'id' => 'Adress2'
                ],])
            ->add('postalcode')
            ->add('city')
            ->add('region')
            ->add('country')
            ->add('latitude', HiddenType::class)
            ->add('longitude', HiddenType::class)
//            ->add('users')

        ;
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => Adress::class,
        ]);
    }
}

树枝模板

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

{% block title %}Register{% endblock %}
{% block body %}

    {% for flashError in app.flashes('verify_email_error') %}
        <div class="alert alert-danger" role="alert">{{ flashError }}</div>
    {% endfor %}
    <div class="container">
    {{ form_start(registrationForm) }}
    <div class="form-group">{{ form_row(registrationForm.roles) }}</div>
    <div class="form-row">
        <div class="form-group col-md-6">{{ form_row(registrationForm.name) }}</div>
        <div class="form-group col-md-6">{{ form_row(registrationForm.firstname) }}</div>
    </div>
    <div class="form-row">
        <div class="form-group col-md-6">{{ form_row(registrationForm.email) }}</div>
        <div class="form-group col-md-6">{{ form_row(registrationForm.plainPassword, {
                label: 'Password'
            }) }}</div>
    </div>
    <div class="form-row">
        <div class="form-group col-md-4">{{ form_row(registrationForm.phone) }}</div>
        <div class="form-group col-md-4">{{ form_row(registrationForm.NISS, {label: 'NISS'}) }}</div>
        <div class="form-group col-md-4">{{ form_row(registrationForm.BCE, {label: 'BCE'}) }}</div>
    </div>
{#        @TODO formatter pour qu'on differencie bien les adresses entre elles par block. Cf block en dessous#}
        <h3>Adresses</h3>
        {% block _registration_form_adress_entry_widget %}
        <div class="adress" data-prototype="{{ form_widget(registrationForm.adress.vars.prototype)|e('html_attr') }}">
            {% for adress in registrationForm.adress %}
            <div class="form-group col-md-4">{{ form_row(adress.type) }}</div>
        <div class="form-row">
                <div class="form-group col-md-4">{{ form_row(adress.street) }}</div>
                <div class="form-group col-md-4">{{ form_row(adress.housenumber) }}</div>
                <div class="form-group col-md-4">{{ form_row(adress.additional_info) }}</div>
        </div>
        <div class="form-row">
            <div class="form-group col-md-3">{{ form_row(adress.postalcode) }}</div>
            <div class="form-group col-md-3">{{ form_row(adress.city) }}</div>
            <div class="form-group col-md-3">{{ form_row(adress.region) }}</div>
            <div class="form-group col-md-3">{{ form_row(adress.country) }}</div>
        </div>
            {% endfor %}
        </div>

        <button type="button" class="add_item_link" data-collection-holder-class="adress">Add an adress</button>



        {{ form_end(registrationForm) }}
        {% endblock %}
    </div>
    <script>
        const addFormToCollection = (e) => {
            const collectionHolder = document.querySelector('.' + e.currentTarget.dataset.collectionHolderClass);

            const item = document.createElement('li');

            item.innerHTML = collectionHolder
                .dataset
                .prototype
                .replace(
                    /__name__/g,
                    collectionHolder.dataset.index
                );

            collectionHolder.appendChild(item);

            collectionHolder.dataset.index++;
        };
        document
            .querySelectorAll('.add_item_link')
            .forEach(btn => btn.addEventListener("click", addFormToCollection));

    </script>
{% endblock %}
{#{% block registration_form_adress_entry_row %}#}
{#    <div class="form-row">#}
{#        <div class="form-group col-md-4">{{ form_row(adress.street) }}</div>#}
{#            <div class="form-group col-md-4">{{ form_row(adress.housenumber) }}</div>#}
{#            <div class="form-group col-md-4">{{ form_row(adress.additional_info) }}</div>#}
{#        </div>#}
{#        <div class="form-row">#}
{#            <div class="form-group col-md-3">{{ form_row(adress.postalcode) }}</div>#}
{#            <div class="form-group col-md-3">{{ form_row(adress.city) }}</div>#}
{#            <div class="form-group col-md-3">{{ form_row(adress.region) }}</div>#}
{#            <div class="form-group col-md-3">{{ form_row(adress.country) }}</div>#}
{#    </div>#}
{#{% endblock %}#}

代码渲染

谢谢你:)

【问题讨论】:

    标签: forms symfony collections twig embed


    【解决方案1】:

    您可以定义一个宏来呈现表单的现有子字段和原型字符串。

    {% import _self as formMacros %}
    {% macro address(item) %}
    ... your code formatting the address subform ... 
    {% endmacro %}
    
    

    所以树枝中的这一行:

    <div class="adress" data-prototype="{{ form_widget(registrationForm.adress.vars.prototype)|e('html_attr') }}">
    

    <div class="adress" data-prototype="{{ formMacros.address(registrationForm.adress.vars.prototype)|e('html_attr') }}">
    

    现有字段也是如此。

    【讨论】:

      猜你喜欢
      • 2015-08-04
      • 2016-11-29
      • 1970-01-01
      • 2021-03-13
      • 2018-11-21
      • 1970-01-01
      • 1970-01-01
      • 2021-05-07
      • 2016-09-27
      相关资源
      最近更新 更多