Symfony 2 GenemuFormBundle如何用Ajax创建一个jQuery Select2

时间:2014-03-27 09:42:01

标签: forms symfony

我正在尝试使用GenemuFormBundle中的"Use jQuery Select2 with Ajax" doc添加Select2输入。在jQuery Select2 Field documentation之后添加jQuery Select2字段就可以了。

但是关于如何实现Ajax加载Select2表单的文档是非常不确定的。如果我正确地解读文档,它的目的是创建Select2 documentation中提到的相同内容。这正是我想创造的东西。我添加了隐藏字段以及所需的JavaScript,但我得到的唯一内容是Variable "id" does not exist in xBundle:x:new.html.twig at line x

表单构建器(直接从上述文档中获取):

...
->add('field_name', 'genemu_jqueryselect2_hidden', array(
    'configs' => array(
        'multiple' => true // Wether or not multiple values are allowed (default to false)
    )
))
->add('field_name', 'genemu_jqueryselect2_entity', array(
    'class' => 'xBundle:Entity',
    'property' => 'foo',
))

查看(也直接从文档中获取):

{% block stylesheets %}
    {{ form_stylesheet(form) }}
{% endblock %}

{% block javascript %}
    {{ form_javascript(form) }}
{% endblock %}

{% block genemu_jqueryselect2_javascript %}

    <script type="text/javascript">
        $field = $('#{{ id }}');

        var $configs = {{ configs|json_encode|raw }};

        // custom configs
        $configs = $.extend($configs, {
            query: function (query) {
                var data = {results: []}, i, j, s;
                for (i = 1; i < 5; i++) {
                    s = "";
                    for (j = 0; j < i; j++) {s = s + query.term;}
                    data.results.push({id: query.term + i, text: s});
                }
                query.callback(data);
            }
        });
        // end of custom configs

        $field.select2($configs);
    </script>

{% endblock %}

1 个答案:

答案 0 :(得分:5)

我只是在努力解决这个问题并且认为我会把自己的发现投入到碰巧碰到这个问题的任何人身上。我确实找到了一个解决方案,但我可能仍然建议使用Doug推荐的ZenStruckFormBundle,因为它似乎实际上被设计为用于通过ajax加载并与实体相关的select2字段类型的解决方案。

这不起作用的真正原因是,当您需要ajax-loading select2字段时,GenemuFormBundle的genemu_jqueryselect2_*类型不实现将与实体一起使用的data transformer 。它似乎没有被设计成以这种方式工作。当您使用genemu_jqueryselect2_hidden类型并将“multiple”配置选项设置为true时,它会添加ArrayToStringTransformer。这不适用于实体。

要解决此问题,您需要创建一个新的表单字段类型,并定义其父项为genemu_jqueryselect2_hidden,然后进行一些自定义。它看起来像这样......

namespace Acme\DemoBundle\Form\Type;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Acme\DemoBundle\Form\DataTransformer\EntityCollectionToIdTransformer;
use Doctrine\Common\Persistence\ObjectManager;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;

class EntityCollectionSelectorType extends AbstractType
{
    /**
     * @var ObjectManager
     */
     protected $om;

    /**
     * @param ObjectManager $om
     */
    public function __construct(ObjectManager $om)
    {
        $this->om = $om;
    }

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $transformer = new EntityCollectionToIdTransformer($this->om, $options['configs']['entity']);
        $builder->resetViewTransformers();
        $builder->addModelTransformer($transformer);
    }

    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(array(
            'invalid_message' => 'The selected entity does not exist',
            'required' => true,
            'auto_initialize' => false,
            'configs' => array('multiple' => true),
            'error_bubbling' => false, 
        ));
    }

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

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

然后你还需要添加上面表单字段类型中使用的新数据转换器,以便它可以转换表单字段和实体之间的值......

namespace Acme\DemoBundle\Form\DataTransformer;

use Symfony\Component\Form\DataTransformerInterface;
use Symfony\Component\Form\Exception\TransformationFailedException;
use Doctrine\ORM\PersistentCollection;
use Doctrine\Common\Persistence\ObjectManager;
use Doctrine\Common\Collections\ArrayCollection;

class EntityCollectionToIdTransformer implements DataTransformerInterface
{
    /**
     * @var ObjectManager
     */
    private $om;

    /**
     * @var string The Doctrine entity type to use
     */
    private $entityType;

    /**
     * @param ObjectManager $om
     */
    public function __construct(ObjectManager $om, $entityType)
    {
        $this->om = $om;
        $this->entityType = $entityType;
    }

    /**
     * Transforms a collection of entities to a comma separated string
     *
     * @param  ArrayCollection $entities
     * @return string
     */
    public function transform($entities)
    {
        if (null == $entities || empty($entities)) {
            return '';
        }

        $results = '';
        foreach ($entities as $entity) {
            $results .= $entity->getId() . ',';
        }
        $results = trim($results, ' ,');

        return $results;
    }

   /**
    * Transforms a string of comma separated IDs to a PersistentCollection for Doctrine
    *
    * @param  string $values
    * @return PersistentCollection|ArrayCollection
    * @throws TransformationFailedException if entity is not found.
    */
    public function reverseTransform($values)
    {
        if (!$values) {
            return new ArrayCollection();
        }
        $values = explode(',', $values);

        $collection = array();
        foreach ($values as $id) {
            $item = $this->om->getRepository($this->entityType)->findOneById($id);

            if (!is_null($item)) {
                $collection[] = $item;
            }
            else {
                throw new TransformationFailedException(sprintf(
                    'An entity with ID "%s" does not exist!',
                    $value
                ));
            }
        }

        return new PersistentCollection($this->om, $this->entityType, new ArrayCollection($collection));
    }
}

现在确保在配置中为您的服务定义新的字段类型...

<?xml version="1.0" ?>

<container xmlns="http://symfony.com/schema/dic/services"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">

    <parameters>
        ...
        <parameter key="acme_demo.form.type.entity_collection_selector.class">Acme\DemoBundle\Form\Type\EntityCollectionSelectorType</parameter>
        ...
    </parameters>

    <services>
        ...
        <service id="acme_demo.form.type.entity_collection_selector"
            class="%acme_demo.form.type.entity_collection_selector.class%">
            <argument type="service" id="doctrine.orm.default_entity_manager" />
            <tag name="form.type" alias="entity_collection_selector" />
        </service>
        ...
    </services>
</container>

现在你可以这样使用它......

$builder->add('customers', 'entity_collection_selector', array(
    'configs' => array('entity' => 'AcmeDemoBundle:Customer')
));