kissifrot/select2entity-bundle

一个集成Select2的Symfony扩展包,作为Symfony表单中标准实体字段的替代。

安装次数: 1,997

依赖项: 0

建议者: 0

安全性: 0

星标: 0

关注者: 0

分支: 110

类型:symfony-bundle

v4.0.0 2024-07-04 15:46 UTC

This package is auto-updated.

Last update: 2024-09-04 16:24:33 UTC


README

简介

这是一个Symfony扩展包,允许使用流行的Select2组件作为Symfony表单中标准实体字段的替代。

它与Symfony 4和5兼容。对于Symfony 2和3,请使用2.x版本的扩展包。对于Select2 4.0及以上版本。对于旧版本,请使用1.x版本的扩展包(与Symfony 5不兼容)。

与标准Symfony实体字段(使用html select渲染)相比,该扩展包提供的最主要特性是列表通过远程ajax调用获取。这意味着列表的大小几乎是无限的。唯一的限制是数据库查询或远程Web服务中检索数据的性能。

它支持单选和多选。如果表单正在编辑一个Symfony实体,则这些模式对应于一对多和多对多关系。在多选模式下,大多数人发现Select2的用户界面比具有多个=true属性的标准选择标签更容易使用,后者涉及到尴尬的Ctrl键使用等。

该项目受到了lifo/typeahead-bundle的启发,该项目使用Bootstrap 2中的Typeahead组件提供类似的功能。Select2Entity可以用于Select2可以安装的任何地方,包括Bootstrap 3。

感谢@ismailbaskin,我们现在有了Select2版本4的兼容性。

截图

这是一个具有展开的单选字段列表的表单。

Single select example

这是一个具有展开的多选字段列表的表单。

Multiple select example

安装

首先必须安装并使Select2正常工作。我希望设置一个演示网站,但我的设置基本上是BraincraftedBootstrapBundle与Bootstrap 3一起安装Select2。一旦Braincrafted扩展包正常工作,我唯一需要安装的文件是

https://github.com/select2/select2/tree/4.0.0安装select2.js和select2.css

https://github.com/t0m/select2-bootstrap-css/tree/bootstrap3安装select2-bootstrap.css。这样就可以为Bootstrap 3工作。

这些文件位于我的某个扩展包的Resources/public/js和Resources/public/css文件夹中,然后包含在我的main_layout.html.twig主布局文件中。

或者,可以使用以下两行代码从CloudFlare CDN加载select2.js和select2.css的压缩版本:https://select2.github.io。确保脚本标签在jQuery加载之后,这可能在页面页脚中。

  • tetranz/select2entity-bundle 添加到项目 composer.json 的 "requires" 部分
{
    // ...
    "require": {
        // ...
        "tetranz/select2entity-bundle": "2.*"
    }
}

注意,这仅适用于Select2版本4。如果您使用Select2版本3.X,请在 composer.json 中使用 "tetranz/select2entity-bundle": "1.*"

  • 在项目根目录中运行 php composer.phar update tetranz/select2entity-bundle
  • 更新项目的 config/bundles.php 文件,并将此扩展包添加到 $bundles 数组
$bundles = [
    // ...
    Tetranz\Select2EntityBundle\TetranzSelect2EntityBundle::class => ['all' => true]
];
  • 更新项目的 config/packages/twig.yaml 文件,以提供全局twig表单模板
twig:
    form_themes:
        - '@TetranzSelect2Entity/Form/fields.html.twig'

* Load the Javascript on the page. The simplest way is to add the following to your layout file. Don't forget to run console assets:install. Alternatively, do something more sophisticated with Assetic.
<script src="{{ asset('bundles/tetranzselect2entity/js/select2entity.js') }}"></script>

如何使用

以下内容适用于 Symfony 4。有关 Symfony 2/3 的配置和使用,请参阅 https://github.com/tetranz/select2entity-bundle/tree/v2.1

Select2Entity 的使用非常简单。在表单类型类的 buildForm 方法中,将 Select2EntityType::class 指定为类型,而不是通常使用的 entity:class

以下是一个示例

$builder
   ->add('country', Select2EntityType::class, [
            'multiple' => true,
            'remote_route' => 'tetranz_test_default_countryquery',
            'remote_params' => [], // static route parameters for request->query
            'class' => '\Tetranz\TestBundle\Entity\Country',
            'primary_key' => 'id',
            'text_property' => 'name',
            'minimum_input_length' => 2,
            'page_limit' => 10,
            'allow_clear' => true,
            'delay' => 250,
            'cache' => true,
            'cache_timeout' => 60000, // if 'cache' is true
            'language' => 'en',
            'placeholder' => 'Select a country',
            'query_parameters' => [
                'start' => new \DateTime(),
                'end' => (new \DateTime())->modify('+5d'),
                // any other parameters you want your ajax route request->query to get, that you might want to modify dynamically
            ],
            // 'object_manager' => $objectManager, // inject a custom object / entity manager 
        ])

将以下内容放在包含表单类型类的文件顶部

use Tetranz\Select2EntityBundle\Form\Type\Select2EntityType;

选项

如果未设置,将使用默认值。

  • class 是您的实体类。必需
  • primary_key 是用于唯一标识实体的属性名称。默认为 'id'
  • text_property 这是用于检索现有数据的实体属性。如果省略 text_property,则将实体转换为字符串。这要求它必须具有 __toString() 方法。
  • multiple 对于多选(多对多)为 True。对于单选(多对一)为 False。
  • minimum_input_length 是您需要按下的键数,搜索才会发生。默认为 2。
  • page_limit 这作为查询参数传递给远程调用。它旨在用于限制返回列表的大小。默认为 10。
  • scroll 为 True 将启用无限滚动。默认为 false。
  • allow_clear 为 True 将导致 Select2 显示一个小的 x 来清除值。默认为 false。
  • allow_add 是 Select2 的添加标签设置的选项数组。仅在表单上的 'multiple' 为 True 时可用。
    • enabled 启用允许新标签选项。True 或 False。默认 False。
    • new_tag_text 如果 allow_add 为 true,则显示在不存在实体后面的文本。默认为 " (NEW)"。
    • new_tag_prefix 新标签的前缀标识符,默认为 "__"。您的实际值开头不能包含这些符号。
    • tag_separators 一个 JavaScript 分隔符数组,用于自动拆分标签。
  • delay 在触发另一个 AJAX 请求之前,按键后的延迟(以毫秒为单位)。默认为 250 ms。
  • placeholder 占位符文本。
  • language i18n 语言代码。默认为 en。
  • theme 默认为 'default'。
  • cache 启用 AJAX 缓存。对于每个查询的 'term',结果将被缓存。
  • cache_timeout 缓存查询的时间(以毫秒为单位)。设置为 0 将导致缓存永远不会超时 (60000 = 60 seconds)
  • transformer 如果需要如以下所述的灵活性,则提供完全限定的自定义转换器类名。
  • autostart 确定是否在文档准备好时自动调用 select2 jQuery 代码。默认为 true,这提供正常操作。
  • width 如果不为 null,则设置数据-width 属性。默认为 null。
  • class_type 可选值将作为查询字符串参数添加到 AJAX 请求中。
  • render_html 这将在 ['html'] 下渲染您返回的结果。

远程查询的 URL 可以通过两种方式之一提供:remote_route 是 Symfony 路由。可以可选地指定 remote_params 来提供参数。或者,可以使用 remote_path 来指定 URL。

您可以使用 query_parameters 在远程_params 需要动态更改时使用。您可以使用 $('#elem').data('query-parameters', { /* new params */ }); 来更改它们。

您可以在您的 config/packages/tetranzselect2entity.yaml 文件中使用以下格式更改默认值。

tetranz_select2_entity:
    minimum_input_length: 2
    page_limit: 8
    allow_clear: true
    delay: 500
    language: 'fr'
    theme: 'default'
    cache: false
    cache_timeout: 0
    scroll: true
    object_manager: 'manager_alias'
    render_html: true

AJAX 响应

控制器应返回以下格式的 JSON 数组。属性必须是 idtext

[
  { id: 1, text: 'Displayed Text 1' },
  { id: 2, text: 'Displayed Text 2' }
]

无限滚动

如果您的结果是通过Select2的“无限滚动”功能分页显示,那么您可以选择继续返回与上面显示相同的数组(为了向后兼容,此捆绑包将自动尝试确定是否需要更多结果),或者返回以下所示的对象以对分页结果有更精细的控制。

如果还有更多结果要加载,则more字段应为true。

{
  results: [
     { id: 1, text: 'Displayed Text 1' },
     { id: 2, text: 'Displayed Text 2' }
  ],
  more: true
}

获取结果的控制器操作将接收到一个名为page的参数,表示应加载哪个结果页。如果您将滚动设置为true,则必须在查询中处理页面参数。如果不这样做,会发生奇怪的事情。

自定义选项文本

如果您需要更多灵活性来显示每个选项的文本,例如显示实体中几个字段的值或显示图像,您可以定义自己的自定义转换器。您的转换器必须实现DataTransformerInterface。最简单的方法可能是扩展EntityToPropertyTransformer或EntitiesToPropertyTransformer,并重新定义transform()方法。这样,您就可以返回任何东西作为text,而不仅仅是单个实体属性。

以下是一个示例,它返回国家名称和大陆(Country实体中的两个不同属性)

$builder
    ->add('country', Select2EntityType::class, [
        'multiple' => true,
        'remote_route' => 'tetranz_test_default_countryquery',
        'class' => '\Tetranz\TestBundle\Entity\Country',
        'transformer' => '\Tetranz\TestBundle\Form\DataTransformer\CountryEntitiesToPropertyTransformer',
    ]);

在transform中设置数据数组如下

$data[] = array(
    'id' => $country->getId(),
    'text' => $country->getName().' ('.$country->getContinent()->getName().')',
);

您的自定义转换器和相应的Ajax控制器应返回以下格式的数组

[ 
    { id: 1, text: 'United Kingdom (Europe)' },
    { id: 1, text: 'China (Asia)' }
]

如果您正在使用allow_add选项,并且实体除了text_property字段外还需要其他字段以有效,则您可能需要扩展EntitiesToPropertyTransformer来添加缺失的字段,创建doctrine prePersist监听器,或者在提交表单后保存之前在表单视图中添加缺失的数据。

添加新标签

如果您想通过Select2标签创建新实体,可以使用allow_add选项集启用它。

例如

$builder
    ->add('tags', Select2EntityType::class, [
        'remote_route' => 'tetranz_test_tags',
        'class' => '\Tetranz\TestBundle\Entity\PostTags',
        'text_property' => 'name',
        'multiple' => true,
        'allow_add' => [
            'enabled' => true,
            'new_tag_text' => ' (NEW)',
            'new_tag_prefix' => '__',
            'tag_separators' => '[",", " "]'
        ],
    ]);

添加标签时需要注意的一些事项

  • 您的数据不应有任何可能与new_tag_prefix的前缀字符匹配的机会。如果有,请将其更改为其他类似“**”或“$$”的内容。
  • tag_separators与Select2选项相同。它应是一个JavaScript数组。
  • 如果您想要允许添加的实体除了在text_property中指定的字段外还有其他必填字段,则必须在表单提交中添加它们或向doctrine实体添加prePersist钩子。
  • 如果您想通过单个输入模式允许使用“tags”创建新实体,请注意您需要移除空格作为分隔符,否则您将无法在此实体中输入空格字符。
$builder
    ->add('tags', Select2EntityType::class, [
        ...
        'allow_add' => [
            ...
            'tag_separators' => '[",", ""]' // No Space here
        ],
    ]);

在请求中包含其他字段值

如果您需要包含其他字段值,因为选择取决于它,您可以使用req_params选项。键是查询字符串中的参数名称,值是FormView中的路径(如果您不知道路径,可以在模板中执行类似{{ dump(form) }}的操作)。

property选项指的是用作标签以及搜索词的实体字段。

在回调中,您将获得QueryBuilder作为参数来修改结果查询和数据对象(数据可以是简单的Request对象或纯数组。有关更多详细信息,请参阅AutocompleteService.php)。

$builder
    ->add('firstName', TextType::class)
        ->add('lastName', TextType::class)
        ->add('state', EntityType::class, array('class' => State::class))
        ->add('county', Select2EntityType::class, [
            'required' => true,
            'multiple' => false,
            'remote_route' => 'ajax_autocomplete',
            'class' => County::class,
            'minimum_input_length' => 0,
            'page_limit' => 10,
            'scroll' => true,
            'allow_clear' => false,
            'req_params' => ['state' => 'parent.children[state]'],
            'property' => 'name',
            'callback'    => function (QueryBuilder $qb, $data) {
                $qb->andWhere('e.state = :state');

                if ($data instanceof Request) {
                    $qb->setParameter('state', $data->get('state'));
                } else {
                    $qb->setParameter('state', $data['state']);
                }

            },
        ])
    ->add('city', Select2EntityType::class, [
        'required' => true,
        'multiple' => false,
        'remote_route' => 'ajax_autocomplete',
        'class' => City::class,
        'minimum_input_length' => 0,
        'page_limit' => 10,
        'scroll' => true,
        'allow_clear' => false,
        'req_params' => ['county' => 'parent.children[county]'],
        'property' => 'name',
        'callback'    => function (QueryBuilder $qb, $data) {
            $qb->andWhere('e.county = :county');

            if ($data instanceof Request) {
                $qb->setParameter('county', $data->get('county'));
            } else {
                $qb->setParameter('county', $data['county']);
            }

        },
    ]);

由于请求处理通常非常相似,您可以使用一个服务来帮助您处理结果。要使用它,只需在您的控制器中添加一个路由即可

    /**
     * @param Request $request
     *
     * @Route("/autocomplete", name="ajax_autocomplete")
     *
     * @return Response
     */
    public function autocompleteAction(Request $request)
    {
        // Check security etc. if needed
    
        $as = $this->get('tetranz_select2entity.autocomplete_service');

        $result = $as->getAutocompleteResults($request, YourFormType::class);

        return new JsonResponse($result);
    }

模板化

现在已将通用模板化添加到捆绑包中。如果您需要在选择结果中渲染HTML代码,请将render_html选项设置为true,并在控制器中返回如下数据

[ 
    { id: 1, text: 'United Kingdom (Europe)', html: '<img src="images/flags/en.png" />' },
    { id: 2, text: 'China (Asia)', html: '<img src="images/flags/ch.png">' }
]
如果您需要进一步的模板化,您需要按照以下方式重写 .select2entity() 方法。如果您需要在 Select2 中使用 [模板化](https://select2.org/dropdown#templating),您可以参考以下示例,该示例在每个选项旁边显示国家标志。

您的自定义转换器应返回类似以下的数据

[ 
    { id: 1, text: 'United Kingdom (Europe)', img: 'images/flags/en.png' },
    { id: 2, text: 'China (Asia)', img: 'images/flags/ch.png' }
]

您需要定义自己的 JavaScript 函数 select2entityAjax,该函数扩展了原始的 select2entity 函数,并显示带有图像的自定义模板

$.fn.select2entityAjax = function(action) {
    var action = action || {};
    var template = function (item) {
        var img = item.img || null;
        if (!img) {
            if (item.element && item.element.dataset.img) {
                img = item.element.dataset.img;
            } else {
                return item.text;
            }
        }
        return $(
            '<span><img src="' + img + '" class="img-circle img-sm"> ' + item.text + '</span>'
        );
    };
    this.select2entity($.extend(action, {
        templateResult: template,
        templateSelection: template
    }));
    return this;
};
$('.select2entity').select2entityAjax();

此脚本将为所有具有类 select2entity 的元素全局添加功能,但如果未传递 img,则将像原始的 select2entity 一样工作。您应该在表单中添加 'autostart' => false 以正确运行 JS 代码。

    ->add('contry', Select2EntityType::class, [
        'remote_route' => 'country_select2_query',
        'autostart' => false,
    ])

您还需要重写以下模板中的以下部分

{% block tetranz_select2entity_widget_select_option %}
    <option value="{{ label.id }}" selected="selected"
            {% for key, data in label %}
                {% if key not in ['id', 'text'] %} data-{{ key }}="{{ data }}"{% endif %}
            {% endfor %}>
        {{ label.text }}
    </option>
{% endblock %}

此部分将所有需要添加到 JavaScript 函数 select2entityAjax 的额外数据添加到其中,如数据属性。在这种情况下,我们传递了 data-img

主题

Select2 支持使用 theme 选项自定义主题,因此您可以将其样式设置为与您的应用程序其他部分相匹配。对于 Bootstrap4 主题,请参阅 https://github.com/ttskch/select2-bootstrap4-theme

嵌入集合表单

如果您使用 嵌入式集合表单data-prototype 在您的表单中添加新元素,您将需要以下 JavaScript,该 JavaScript 将监听添加 .select2entity 元素的事件

$('body').on('click', '[data-prototype]', function(e) {
    $(this).prev().find('.select2entity').last().select2entity();
});