mirror of
https://github.com/Part-DB/Part-DB-server.git
synced 2025-06-28 12:40:08 +02:00
Added autocomplete for part tags input.
This commit is contained in:
parent
bddd5b758a
commit
7a5a2f65f9
4 changed files with 153 additions and 5 deletions
|
@ -231,9 +231,38 @@ $(document).on("ajaxUI:reload", function () {
|
|||
$(".file").fileinput();
|
||||
});
|
||||
|
||||
$(document).on("ajaxUI:reload", function () {
|
||||
//@ts-ignore
|
||||
$("input[data-role='tagsinput']").tagsinput();
|
||||
$(document).on("ajaxUI:start ajaxUI:reload", function () {
|
||||
$('input.tagsinput').each(function() {
|
||||
|
||||
//Use typeahead if an autocomplete url was specified.
|
||||
if($(this).data('autocomplete')) {
|
||||
|
||||
//@ts-ignore
|
||||
var engine = new Bloodhound({
|
||||
//@ts-ignore
|
||||
datumTokenizer: Bloodhound.tokenizers.obj.whitespace(''),
|
||||
//@ts-ignore
|
||||
queryTokenizer: Bloodhound.tokenizers.obj.whitespace(''),
|
||||
remote: {
|
||||
url: $(this).data('autocomplete'),
|
||||
wildcard: 'QUERY'
|
||||
}
|
||||
});
|
||||
|
||||
//@ts-ignore
|
||||
$(this).tagsinput({
|
||||
typeaheadjs: {
|
||||
name: 'tags',
|
||||
source: engine.ttAdapter()
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
} else { //Init tagsinput without typeahead
|
||||
//@ts-ignore
|
||||
$(this).tagsinput();
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
/**
|
||||
|
|
|
@ -24,6 +24,7 @@ namespace App\Controller;
|
|||
|
||||
|
||||
use App\Services\Attachments\BuiltinAttachmentsFinder;
|
||||
use App\Services\TagFinder;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
@ -47,6 +48,24 @@ class TypeaheadController extends AbstractController
|
|||
$array = $finder->find($query);
|
||||
|
||||
|
||||
$normalizers = [
|
||||
new ObjectNormalizer()
|
||||
];
|
||||
$encoders = [
|
||||
new JsonEncoder()
|
||||
];
|
||||
$serializer = new Serializer($normalizers, $encoders);
|
||||
$data = $serializer->serialize($array, 'json');
|
||||
return new JsonResponse($data, 200, [], true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/tags/search/{query}", name="typeahead_tags", requirements={"query"= ".+"})
|
||||
*/
|
||||
public function tags(string $query, TagFinder $finder)
|
||||
{
|
||||
$array = $finder->searchTags($query);
|
||||
|
||||
$normalizers = [
|
||||
new ObjectNormalizer()
|
||||
];
|
||||
|
|
|
@ -51,6 +51,7 @@ use Symfony\Component\Form\Extension\Core\Type\ResetType;
|
|||
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||
use Symfony\Component\Security\Core\Security;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
use function foo\func;
|
||||
|
@ -59,11 +60,13 @@ class PartBaseType extends AbstractType
|
|||
{
|
||||
protected $security;
|
||||
protected $trans;
|
||||
protected $urlGenerator;
|
||||
|
||||
public function __construct(Security $security, TranslatorInterface $trans)
|
||||
public function __construct(Security $security, TranslatorInterface $trans, UrlGeneratorInterface $urlGenerator)
|
||||
{
|
||||
$this->security = $security;
|
||||
$this->trans = $trans;
|
||||
$this->urlGenerator = $urlGenerator;
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
|
@ -119,7 +122,9 @@ class PartBaseType extends AbstractType
|
|||
'required' => false,
|
||||
'label' => $this->trans->trans('part.edit.tags'),
|
||||
'empty_data' => "",
|
||||
'attr' => ['data-role' => 'tagsinput'],
|
||||
'attr' => [
|
||||
'class' => 'tagsinput',
|
||||
'data-autocomplete' => $this->urlGenerator->generate('typeahead_tags', ['query' => 'QUERY']),],
|
||||
'disabled' => !$this->security->isGranted('tags.edit', $part)
|
||||
]);
|
||||
|
||||
|
|
95
src/Services/TagFinder.php
Normal file
95
src/Services/TagFinder.php
Normal file
|
@ -0,0 +1,95 @@
|
|||
<?php
|
||||
/**
|
||||
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony)
|
||||
*
|
||||
* Copyright (C) 2019 Jan Böhmer (https://github.com/jbtronics)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*
|
||||
*/
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
|
||||
use App\Entity\Parts\Part;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
/**
|
||||
* A service related for searching for tags. Mostly useful for autocomplete reasons.
|
||||
* @package App\Services
|
||||
*/
|
||||
class TagFinder
|
||||
{
|
||||
protected $em;
|
||||
|
||||
public function __construct(EntityManagerInterface $entityManager)
|
||||
{
|
||||
$this->em = $entityManager;
|
||||
}
|
||||
|
||||
protected function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'query_limit' => 75,
|
||||
'return_limit' => 25,
|
||||
'min_keyword_length' => 3
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Search tags that begins with the certain keyword.
|
||||
* @param string $keyword The keyword the tag must begin with
|
||||
* @param array $options Some options specifying the search behavior. See configureOptions for possible options.
|
||||
* @return string[] An array containing the tags that match the given keyword.
|
||||
*/
|
||||
public function searchTags(string $keyword, array $options = [])
|
||||
{
|
||||
$results = [];
|
||||
$keyword_regex = '/^' . preg_quote($keyword, '/') . '/';
|
||||
|
||||
$resolver = new OptionsResolver();
|
||||
$this->configureOptions($resolver);
|
||||
|
||||
$options = $resolver->resolve($options);
|
||||
|
||||
//If the keyword is too short we will get to much results, which takes too much time...
|
||||
if (mb_strlen($keyword) < $options['min_keyword_length']) {
|
||||
return [];
|
||||
}
|
||||
|
||||
//Build a query to get all
|
||||
$qb = $this->em->createQueryBuilder();
|
||||
|
||||
$qb->select('p.tags')
|
||||
->from(Part::class, 'p')
|
||||
->where("p.tags LIKE ?1")
|
||||
->setMaxResults($options['query_limit'])
|
||||
//->orderBy('RAND()')
|
||||
->setParameter(1, '%' . $keyword . '%');
|
||||
|
||||
$possible_tags = $qb->getQuery()->getArrayResult();
|
||||
|
||||
//Iterate over each possible tags (which are comma separated) and extract tags which match our keyword
|
||||
foreach ($possible_tags as $tags) {
|
||||
$tags = explode(',', $tags['tags']);
|
||||
$results = array_merge($results, preg_grep($keyword_regex, $tags));
|
||||
}
|
||||
|
||||
$results = array_unique($results);
|
||||
//Limit the returned tag count to specified value.
|
||||
return array_slice($results, 0, $options['return_limit']);
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue