Symfony2:集合中的单选按钮 [英] Symfony2 : Radio buttons in a collection

查看:25
本文介绍了Symfony2:集合中的单选按钮的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

In my application, I created a form using the collection field type :

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

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

With some JQuery, this code works correctly, but now I would like to select one of this dynamic tag to make it "the main tag".

在我的标签实体中,我添加了一个布尔属性来定义标签是否是主要标签:

In my Tag entity, I added a boolean attribute which define if the tag is the main or not :

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

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

But in my view, each row now contains a checkbox. So I can select more than one main tag. How to transform this checkbox in radio button please ?

推荐答案

您没有从正确的角度解决问题.如果应该有一个主标签,那么这个属性不应该添加在Tag实体本身中,而应该添加到包含它的实体中!

You're not tackling the problem from the right angle. If there should be a main tag, then this property should not be added in the Tag entity itself, but in the entity that contains it!

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

I'm speaking of the data_class entity related to the form having the tags attribute. This is the entity that should have a mainTag property.

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

If defined properly, this new mainTag attribute will not be a boolean, for it will contain a Tag instance, and thus will not be associated to a checkbox entry.

所以,在我看来,您应该有一个包含您的实例的 ma​​inTag 属性和一个包含所有其他标签的 tags 属性.

So, the way I see it, you should have a mainTag property containing your instance and a tags property that conatins all other tags.

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

The problem with that is that your collection field will no longer contain the main tag. You should thus also create a special getter getAllTags that will merge your main tag with all others, and change your collection definition to:

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

现在,您可能会问,我们如何添加单选框?为此,您必须生成一个新字段:

Now, how do we add the radio boxes, you may ask? For this, you will have to generate a new field:

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

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

These are the basics however, it only grows more complex from here. The real problem here is how your form is displayed. In a same field, you mix the usual display of a collection and the display of a choice field of the parent form of that collection. This will force you to use form theming.

为了给可重用性留出一些空间,您需要创建一个自定义字段.关联的数据类:

To allow some room to reusability, you need to create a custom field. The associated 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,
        ));
   }
}

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

Finally, in the form theme template:

{% 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 %}

最后,我们需要将其包含在您的父实体中,即最初包含标签的实体:

Lastly, we need to include that in your parent entity, the one that originally contained tags:

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 中无法轻松完成您想要做的事情.

I recognize the solution I propose is verbose, however it seems to me to be the most efficient. What you are trying to do cannot be done easily in Symfony.

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

You can also note that there is an odd "prototype" option in the comment. I just wanted to underline a very useful property of "collection" in your case: the prototype option contains a blank item of your collection, with placeholders to replace. This allow to quickly add new items in a collection field using javascript, more info here.

这篇关于Symfony2:集合中的单选按钮的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆