使用symfony / form输入标签

时间:2016-03-01 11:25:59

标签: php forms symfony

我需要实现像https://select2.github.io/examples.html#tags这样的标签输入。我不太确定如何使用symfony / form实现服务器端部分。我无法找到任何现有的实现,我不确定是否应该尝试以某种方式破解ChoiceType或尝试单独实现它。

你能帮帮我吗?我错过了任何现有的解决方案吗?

3 个答案:

答案 0 :(得分:1)

IMO这种模式非常适合CollectionTypeHow to Embed a Collection of Forms也可能很有趣。您只需要确保您的javascript / jquery插件可以与原型功能一起使用。

答案 1 :(得分:1)

<强> 1。创建一个继承自ChoiceType的特殊FormType:

    namespace Alsatian\FormBundle\Form;

    use Symfony\Component\Form\AbstractType;

    use Symfony\Component\Form\FormBuilderInterface;
    use Symfony\Component\OptionsResolver\Options;
    use Symfony\Component\OptionsResolver\OptionsResolver;

    use Symfony\Component\Form\Extension\Core\Type\ChoiceType;

    class ExtentedChoiceType extends AbstractType
    {
       /**
        * {@inheritdoc}
        */
        public function configureOptions(OptionsResolver $resolver)
        {  
            $resolver->setDefaults(array('choices'=>array()));
        }

        public function getParent()
        {
            return ChoiceType::class;
        }
    }

<强> 2。创建一个FormEvent订阅者,使用您在客户端添加的标记填充表单:

        namespace Alsatian\FormBundle\Form\Extensions;

        use Symfony\Component\EventDispatcher\EventSubscriberInterface;
        use Symfony\Component\Form\FormEvent;
        use Symfony\Component\Form\FormEvents;

        use Alsatian\FormBundle\Form\ExtentedChoiceType;

        class ExtentedChoicesSubscriber implements EventSubscriberInterface
        {
            public static function getSubscribedEvents()
            {
                return array(
                    FormEvents::PRE_SET_DATA => array('populateChoices',-50),
                    FormEvents::PRE_SUBMIT   => array('populateChoices',-50)
                );
            }

            public function populateChoices(FormEvent $event)
            {
                foreach($event->getForm()->all() as $child){
                    if(get_class($child->getConfig()->getType()->getInnerType()) === ExtentedChoiceType::class){
                        $this->populateChoice($event,$child->getName());
                    }
                }
            }

            private function populateChoice(FormEvent $event,$childName)
            {   
                $form = $event->getForm();
                $child = $form->get($childName);
                $type = get_class($child->getConfig()->getType()->getInnerType());
                $options = $child->getConfig()->getOptions();

                $choices = array();

                $data = $event->getData();

                if(!array_key_exists($childName,$data)){return;}
                $data = $data[$childName];

                if($data != null){
                    if(is_array($data)){
                        foreach($data as $choice){
                            $choices[$choice] = $choice;
                        }
                    }
                    else{
                        $choices[$data] = $data;
                    }
                }

                // Feel free to find a better way to reuse the defined options. In Sf 2.6 it was not possible here :
                $newOptions = array('route'=>$options['route'],'required'=>$options['required'],'multiple'=>$options['multiple'],'choices'=>$choices);
                $form->add($childName,$type,$newOptions);
            }
        }

第3。用法:

        namespace AppBundle\Form;

        use Symfony\Component\Form\AbstractType;
        use Symfony\Component\Form\FormBuilderInterface;

        use Alsatian\FormBundle\Form\ExtentedChoiceType;
        use Alsatian\FormBundle\Form\Extensions\ExtentedChoicesSubscriber;

        class ArticleType extends AbstractType
        {
            /**
             * @param FormBuilderInterface $builder
             * @param array $options
             */
            public function buildForm(FormBuilderInterface $builder, array $options)
            {
                $builder
                    ->add('tags',ExtentedChoiceType::class,array("multiple"=>true))
                    ->addEventSubscriber(new ExtentedChoicesSubscriber());
            }
        }

答案 2 :(得分:0)

我确定有很多方法可以做到这一点。例如,这是我在项目中使用它的方式。

此自定义表单类型在从视图转换为模型数据时返回array,并且在从模型转换为视图数据时需要array

有关Symfony3中数据转换器的更多信息:http://symfony.com/doc/current/cookbook/form/data_transformers.html#about-model-and-view-transformers

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\CallbackTransformer;
use Symfony\Component\Form\FormBuilderInterface;

class TagsType extends AbstractType {

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->addViewTransformer(new CallbackTransformer(
            function ($original) {
                return $original ? implode(', ', $original) : '';
            },
            function ($submitted) {
                if (!$submitted) {
                    return [];
                }

                $submitted = array_map(function($tag) {
                    return trim($tag);
                }, explode(',', $submitted));

                return $submitted;
            }
        ));
    }

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

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

}

根据您的前端部分,默认情况下会将其渲染为输入字段,您可以在其中编写以逗号分隔的标记。