From 5cd9a4b25fbbcd7e352e15d866467713e4a74ff0 Mon Sep 17 00:00:00 2001 From: Piotr Antosik Date: Mon, 27 Nov 2017 18:33:58 +0100 Subject: [PATCH 01/17] sf4 --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 2ec21f4..221dccc 100644 --- a/composer.json +++ b/composer.json @@ -12,7 +12,7 @@ ], "require": { "php": ">=5.4.0", - "symfony/symfony": ">=2.2", + "symfony/symfony": "^2.7 || ^3.0 || ^4.0", "doctrine/orm": ">=2.4" }, "autoload": { From 8a78c1a51b0695d4ab11beb7f9c67c76e6209507 Mon Sep 17 00:00:00 2001 From: Piotr Antosik Date: Mon, 27 Nov 2017 18:37:22 +0100 Subject: [PATCH 02/17] sf4 --- composer.json | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/composer.json b/composer.json index 221dccc..907cd50 100644 --- a/composer.json +++ b/composer.json @@ -11,16 +11,11 @@ } ], "require": { - "php": ">=5.4.0", + "php": "^5.5.9 || ^7.0", "symfony/symfony": "^2.7 || ^3.0 || ^4.0", - "doctrine/orm": ">=2.4" + "doctrine/doctrine-bundle": "^1.3" }, "autoload": { - "psr-0": { "Tetranz\\Select2EntityBundle": "" } - }, - "target-dir": "Tetranz/Select2EntityBundle", - "extra": { - "branch-alias": { - } + "psr-4": { "Tetranz\\Select2EntityBundle": "" } } } From 97c6159f815a712304124b99d2680bd5e4c9ae68 Mon Sep 17 00:00:00 2001 From: Piotr Antosik Date: Mon, 27 Nov 2017 18:42:33 +0100 Subject: [PATCH 03/17] sf4 --- composer.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/composer.json b/composer.json index 907cd50..de66054 100644 --- a/composer.json +++ b/composer.json @@ -17,5 +17,10 @@ }, "autoload": { "psr-4": { "Tetranz\\Select2EntityBundle": "" } + }, + "extra": { + "branch-alias": { + "dev-master": "2.9.x-dev" + } } } From ee8340c93ca303f0816b95a83792707b11f9fb44 Mon Sep 17 00:00:00 2001 From: Piotr Antosik Date: Mon, 27 Nov 2017 18:45:46 +0100 Subject: [PATCH 04/17] sf4 --- composer.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index de66054..c932270 100644 --- a/composer.json +++ b/composer.json @@ -12,7 +12,9 @@ ], "require": { "php": "^5.5.9 || ^7.0", - "symfony/symfony": "^2.7 || ^3.0 || ^4.0", + "symfony/framework-bundle": "^2.7 || ^3.0 || ^4.0", + "symfony/form": "^2.7 || ^3.0 || ^4.0", + "symfony/templating": "^2.7 || ^3.0 || ^4.0", "doctrine/doctrine-bundle": "^1.3" }, "autoload": { From 76008878fb40cc3f2aade5ff175b8db8cce5cd59 Mon Sep 17 00:00:00 2001 From: Piotr Antosik Date: Mon, 27 Nov 2017 18:46:45 +0100 Subject: [PATCH 05/17] sf4 --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index c932270..8b1e291 100644 --- a/composer.json +++ b/composer.json @@ -18,7 +18,7 @@ "doctrine/doctrine-bundle": "^1.3" }, "autoload": { - "psr-4": { "Tetranz\\Select2EntityBundle": "" } + "psr-4": { "Tetranz\\Select2EntityBundle\\": "" } }, "extra": { "branch-alias": { From cc3be37cb01796fe5e79642c106cbe7697699756 Mon Sep 17 00:00:00 2001 From: Piotr Antosik Date: Mon, 27 Nov 2017 19:40:22 +0100 Subject: [PATCH 06/17] js --- Resources/public/js/select2entity.js | 1 + 1 file changed, 1 insertion(+) diff --git a/Resources/public/js/select2entity.js b/Resources/public/js/select2entity.js index 0ce10f7..48486b8 100644 --- a/Resources/public/js/select2entity.js +++ b/Resources/public/js/select2entity.js @@ -31,6 +31,7 @@ } }, ajax: { + url: $s2.data('data-ajax--url'), transport: function (params, success, failure) { // is caching enabled? if ($s2.data('ajax--cache')) { From f2281ec91aa2bf2923c8a25593b6ae65bc7cee76 Mon Sep 17 00:00:00 2001 From: Piotr Antosik Date: Fri, 29 Dec 2017 16:50:31 +0100 Subject: [PATCH 07/17] reset joined select from count --- Service/AutocompleteService.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Service/AutocompleteService.php b/Service/AutocompleteService.php index 4e11768..c357b82 100644 --- a/Service/AutocompleteService.php +++ b/Service/AutocompleteService.php @@ -69,6 +69,11 @@ public function getAutocompleteResults(Request $request, $type) $cb = $fieldOptions['callback']; $cb($countQB, $request); + //reset joined select from count + $countQB->resetDQLPart('select'); + $countQB + ->addSelect($countQB->expr()->count('e')); + $cb($resultQb, $request); } From 8457f83707efc8c93c1aeb6b303f90fc97f48abd Mon Sep 17 00:00:00 2001 From: Piotr Antosik Date: Fri, 29 Dec 2017 16:53:17 +0100 Subject: [PATCH 08/17] allow form data --- Service/AutocompleteService.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Service/AutocompleteService.php b/Service/AutocompleteService.php index c357b82..4f2cb85 100644 --- a/Service/AutocompleteService.php +++ b/Service/AutocompleteService.php @@ -34,12 +34,13 @@ public function __construct(FormFactoryInterface $formFactory, ManagerRegistry $ /** * @param Request $request * @param string|FormTypeInterface $type + * @param mixed $data * * @return array */ - public function getAutocompleteResults(Request $request, $type) + public function getAutocompleteResults(Request $request, $type, $data = null) { - $form = $this->formFactory->create($type); + $form = $this->formFactory->create($type, $data); $fieldOptions = $form->get($request->get('field_name'))->getConfig()->getOptions(); /** @var EntityRepository $repo */ From 6490c392142e1cf0f530807af7a0df44b16fabd5 Mon Sep 17 00:00:00 2001 From: Derek Chafin Date: Mon, 11 Dec 2017 02:08:31 -0600 Subject: [PATCH 09/17] jQuery adds the data part automatically. --- Resources/public/js/select2entity.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/public/js/select2entity.js b/Resources/public/js/select2entity.js index 48486b8..9fc81ad 100644 --- a/Resources/public/js/select2entity.js +++ b/Resources/public/js/select2entity.js @@ -31,7 +31,7 @@ } }, ajax: { - url: $s2.data('data-ajax--url'), + url: $s2.data('ajax--url'), transport: function (params, success, failure) { // is caching enabled? if ($s2.data('ajax--cache')) { From 3a1fcb927b10cd0b175afc88f03da6962d008a20 Mon Sep 17 00:00:00 2001 From: Piotr Antosik Date: Thu, 19 Apr 2018 12:52:28 +0200 Subject: [PATCH 10/17] Update AutocompleteService.php --- Service/AutocompleteService.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Service/AutocompleteService.php b/Service/AutocompleteService.php index 4f2cb85..6a4c488 100644 --- a/Service/AutocompleteService.php +++ b/Service/AutocompleteService.php @@ -2,7 +2,7 @@ namespace Tetranz\Select2EntityBundle\Service; -use Doctrine\Common\Persistence\ManagerRegistry; +use Symfony\Bridge\Doctrine\RegistryInterface; use Doctrine\ORM\EntityRepository; use Symfony\Component\Form\FormFactoryInterface; use Symfony\Component\Form\FormTypeInterface; @@ -25,7 +25,7 @@ class AutocompleteService * @param FormFactoryInterface $formFactory * @param ManagerRegistry $doctrine */ - public function __construct(FormFactoryInterface $formFactory, ManagerRegistry $doctrine) + public function __construct(FormFactoryInterface $formFactory, RegistryInterface $doctrine) { $this->formFactory = $formFactory; $this->doctrine = $doctrine; From 2faf8d021bb873b97082ad73822e2d978f8d9efa Mon Sep 17 00:00:00 2001 From: Piotr Antosik Date: Tue, 24 Apr 2018 23:48:45 +0200 Subject: [PATCH 11/17] add exclude option --- Service/AutocompleteService.php | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Service/AutocompleteService.php b/Service/AutocompleteService.php index 6a4c488..437fc9e 100644 --- a/Service/AutocompleteService.php +++ b/Service/AutocompleteService.php @@ -29,7 +29,7 @@ public function __construct(FormFactoryInterface $formFactory, RegistryInterface { $this->formFactory = $formFactory; $this->doctrine = $doctrine; - } + } /** * @param Request $request @@ -66,6 +66,14 @@ public function getAutocompleteResults(Request $request, $type, $data = null) ->setFirstResult($offset) ; + if ($request->get('exclude') && $exclude = explode(',', $request->get('exclude'))) { + if (\count($exclude) > 0) { + $resultQb + ->andWhere('e.id NOT IN (:exclude)') + ->setParameter('exclude', $exclude); + } + } + if (is_callable($fieldOptions['callback'])) { $cb = $fieldOptions['callback']; From a889dd82640a5a875615caa9687d2c8516d9913d Mon Sep 17 00:00:00 2001 From: Piotr Antosik Date: Fri, 27 Dec 2019 11:20:05 +0100 Subject: [PATCH 12/17] Sync with main repo --- DependencyInjection/Configuration.php | 33 +++---- .../EntitiesToPropertyTransformer.php | 27 +++-- .../EntityToPropertyTransformer.php | 4 +- Form/Type/Select2EntityType.php | 99 +++++++++---------- Resources/config/services.xml | 2 +- Resources/public/js/select2entity.js | 75 +++++++++----- Resources/views/Form/fields.html.twig | 50 ++++++---- Service/AutocompleteService.php | 1 - composer.json | 14 ++- 9 files changed, 171 insertions(+), 134 deletions(-) diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index d2fd657..5bd9542 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -6,7 +6,7 @@ use Symfony\Component\Config\Definition\ConfigurationInterface; /** - * This is the class that validates and merges configuration from your app/config files + * This is the class that validates and merges configuration from your config/packages files * * To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/extension.html#cookbook-bundles-extension-config-class} */ @@ -17,38 +17,33 @@ class Configuration implements ConfigurationInterface */ public function getConfigTreeBuilder() { - $treeBuilder = new TreeBuilder(); - $rootNode = $treeBuilder->root('tetranz_select2_entity'); + $treeBuilder = new TreeBuilder('tetranz_select2_entity'); + $rootNode = $treeBuilder->getRootNode(); $rootNode ->children() - ->scalarNode('minimum_input_length')->defaultValue(1)->end() - ->scalarNode('scroll')->defaultFalse()->end() - ->scalarNode('page_limit')->defaultValue(10)->end() - ->scalarNode('allow_clear')->defaultFalse()->end() + ->integerNode('minimum_input_length')->min(0)->defaultValue(1)->end() + ->booleanNode('scroll')->defaultFalse()->end() + ->integerNode('page_limit')->defaultValue(10)->end() + ->booleanNode('allow_clear')->defaultFalse()->end() ->arrayNode('allow_add')->addDefaultsIfNotSet() ->children() - ->scalarNode('enabled')->defaultFalse()->end() + ->booleanNode('enabled')->defaultFalse()->end() ->scalarNode('new_tag_text')->defaultValue(' (NEW)')->end() ->scalarNode('new_tag_prefix')->defaultValue('__')->end() ->scalarNode('tag_separators')->defaultValue('[",", " "]')->end() ->end() ->end() - ->scalarNode('delay')->defaultValue(250)->end() + ->integerNode('delay')->defaultValue(250)->min(0)->end() ->scalarNode('language')->defaultValue('en')->end() - ->scalarNode('cache')->defaultTrue()->end() - // default to 1ms for backwards compatibility for older versions where 'cache' is true but the - // user is not aware of the updated caching feature. This way the cache will, by default, not - // be very effective. Realistically this should be like 60000ms (60 seconds). - ->scalarNode('cache_timeout')->defaultValue(1)->end() + ->scalarNode('theme')->defaultValue('default')->end() + ->booleanNode('cache')->defaultTrue()->end() + ->integerNode('cache_timeout')->defaultValue(60000)->min(0)->end() ->scalarNode('width')->defaultNull()->end() - ->scalarNode('object_manager')->defaultValue(1)->end() + ->scalarNode('object_manager')->defaultNull()->end() + ->booleanNode('render_html')->defaultFalse()->end() ->end(); - // Here you should define the parameters that are allowed to - // configure your bundle. See the documentation linked above for - // more information on that topic. - return $treeBuilder; } } diff --git a/Form/DataTransformer/EntitiesToPropertyTransformer.php b/Form/DataTransformer/EntitiesToPropertyTransformer.php index cc5fb7e..67e83c5 100644 --- a/Form/DataTransformer/EntitiesToPropertyTransformer.php +++ b/Form/DataTransformer/EntitiesToPropertyTransformer.php @@ -25,7 +25,7 @@ class EntitiesToPropertyTransformer implements DataTransformerInterface /** @var string */ protected $primaryKey; /** @var string */ - protected $newTaxPrefix; + protected $newTagPrefix; /** @var string */ protected $newTagText; /** @var PropertyAccessor */ @@ -107,21 +107,20 @@ public function reverseTransform($values) } } - try { - // get multiple entities with one query - $entities = $this->em->createQueryBuilder() - ->select('entity') - ->from($this->className, 'entity') - ->where('entity.'.$this->primaryKey.' IN (:ids)') - ->setParameter('ids', $values) - ->getQuery() - ->getResult(); - } - catch (\Exception $ex) { + // get multiple entities with one query + $entities = $this->em->createQueryBuilder() + ->select('entity') + ->from($this->className, 'entity') + ->where('entity.'.$this->primaryKey.' IN (:ids)') + ->setParameter('ids', $values) + ->getQuery() + ->getResult(); + // this will happen if the form submits invalid data - throw new TransformationFailedException('One or more id values are invalid'); + if (count($entities) != count($values)) { + throw new TransformationFailedException('One or more id values are invalid'); } return array_merge($entities, $newObjects); } -} +} \ No newline at end of file diff --git a/Form/DataTransformer/EntityToPropertyTransformer.php b/Form/DataTransformer/EntityToPropertyTransformer.php index bcae71c..bd7ded0 100644 --- a/Form/DataTransformer/EntityToPropertyTransformer.php +++ b/Form/DataTransformer/EntityToPropertyTransformer.php @@ -110,7 +110,7 @@ public function reverseTransform($value) ->getQuery() ->getSingleResult(); } - catch (\Exception $ex) { + catch (\Doctrine\ORM\UnexpectedResultException $ex) { // this will happen if the form submits invalid data throw new TransformationFailedException(sprintf('The choice "%s" does not exist or is not unique', $value)); } @@ -122,4 +122,4 @@ public function reverseTransform($value) return $entity; } -} +} \ No newline at end of file diff --git a/Form/Type/Select2EntityType.php b/Form/Type/Select2EntityType.php index bc681c0..9942882 100644 --- a/Form/Type/Select2EntityType.php +++ b/Form/Type/Select2EntityType.php @@ -2,17 +2,18 @@ namespace Tetranz\Select2EntityBundle\Form\Type; +use Tetranz\Select2EntityBundle\Form\DataTransformer\EntitiesToPropertyTransformer; +use Tetranz\Select2EntityBundle\Form\DataTransformer\EntityToPropertyTransformer; + use Doctrine\Common\Persistence\ObjectManager; +use Doctrine\Common\Persistence\ManagerRegistry; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\DataTransformerInterface; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormInterface; use Symfony\Component\Form\FormView; use Symfony\Component\OptionsResolver\OptionsResolver; -use Symfony\Component\OptionsResolver\OptionsResolverInterface; use Symfony\Component\Routing\RouterInterface; -use Tetranz\Select2EntityBundle\Form\DataTransformer\EntitiesToPropertyTransformer; -use Tetranz\Select2EntityBundle\Form\DataTransformer\EntityToPropertyTransformer; use Symfony\Component\PropertyAccess\PropertyAccess; /** @@ -22,21 +23,24 @@ */ class Select2EntityType extends AbstractType { + /** @var ManagerRegistry */ + protected $registry; /** @var ObjectManager */ protected $em; /** @var RouterInterface */ protected $router; - /** @var array */ + /** @var array */ protected $config; /** - * @param ObjectManager $em - * @param RouterInterface $router - * @param array $config + * @param ManagerRegistry $registry + * @param RouterInterface $router + * @param array $config */ - public function __construct(ObjectManager $em, RouterInterface $router, $config) + public function __construct(ManagerRegistry $registry, RouterInterface $router, $config) { - $this->em = $em; + $this->registry = $registry; + $this->em = $registry->getManager(); $this->router = $router; $this->config = $config; } @@ -44,13 +48,25 @@ public function __construct(ObjectManager $em, RouterInterface $router, $config) public function buildForm(FormBuilderInterface $builder, array $options) { // custom object manager for this entity, override the default entity manager ? - if(isset($options['object_manager'])) { + if (isset($options['object_manager'])) { $em = $options['object_manager']; - if(!$em instanceof ObjectManager) { + if (!$em instanceof ObjectManager) { throw new \Exception('The entity manager \'em\' must be an ObjectManager instance'); } // Use the custom manager instead. $this->em = $em; + } else if (isset($this->config['object_manager'])) { + $em = $this->registry->getManager($this->config['object_manager']); + if (!$em instanceof ObjectManager) { + throw new \Exception('The entity manager \'em\' must be an ObjectManager instance'); + } + $this->em = $em; + } + else { + $manager = $this->registry->getManagerForClass($options['class']); + if ($manager instanceof ObjectManager) { + $this->em = $manager; + } } // add custom data transformer @@ -70,9 +86,8 @@ public function buildForm(FormBuilderInterface $builder, array $options) // add the default data transformer } else { - - $newTagPrefix = isset($options['allow_add']['new_tag_prefix']) ? $options['allow_add']['new_tag_prefix'] : $this->config['allow_add']['new_tag_prefix']; - $newTagText = isset($options['allow_add']['new_tag_text']) ? $options['allow_add']['new_tag_text'] : $this->config['allow_add']['new_tag_text']; + $newTagPrefix = $options['allow_add']['new_tag_prefix'] ?? $this->config['allow_add']['new_tag_prefix']; + $newTagText = $options['allow_add']['new_tag_text'] ?? $this->config['allow_add']['new_tag_text']; $transformer = $options['multiple'] ? new EntitiesToPropertyTransformer($this->em, $options['class'], $options['text_property'], $options['primary_key'], $newTagPrefix, $newTagText) @@ -87,10 +102,10 @@ public function finishView(FormView $view, FormInterface $form, array $options) parent::finishView($view, $form, $options); // make variables available to the view $view->vars['remote_path'] = $options['remote_path'] - ?: $this->router->generate($options['remote_route'], array_merge($options['remote_params'], [ 'page_limit' => $options['page_limit'] ])); + ?: $this->router->generate($options['remote_route'], array_merge($options['remote_params'], ['page_limit' => $options['page_limit'] ])); // merge variable names which are only set per instance with those from yml config - $varNames = array_merge(array('multiple', 'placeholder', 'primary_key', 'autostart'), array_keys($this->config)); + $varNames = array_merge(['multiple', 'placeholder', 'primary_key', 'autostart', 'query_parameters'], array_keys($this->config)); foreach ($varNames as $varName) { $view->vars[$varName] = $options[$varName]; } @@ -109,26 +124,14 @@ public function finishView(FormView $view, FormInterface $form, array $options) //tags options $varNames = array_keys($this->config['allow_add']); foreach ($varNames as $varName) { - if (isset($options['allow_add'][$varName])) { - $view->vars['allow_add'][$varName] = $options['allow_add'][$varName]; - } else { - $view->vars['allow_add'][$varName] = $this->config['allow_add'][$varName]; - } + $view->vars['allow_add'][$varName] = $options['allow_add'][$varName] ?? $this->config['allow_add'][$varName]; } if ($options['multiple']) { $view->vars['full_name'] .= '[]'; } - } - /** - * Added for pre Symfony 2.7 compatibility - * - * @param OptionsResolverInterface $resolver - */ - public function setDefaultOptions(OptionsResolverInterface $resolver) - { - $this->configureOptions($resolver); + $view->vars['class_type'] = $options['class_type']; } /** @@ -136,56 +139,48 @@ public function setDefaultOptions(OptionsResolverInterface $resolver) */ public function configureOptions(OptionsResolver $resolver) { - $resolver->setDefaults( - array( - 'object_manager'=> null, + $resolver->setDefaults([ + 'object_manager' => null, 'class' => null, + 'data_class' => null, 'primary_key' => 'id', 'remote_path' => null, 'remote_route' => null, - 'remote_params' => array(), + 'remote_params' => [], 'multiple' => false, 'compound' => false, 'minimum_input_length' => $this->config['minimum_input_length'], 'page_limit' => $this->config['page_limit'], 'scroll' => $this->config['scroll'], 'allow_clear' => $this->config['allow_clear'], - 'allow_add' => array( + 'allow_add' => [ 'enabled' => $this->config['allow_add']['enabled'], 'new_tag_text' => $this->config['allow_add']['new_tag_text'], 'new_tag_prefix' => $this->config['allow_add']['new_tag_prefix'], 'tag_separators' => $this->config['allow_add']['tag_separators'] - ), + ], 'delay' => $this->config['delay'], 'text_property' => null, - 'placeholder' => '', + 'placeholder' => false, 'language' => $this->config['language'], + 'theme' => $this->config['theme'], 'required' => false, 'cache' => $this->config['cache'], 'cache_timeout' => $this->config['cache_timeout'], 'transformer' => null, 'autostart' => true, - 'width' => isset($this->config['width']) ? $this->config['width'] : null, - 'req_params' => array(), + 'width' => $this->config['width'] ?? null, + 'req_params' => [], 'property' => null, 'callback' => null, - ) + 'class_type' => null, + 'query_parameters' => [], + 'render_html' => $this->config['render_html'] ?? false + ] ); } /** - * pre Symfony 3 compatibility - * - * @return string - */ - public function getName() - { - return $this->getBlockPrefix(); - } - - /** - * Symfony 2.8+ - * * @return string */ public function getBlockPrefix() diff --git a/Resources/config/services.xml b/Resources/config/services.xml index f27223d..4cd57eb 100644 --- a/Resources/config/services.xml +++ b/Resources/config/services.xml @@ -7,7 +7,7 @@ - + %tetranz_select2_entity.config% diff --git a/Resources/public/js/select2entity.js b/Resources/public/js/select2entity.js index 48486b8..6bc1b97 100644 --- a/Resources/public/js/select2entity.js +++ b/Resources/public/js/select2entity.js @@ -1,20 +1,22 @@ -(function( $ ) { +(function ($) { $.fn.select2entity = function (options) { this.each(function () { - var request; + let request; // Keep a reference to the element so we can keep the cache local to this instance and so we can // fetch config settings since select2 doesn't expose its options to the transport method. - var $s2 = $(this), + let $s2 = $(this), limit = $s2.data('page-limit') || 0, scroll = $s2.data('scroll'), prefix = Date.now(), + query_parameters = $s2.data('query-parameters'), + render_html = $s2.data('render-html'), cache = []; - var reqParams = $s2.data('req_params'); + let reqParams = $s2.data('req_params'); if (reqParams) { $.each(reqParams, function (key, value) { - $('*[name="'+value+'"]').on('change', function () { + $('*[name="' + value + '"]').on('change', function () { $s2.val(null); $s2.trigger('change'); }); @@ -22,16 +24,16 @@ } // Deep-merge the options - $s2.select2($.extend(true, { + let mergedOptions = $.extend(true, { // Tags support createTag: function (data) { if ($s2.data('tags') && data.term.length > 0) { - var text = data.term + $s2.data('tags-text'); - return {id: $s2.data('new-tag-prefix') + data.term, text: text}; + let text = data.term + $s2.data('tags-text'); + return { id: $s2.data('new-tag-prefix') + data.term, text: text }; } }, ajax: { - url: $s2.data('data-ajax--url'), + url: $s2.data('ajax--url') || null, transport: function (params, success, failure) { // is caching enabled? if ($s2.data('ajax--cache')) { @@ -39,8 +41,8 @@ var key = prefix + ' page:' + (params.data.page || 1) + ' ' + params.data.q, cacheTimeout = $s2.data('ajax--cacheTimeout'); // no cache entry for 'term' or the cache has timed out? - if (typeof cache[key] == 'undefined' || (cacheTimeout && Date.now() >= cache[key].time)) { - $.ajax(params).fail(failure).done(function (data) { + if (typeof cache[key] === 'undefined' || (cacheTimeout && Date.now() >= cache[key].time)) { + return $.ajax(params).fail(failure).done(function (data) { cache[key] = { data: data, time: cacheTimeout ? Date.now() + cacheTimeout : null @@ -59,18 +61,21 @@ request = $.ajax(params).fail(failure).done(success).always(function () { request = undefined; }); + + return request; } }, data: function (params) { - var ret = { + let ret = { 'q': params.term, - 'field_name': $s2.data('name') + 'field_name': $s2.data('name'), + 'class_type': $s2.data('classtype') }; - var reqParams = $s2.data('req_params'); + let reqParams = $s2.data('req_params'); if (reqParams) { $.each(reqParams, function (key, value) { - ret[key] = $('*[name="'+value+'"]').val() + ret[key] = $('*[name="' + value + '"]').val(); }); } @@ -79,15 +84,26 @@ ret['page'] = params.page || 1; } + if (Array.isArray(query_parameters) || + typeof (query_parameters) === 'object') { + for (var key in query_parameters) { + // prevent overriding required parameters + if (!ret[key]) { + ret[key] = query_parameters[key]; + } + } + } + return ret; }, processResults: function (data, params) { - var results, more = false, response = {}; + let results, more = false, + response = {}; params.page = params.page || 1; if ($.isArray(data)) { results = data; - } else if (typeof data == 'object') { + } else if (typeof data === 'object') { // assume remote result was proper object results = data.results; more = data.more; @@ -97,21 +113,36 @@ } if (scroll) { - response.pagination = {more: more}; + response.pagination = { more: more }; } response.results = results; return response; } } - }, options || {})); + }, options || {}); + if (render_html) { + mergedOptions = $.extend({ + escapeMarkup: function (text) { + return text; + }, + templateResult: function (option) { + return option.html ? option.html : option.text; + }, + templateSelection: function (option) { + return option.text; + } + }, mergedOptions); + } + + $s2.select2(mergedOptions); }); return this; }; -})( jQuery ); +})(jQuery); -(function( $ ) { +(function ($) { $(document).ready(function () { $('.select2entity[data-autostart="true"]').select2entity(); }); -})( jQuery ); +})(jQuery); diff --git a/Resources/views/Form/fields.html.twig b/Resources/views/Form/fields.html.twig index 07bedf7..c508e7e 100644 --- a/Resources/views/Form/fields.html.twig +++ b/Resources/views/Form/fields.html.twig @@ -1,31 +1,38 @@ {% block tetranz_select2entity_widget %} {% set attr = attr|merge({ - 'data-ajax--url':remote_path, + 'data-ajax--url': remote_path, 'data-ajax--cache': cache ? 'true' : 'false', 'data-ajax--cache-timeout': cache_timeout|default(0), - 'data-ajax--delay':delay, - 'data-ajax--data-type':"json", - 'data-language':language, - 'data-minimum-input-length':minimum_input_length, - 'data-placeholder':placeholder|trans({}, translation_domain), - 'data-page-limit':page_limit, - 'data-scroll':scroll ? 'true' : 'false', - 'data-autostart':autostart ? 'true' : 'false', - 'class' : (attr.class|default('') ~ ' select2entity form-control')|trim, - 'data-name' : name|e('html_attr') + 'data-ajax--delay': delay, + 'data-ajax--data-type': "json", + 'data-language' :language, + 'data-theme' :theme, + 'data-minimum-input-length': minimum_input_length, + 'data-placeholder': placeholder|trans({}, translation_domain), + 'data-page-limit': page_limit, + 'data-scroll': scroll ? 'true' : 'false', + 'data-autostart': autostart ? 'true' : 'false', + 'class': (attr.class|default('') ~ ' select2entity form-control')|trim, + 'data-name': name|e('html_attr') }) %} {% if allow_add.enabled %} {% set attr = attr|merge({ - 'data-tags':'true', - 'data-tags-text':allow_add.new_tag_text|trans({}, translation_domain), - 'data-new-tag-prefix':allow_add.new_tag_prefix|trans({}, translation_domain), - 'data-token-separators':allow_add.tag_separators, + 'data-tags': 'true', + 'data-tags-text': allow_add.new_tag_text|trans({}, translation_domain), + 'data-new-tag-prefix': allow_add.new_tag_prefix|trans({}, translation_domain), + 'data-token-separators': allow_add.tag_separators, }) %} {% endif %} {% if multiple %} - {% set attr = attr|merge({'multiple':'multiple'}) %} + {% set attr = attr|merge({'multiple': 'multiple'}) %} + {% endif %} + + {% if query_parameters %} + {% set attr = attr|merge({ + 'data-query-parameters': query_parameters|json_encode + }) %} {% endif %} {% if allow_clear %} @@ -36,8 +43,15 @@ {% set attr = attr|merge({'data-width': width}) %} {% endif %} + {% if render_html %} + {% set attr = attr|merge({'data-render-html': 'true'}) %} + {% endif %} + + {% if class_type %} + {% set attr = attr|merge({'data-classtype': class_type}) %} + {% endif %} - {% spaceless %} + {% apply spaceless %} - {% endspaceless %} + {% endapply %} {% endblock %} diff --git a/Service/AutocompleteService.php b/Service/AutocompleteService.php index 4f2cb85..8d4a5f9 100644 --- a/Service/AutocompleteService.php +++ b/Service/AutocompleteService.php @@ -34,7 +34,6 @@ public function __construct(FormFactoryInterface $formFactory, ManagerRegistry $ /** * @param Request $request * @param string|FormTypeInterface $type - * @param mixed $data * * @return array */ diff --git a/composer.json b/composer.json index 8b1e291..b8ede8d 100644 --- a/composer.json +++ b/composer.json @@ -11,11 +11,15 @@ } ], "require": { - "php": "^5.5.9 || ^7.0", - "symfony/framework-bundle": "^2.7 || ^3.0 || ^4.0", - "symfony/form": "^2.7 || ^3.0 || ^4.0", - "symfony/templating": "^2.7 || ^3.0 || ^4.0", - "doctrine/doctrine-bundle": "^1.3" + "php": ">=7.1.3", + "doctrine/orm": ">=2.4", + "twig/twig": ">=2.9", + "symfony/property-access": ">=4.0", + "symfony/dependency-injection": ">=4.0", + "symfony/http-kernel": ">=4.0", + "symfony/form": ">=4.0", + "symfony/config": ">=4.0", + "symfony/routing": ">=4.0" }, "autoload": { "psr-4": { "Tetranz\\Select2EntityBundle\\": "" } From 8135147bb0b1e3227e5222375f923ac53fd56b46 Mon Sep 17 00:00:00 2001 From: Piotr Antosik Date: Fri, 27 Dec 2019 12:24:17 +0100 Subject: [PATCH 13/17] Sync with main repo --- Service/AutocompleteService.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Service/AutocompleteService.php b/Service/AutocompleteService.php index a2e9f13..c38cfdc 100644 --- a/Service/AutocompleteService.php +++ b/Service/AutocompleteService.php @@ -2,7 +2,7 @@ namespace Tetranz\Select2EntityBundle\Service; -use Symfony\Bridge\Doctrine\RegistryInterface; +use Doctrine\Common\Persistence\ManagerRegistry; use Doctrine\ORM\EntityRepository; use Symfony\Component\Form\FormFactoryInterface; use Symfony\Component\Form\FormTypeInterface; @@ -25,7 +25,7 @@ class AutocompleteService * @param FormFactoryInterface $formFactory * @param ManagerRegistry $doctrine */ - public function __construct(FormFactoryInterface $formFactory, RegistryInterface $doctrine) + public function __construct(FormFactoryInterface $formFactory, ManagerRegistry $doctrine) { $this->formFactory = $formFactory; $this->doctrine = $doctrine; From 7a9e0dfe7fe8252e51386423f4508c8437bc996d Mon Sep 17 00:00:00 2001 From: Piotr Antosik Date: Mon, 30 Aug 2021 22:43:32 +0200 Subject: [PATCH 14/17] Sync with main repo --- Form/DataTransformer/EntitiesToPropertyTransformer.php | 2 +- Form/DataTransformer/EntityToPropertyTransformer.php | 2 +- composer.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Form/DataTransformer/EntitiesToPropertyTransformer.php b/Form/DataTransformer/EntitiesToPropertyTransformer.php index 3774537..d6a9fd8 100644 --- a/Form/DataTransformer/EntitiesToPropertyTransformer.php +++ b/Form/DataTransformer/EntitiesToPropertyTransformer.php @@ -123,4 +123,4 @@ public function reverseTransform($values) return array_merge($entities, $newObjects); } -} \ No newline at end of file +} diff --git a/Form/DataTransformer/EntityToPropertyTransformer.php b/Form/DataTransformer/EntityToPropertyTransformer.php index 9928457..72a30a9 100644 --- a/Form/DataTransformer/EntityToPropertyTransformer.php +++ b/Form/DataTransformer/EntityToPropertyTransformer.php @@ -122,4 +122,4 @@ public function reverseTransform($value) return $entity; } -} \ No newline at end of file +} diff --git a/composer.json b/composer.json index 0e36d31..90f3a35 100644 --- a/composer.json +++ b/composer.json @@ -26,7 +26,7 @@ }, "extra": { "branch-alias": { - "dev-master": "2.9.x-dev" + "dev-master": "3.1.x-dev" } } } From f66aadaa13d2f46855f27c20e1a37fe8970ccd9c Mon Sep 17 00:00:00 2001 From: Piotr Antosik Date: Mon, 30 Aug 2021 22:44:17 +0200 Subject: [PATCH 15/17] Sync with main repo --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 90f3a35..0e36d31 100644 --- a/composer.json +++ b/composer.json @@ -26,7 +26,7 @@ }, "extra": { "branch-alias": { - "dev-master": "3.1.x-dev" + "dev-master": "2.9.x-dev" } } } From cfc93e8b573c12a6c82b1eda6faab3f90764e9b3 Mon Sep 17 00:00:00 2001 From: Piotr Antosik Date: Mon, 30 Aug 2021 22:44:54 +0200 Subject: [PATCH 16/17] Sync with main repo --- composer.json | 1 - 1 file changed, 1 deletion(-) diff --git a/composer.json b/composer.json index 0e36d31..7a23ddd 100644 --- a/composer.json +++ b/composer.json @@ -26,7 +26,6 @@ }, "extra": { "branch-alias": { - "dev-master": "2.9.x-dev" } } } From 15d437036dc542013f6d249eb75af3f9046bfd94 Mon Sep 17 00:00:00 2001 From: Piotr Antosik Date: Wed, 17 Dec 2025 15:10:31 +0100 Subject: [PATCH 17/17] sf 6 compatibility --- DependencyInjection/Configuration.php | 2 +- .../TetranzSelect2EntityExtension.php | 2 +- .../EntitiesToPropertyTransformer.php | 38 ++++++++----------- .../EntityToPropertyTransformer.php | 37 +++++++++--------- Form/Type/Select2EntityType.php | 8 ++-- Service/AutocompleteService.php | 4 +- composer.json | 14 +++---- 7 files changed, 48 insertions(+), 57 deletions(-) diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index 5bd9542..9e267c8 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -15,7 +15,7 @@ class Configuration implements ConfigurationInterface /** * {@inheritDoc} */ - public function getConfigTreeBuilder() + public function getConfigTreeBuilder(): TreeBuilder { $treeBuilder = new TreeBuilder('tetranz_select2_entity'); $rootNode = $treeBuilder->getRootNode(); diff --git a/DependencyInjection/TetranzSelect2EntityExtension.php b/DependencyInjection/TetranzSelect2EntityExtension.php index c1cd2a3..e64c73e 100644 --- a/DependencyInjection/TetranzSelect2EntityExtension.php +++ b/DependencyInjection/TetranzSelect2EntityExtension.php @@ -17,7 +17,7 @@ class TetranzSelect2EntityExtension extends Extension /** * {@inheritDoc} */ - public function load(array $configs, ContainerBuilder $container) + public function load(array $configs, ContainerBuilder $container): void { $configuration = new Configuration(); $config = $this->processConfiguration($configuration, $configs); diff --git a/Form/DataTransformer/EntitiesToPropertyTransformer.php b/Form/DataTransformer/EntitiesToPropertyTransformer.php index d6a9fd8..0fdc381 100644 --- a/Form/DataTransformer/EntitiesToPropertyTransformer.php +++ b/Form/DataTransformer/EntitiesToPropertyTransformer.php @@ -51,31 +51,28 @@ public function __construct(ObjectManager $em, $class, $textProperty = null, $pr /** * Transform initial entities to array - * - * @param mixed $entities - * @return array */ - public function transform($entities) + public function transform(mixed $value): mixed { - if (empty($entities)) { + if (empty($value)) { return array(); } $data = array(); - foreach ($entities as $entity) { + foreach ($value as $entity) { $text = is_null($this->textProperty) ? (string) $entity : $this->accessor->getValue($entity, $this->textProperty); if ($this->em->contains($entity)) { - $value = (string) $this->accessor->getValue($entity, $this->primaryKey); + $transformed = (string) $this->accessor->getValue($entity, $this->primaryKey); } else { - $value = $this->newTagPrefix . $text; + $transformed = $this->newTagPrefix . $text; $text = $text.$this->newTagText; } - $data[$value] = $text; + $data[$transformed] = $text; } return $data; @@ -83,27 +80,24 @@ public function transform($entities) /** * Transform array to a collection of entities - * - * @param array $values - * @return array */ - public function reverseTransform($values) + public function reverseTransform(mixed $value): mixed { - if (!is_array($values) || empty($values)) { - return array(); + if (!is_array($value) || empty($value)) { + return []; } // add new tag entries - $newObjects = array(); + $newObjects = []; $tagPrefixLength = strlen($this->newTagPrefix); - foreach ($values as $key => $value) { - $cleanValue = substr($value, $tagPrefixLength); - $valuePrefix = substr($value, 0, $tagPrefixLength); + foreach ($value as $key => $v) { + $cleanValue = substr($v, $tagPrefixLength); + $valuePrefix = substr($v, 0, $tagPrefixLength); if ($valuePrefix == $this->newTagPrefix) { $object = new $this->className; $this->accessor->setValue($object, $this->textProperty, $cleanValue); $newObjects[] = $object; - unset($values[$key]); + unset($value[$key]); } } @@ -112,12 +106,12 @@ public function reverseTransform($values) ->select('entity') ->from($this->className, 'entity') ->where('entity.'.$this->primaryKey.' IN (:ids)') - ->setParameter('ids', $values) + ->setParameter('ids', $value) ->getQuery() ->getResult(); // this will happen if the form submits invalid data - if (count($entities) != count($values)) { + if (count($entities) != count($value)) { throw new TransformationFailedException('One or more id values are invalid'); } diff --git a/Form/DataTransformer/EntityToPropertyTransformer.php b/Form/DataTransformer/EntityToPropertyTransformer.php index 72a30a9..23368de 100644 --- a/Form/DataTransformer/EntityToPropertyTransformer.php +++ b/Form/DataTransformer/EntityToPropertyTransformer.php @@ -18,19 +18,19 @@ class EntityToPropertyTransformer implements DataTransformerInterface { /** @var ObjectManager */ - protected $em; + protected ObjectManager $em; /** @var string */ - protected $className; + protected string $className; /** @var string */ - protected $textProperty; + protected string $textProperty; /** @var string */ - protected $primaryKey; + protected string $primaryKey; /** @var string */ - protected $newTagPrefix; + protected string $newTagPrefix; /** @var string */ - protected $newTagText; + protected string $newTagText; /** @var PropertyAccessor */ - protected $accessor; + protected PropertyAccessor $accessor; /** * @param ObjectManager $em @@ -52,29 +52,26 @@ public function __construct(ObjectManager $em, $class, $textProperty = null, $pr /** * Transform entity to array - * - * @param mixed $entity - * @return array */ - public function transform($entity) + public function transform(mixed $value): mixed { - $data = array(); - if (empty($entity)) { + $data = []; + if (empty($value)) { return $data; } $text = is_null($this->textProperty) - ? (string) $entity - : $this->accessor->getValue($entity, $this->textProperty); + ? (string) $value + : $this->accessor->getValue($value, $this->textProperty); - if ($this->em->contains($entity)) { - $value = (string) $this->accessor->getValue($entity, $this->primaryKey); + if ($this->em->contains($value)) { + $transformed = (string) $this->accessor->getValue($value, $this->primaryKey); } else { - $value = $this->newTagPrefix . $text; + $transformed = $this->newTagPrefix . $text; $text = $text.$this->newTagText; } - $data[$value] = $text; + $data[$transformed] = $text; return $data; } @@ -85,7 +82,7 @@ public function transform($entity) * @param string $value * @return mixed|null|object */ - public function reverseTransform($value) + public function reverseTransform(mixed $value): mixed { if (empty($value)) { return null; diff --git a/Form/Type/Select2EntityType.php b/Form/Type/Select2EntityType.php index ad7f8d4..a007f17 100644 --- a/Form/Type/Select2EntityType.php +++ b/Form/Type/Select2EntityType.php @@ -45,7 +45,7 @@ public function __construct(ManagerRegistry $registry, RouterInterface $router, $this->config = $config; } - public function buildForm(FormBuilderInterface $builder, array $options) + public function buildForm(FormBuilderInterface $builder, array $options): void { // custom object manager for this entity, override the default entity manager ? if (isset($options['object_manager'])) { @@ -97,7 +97,7 @@ public function buildForm(FormBuilderInterface $builder, array $options) $builder->addViewTransformer($transformer, true); } - public function finishView(FormView $view, FormInterface $form, array $options) + public function finishView(FormView $view, FormInterface $form, array $options): void { parent::finishView($view, $form, $options); // make variables available to the view @@ -137,7 +137,7 @@ public function finishView(FormView $view, FormInterface $form, array $options) /** * @param OptionsResolver $resolver */ - public function configureOptions(OptionsResolver $resolver) + public function configureOptions(OptionsResolver $resolver): void { $resolver->setDefaults([ 'object_manager' => null, @@ -183,7 +183,7 @@ public function configureOptions(OptionsResolver $resolver) /** * @return string */ - public function getBlockPrefix() + public function getBlockPrefix(): string { return 'tetranz_select2entity'; } diff --git a/Service/AutocompleteService.php b/Service/AutocompleteService.php index eb3147e..0c000f5 100644 --- a/Service/AutocompleteService.php +++ b/Service/AutocompleteService.php @@ -14,12 +14,12 @@ class AutocompleteService /** * @var FormFactoryInterface */ - private $formFactory; + private FormFactoryInterface $formFactory; /** * @var ManagerRegistry */ - private $doctrine; + private ManagerRegistry $doctrine; /** * @param FormFactoryInterface $formFactory diff --git a/composer.json b/composer.json index 7a23ddd..542fa3d 100644 --- a/composer.json +++ b/composer.json @@ -11,15 +11,15 @@ } ], "require": { - "php": ">=7.1.3", + "php": ">=8.1", "doctrine/orm": ">=2.4", "twig/twig": ">=2.9", - "symfony/property-access": ">=4.0", - "symfony/dependency-injection": ">=4.0", - "symfony/http-kernel": ">=4.0", - "symfony/form": ">=4.0", - "symfony/config": ">=4.0", - "symfony/routing": ">=4.0" + "symfony/property-access": ">=6.4", + "symfony/dependency-injection": ">=6.4", + "symfony/http-kernel": ">=6.4", + "symfony/form": ">=6.4", + "symfony/config": ">=6.4", + "symfony/routing": ">=6.4" }, "autoload": { "psr-4": { "Tetranz\\Select2EntityBundle\\": "" }