Symfony2:集合中的单选按钮

 COCO歧 发布于 2023-02-05 10:10

在我的应用程序中,我使用collection字段类型创建了一个表单:

$builder->add('tags', 'collection', array(
   'type' => new TagType(),
   'label' => false,
   'allow_add' => true,
   'allow_delete' => true,
   'by_reference' => false
));

使用一些JQuery,这段代码可以正常工作,但现在我想选择一个这个动态标签,使其成为"主要标签".

在我的Tag实体中,我添加了一个布尔属性,用于定义标记是否为main:

/**
 * @ORM\Column(name="main", type="boolean")
 */
private $main;

但在我看来,每行现在都包含一个复选框.所以我可以选择多个主标签.如何在单选按钮中转换此复选框?

2 个回答
  • 这不是正确的解决方案,但因为您使用jQuery来添加/删除...

    TagType

    ->add('main', 'radio', [
        'attr' => [
            'class' => 'toggle'
            ],
         'required' => false
        ])
    

    jQuery的

    div.on('change', 'input.toggle', function() {
    
        div
        .find('input.toggle')
        .not(this)
        .removeAttr('checked');
    });
    

    http://jsfiddle.net/coma/CnvMk/

    并使用回调约束来确保只有一个主标记.

    2023-02-05 10:13 回答
  • 你没有从正确的角度解决这个问题.如果应该有一个主标记,那么不应该在Tag实体本身中添加此属性,而是在包含它的实体中添加!

    我说的是与具有tags属性的表单相关的data_class实体.这是应该具有mainTag属性的实体.

    如果正确定义,这个新的mainTag属性将不是布尔值,因为它将包含一个Tag实例,因此不会与复选框条目相关联.

    所以,我看到它的方式,你应该有一个包含你的实例的mainTag属性和一个包含所有其他标签的tags属性.

    问题是您的收集字段将不再包含主标记.因此,您还应该创建一个特殊的getter getAllTags,它将您的主标记与所有其他标记合并,并将您的集合定义更改为:

    $builder->add('allTags', 'collection', array(
        'type' => new TagType(),
        'label' => false,
        'allow_add' => true,
        'allow_delete' => true,
        'by_reference' => false
    ));
    

    现在,您可以问我们如何添加收音机盒?为此,您必须生成一个新字段:

    $builder->add('mainTag', 'radio', array(
        'type' => 'choice',
        'multiple' => false,
        'expanded' => true,
        'property_path' => 'mainTag.id', // Necessary, for 'choice' does not support data_classes
    ));
    

    然而,这些是基础知识,它从这里开始变得更加复杂.这里真正的问题是你的表单是如何显示的.在同一个字段中,您可以混合集合的常规显示和该集合的父表单的选择字段的显示.这将强制您使用表单主题.

    要允许一些空间可重用,您需要创建自定义字段.关联的data_class:

    class TagSelection
    {
        private mainTag;
    
        private $tags;
    
        public function getAllTags()
        {
            return array_merge(array($this->getMainTag()), $this->getTags());
        }
    
        public function setAllTags($tags)
        {
            // If the main tag is not null, search and remove it before calling setTags($tags)
        }
    
        // Getters, setters
    }
    

    表格类型:

    class TagSelectionType extends AbstractType
    {
        protected buildForm( ... )
        {
            $builder->add('allTags', 'collection', array(
                'type' => new TagType(),
                'label' => false,
                'allow_add' => true,
                'allow_delete' => true,
                'by_reference' => false
            ));
    
            // Since we cannot know which tags are available before binding or setting data, a listener must be used
            $formFactory = $builder->getFormFactory();
            $listener = function(FormEvent $event) use ($formFactory) {
    
                $data = $event->getForm()->getData();
    
                // Get all tags id currently in the data
                $choices = ...;
                // Careful, in PRE_BIND this is an array of scalars while in PRE_SET_DATA it is an array of Tag instances
    
                $field = $this->factory->createNamed('mainTag', 'radio', null, array(
                    'type' => 'choice',
                    'multiple' => false,
                    'expanded' => true,
                    'choices' => $choices,
                    'property_path' => 'mainTag.id',
                ));
                $event->getForm()->add($field);
            }
    
            $builder->addEventListener(FormEvent::PRE_SET_DATA, $listener);
            $builder->addEventListener(FormEvent::PRE_BIND, $listener);
        }
    
        public function getName()
        {
            return 'tag_selection';
        }
    
        public function setDefaultOptions(OptionsResolverInterface $resolver)
        {
            $resolver->setDefaults(array(
                'data_class' => 'TagSelection', // Adapt depending on class name
                // 'prototype' => true,
            ));
       }
    }
    

    最后,在表单主题模板中:

    {% block tag_selection_widget %}
        {% spaceless %}
        {# {% set attr = attr|default({})|merge({'data-prototype': form_widget(prototype)}) %} #}
        <ul {{ block('widget_attributes') }}>
            {% for child in form.allTags %}
            <li>{{ form_widget(form.mainTag[child.name]) }} {{ form_widget(child) }}</li>
            {% endfor %}
        </ul>
        {% endspaceless %}
    {% endblock tag_selection_widget %}
    

    最后,我们需要在您的父实体中包含最初包含标记的实体:

    class entity
    {
        // Doctrine definition and whatnot
        private $tags;
    
        // Doctrine definition and whatnot
        private $mainTag;
    
        ...
        public setAllTags($tagSelection)
        {
            $this->setMainTag($tagSelection->getMainTag());
            $this->setTags($tagSelection->getTags());
        }
    
        public getAllTags()
        {
            $ret = new TagSelection();
            $ret->setMainTag($this->getMainTag());
            $ret->setTags($this->getTags());
    
            return $ret;
        }
    
        ...
    }
    

    并以您的原始形式:

    $builder->add('allTags', new TagSelection(), array(
        'label' => false,
    ));
    

    我认识到我提出的解决方案很冗长,但在我看来它是最有效的.你想要做的事情在Symfony中无法轻易完成.

    您还可以注意到评论中有一个奇怪的"原型"选项.我只想在你的情况下强调一个非常有用的"集合"属性:prototype选项包含你的集合的空白项,并替换占位符.这允许使用javascript在集合字段中快速添加新项目,此处有更多信息.

    2023-02-05 10:14 回答
撰写答案
今天,你开发时遇到什么问题呢?
立即提问
热门标签
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有