访问视图中的嵌套表单字段(嵌入表单集合)

Posted

技术标签:

【中文标题】访问视图中的嵌套表单字段(嵌入表单集合)【英文标题】:Access nested form fields in view (Embed a Collection of Forms) 【发布时间】:2018-06-02 19:25:45 【问题描述】:

我尝试使用CollectionType 在 symfony 3 中构建一个巨大的表单。我必须定义多个子表单,一些是多个,一些是单个。

这是我的 FormType:

public function buildRegistrationForm(FormBuilderInterface $builder, array $options) 
    $builder->add('userRegistration', CollectionType::class, [
        'entry_type' => UserRegistrationType::class,
        'entry_options' => ['label' => true],
    ]);
    $builder->add('meters', CollectionType::class, [
        'entry_type' => MeterType::class,
        'entry_options' => ['label' => true],
        'allow_add' => true,
    ]);
    ...

现在我尝试访问视图中的 CollectionType 字段。代码如下:

 form_label(registrationForm.email, null, 'label_attr': 'class': 'form-label') 
 form_widget(registrationForm.email, 'attr': 'class': 'form-control') 

但我得到了错误:

Neither the property "email" nor one of the methods "email()", "getemail()"/"isemail()"/"hasemail()" or "__call()" exist and have public access in class "Symfony\Component\Form\FormView".

我知道 Symfony 试图直接从主表单 (registrationForm) 中获取电子邮件字段,但我不知道如何访问子表单。在文档 (http://symfony.com/doc/current/form/form_collections.html) 中描述了我可以使用 registrationForm.userRegistration.email 简单地访问子表单。但这给了我错误:

Neither the property "userRegistration" nor one of the methods ...

如何访问视图中的子字段?

【问题讨论】:

【参考方案1】:

    第一步是了解我们为什么要使用collectionType? 如果你有一对多或多对多的关系,你应该使用 CollectionType。 示例:

    /**
     * Class Team
     *
     * @ORM\Entity
     * @ORM\Table(name="example_project_team")
     */
    class Team
    
    
        // ... 
    
        /**
         * Unidirectional Many-To-Many
         *
         * Many Teams has many users accounts.
         *
         * @var ArrayCollection $users
         *
         * @ORM\ManyToMany(
         *     targetEntity="AppBundle\Entity\User",
         *     cascade="persist", "remove",
         *     orphanRemoval=true
         *     )
         *
         * @ORM\JoinTable(name="teams_to_users",
         *      joinColumns=@ORM\JoinColumn(name="team_id", referencedColumnName="id", onDelete="CASCADE"),
         *      inverseJoinColumns=@ORM\JoinColumn(name="user_id", referencedColumnName="id", onDelete="CASCADE")
         *      )
         */
        protected $users;
    
         // ...
    
    
    

在这个例子中,我们有一个名为 team 的实体。每个团队都有很多用户(这只是与您相关的示例)。我猜,您已经创建了用户实体。

    想象一下,您的用户实体拥有 UserRegistrationType。

    /**
     * Class UserRegistrationType
     */
    class UserRegistrationType extends AbstractType
    
        /**
         * @param FormBuilderInterface  $builder
         * @param array                 $options
         */
        public function buildForm(FormBuilderInterface $builder, array $options)
        
            $builder
                ->add('username', 'Symfony\Component\Form\Extension\Core\Type\TextType', [
                    'label'                 => false,
                    'translation_domain'    => 'messages'
                ])
                ->add('email', 'Symfony\Component\Form\Extension\Core\Type\TextType', [
                    'label'                 => false,
                    'translation_domain'    => 'messages'
                ])
    
                // ... the other fields
    
            ;
        
    
        /**
         * @return string
         */
        public function getName()
        
            return 'app_user_registration_type';
        
    
        /**
         * @return null|string
         */
        public function getBlockPrefix()
        
            return 'app_user_registration';
        
    
        /**
         * @param OptionsResolver $resolver
         */
        public function configureOptions(OptionsResolver $resolver)
        
            $resolver->setDefaults(array(
                'data_class'            => User::class,
                'csrf_protection'       => true,
                'validation'            => true,
            ));
        
    
    

    请注意!!! 我们在这里使用'data_class' => User::class, 如您所见,我们在 userRegistrationType 中使用了 User 对象。这意味着,我们可以为每个对象使用此表单,该对象具有 User 类型的字段或 CollectionType 类型的字段(您的情况!)但用户集合! 我们的团队实体有字段用户。

    现在,由于我们已经创建了 userRegistrationType,我们可以将它添加到 TeamFormType。

    public function teamRegistrationFormType(FormBuilderInterface $builder, array $options) 
        $builder->add('users', CollectionType::class, [
            'entry_type' => UserRegistrationType::class,
                'entry_options' => [
                    'label' => false,
                ],
                'label'         => false,
                'allow_add'     => true,
                'allow_delete'  => true,
                'by_reference'  => false,
        ]);
    
        // ...
    
    
    

    最后。您在树枝中的表格:

    # you can add this form in your twig file #
    <div class="box">
        % block team_form %
         form_start(team_form,  'attr': 'class': 'form-horizontal', 'role' : 'form') 
        <div class="box-body">
                    <div class="form-group">
                        % block users_collection %
                            <label for="" class="col-sm-2 control-label">
                                 'admin.label.phones'|trans  
                            </label>
                            <div class="col-sm-10 users" data-prototype=" form_widget(team_form.users.vars.prototype)|e('html_attr') ">
                                % for user in users %
                                    <div>
                                         form_widget(user) 
                                    </div>
                                % endfor %
                            </div>
                            <span class="help-block">
                                 form_errors(users) 
                            </span>
                        % endblock users_collection %
                    </div>
            <div class="form-group">
                    <div class="col-sm-offset-2 col-sm-10">
                        <button type="submit" class="btn btn-danger">
                             'admin.button.submit'|trans 
                        </button>
                    </div>
                </div>
                 form_end(team_form) 
        </div>
        % endblock team_form %
    </div>
    

    这里有奇怪的小部件 form_widget(user) 。我猜,你想编辑这个小部件的样式。你可以使用 Symfony 内置的表单样式来做到这一点。创建文件(如果您还没有创建它)path_to_your_project/src/AppBundle/Resources/views/Form/fields.html.twig 并添加您的 userRegistrationType 的样式。请注意,这个小部件的名称是根据您的名称形成的块前缀和字符串“_widget”(public function getBlockPrefix()

    % file fields.html.twig %
    % trans_default_domain 'messages' %
    
    % block app_user_registration_widget %
        % spaceless %
            <div class="form-group"  block('widget_container_attributes') >
                <div class="col-sm-2">
                     form_widget(form.username, 'attr' : 'class' : 'form-control' ) 
                </div>
                <div class="col-sm-4">
                     form_widget(form.email, 'attr' : 'class' : 'form-control' ) 
                </div>
            </div>
        % endspaceless %
    % endblock %
    

    当然,您必须设置添加和删除按钮。为此,我建议您使用官方文档:How to Embed a Collection of Forms 或者您可以使用捆绑包(但不是官方的):ninsuo/symfony-collection

【讨论】:

以上是关于访问视图中的嵌套表单字段(嵌入表单集合)的主要内容,如果未能解决你的问题,请参考以下文章

Rails 4 - 根据嵌套形式的第一个选择菜单中的选择动态填充第二个选择菜单

backone-保存到带有嵌套视图的数据库

如果 Rails 表单中的另一个字段为空,则验证嵌套字段

EasyAdmin 3:嵌套表单(另一个集合中的调用)

CakePHP 中的嵌套表单

React Hook Form:提交带有嵌套组件的表单或提取嵌套组件的字段提交