mirror of
https://github.com/Part-DB/Part-DB-server.git
synced 2025-07-13 03:44:36 +02:00
Cache list of builtin ressource attachments.
This should be a bit faster than searching every time.
This commit is contained in:
parent
3277d98ee2
commit
1395dae6e4
3 changed files with 164 additions and 33 deletions
|
@ -83,7 +83,10 @@ class AttachmentFormType extends AbstractType
|
||||||
$builder->add('url', TextType::class, [
|
$builder->add('url', TextType::class, [
|
||||||
'label' => $this->trans->trans('attachment.edit.url'),
|
'label' => $this->trans->trans('attachment.edit.url'),
|
||||||
'required' => false,
|
'required' => false,
|
||||||
'attr' => ['data-autocomplete' => $this->urlGenerator->generate('typeahead_builtInRessources', ['query' => 'QUERY'])
|
'attr' => [
|
||||||
|
'data-autocomplete' => $this->urlGenerator->generate('typeahead_builtInRessources', ['query' => 'QUERY']),
|
||||||
|
//Disable browser autocomplete
|
||||||
|
'autocomplete' => 'off'
|
||||||
],
|
],
|
||||||
'constraints' => [
|
'constraints' => [
|
||||||
$options['allow_builtins'] ? new UrlOrBuiltin() : new Url()
|
$options['allow_builtins'] ? new UrlOrBuiltin() : new Url()
|
||||||
|
|
|
@ -36,6 +36,7 @@ use App\Services\Attachments\AttachmentPathResolver;
|
||||||
use Symfony\Component\Finder\Finder;
|
use Symfony\Component\Finder\Finder;
|
||||||
use Symfony\Component\HttpKernel\KernelInterface;
|
use Symfony\Component\HttpKernel\KernelInterface;
|
||||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
|
use Symfony\Contracts\Cache\CacheInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This service is used to find builtin attachment ressources
|
* This service is used to find builtin attachment ressources
|
||||||
|
@ -44,64 +45,110 @@ use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
class BuiltinAttachmentsFinder
|
class BuiltinAttachmentsFinder
|
||||||
{
|
{
|
||||||
protected $pathResolver;
|
protected $pathResolver;
|
||||||
|
protected $cache;
|
||||||
|
|
||||||
public function __construct(KernelInterface $kernel, AttachmentPathResolver $pathResolver)
|
public function __construct(CacheInterface $cache, AttachmentPathResolver $pathResolver)
|
||||||
{
|
{
|
||||||
$this->pathResolver = $pathResolver;
|
$this->pathResolver = $pathResolver;
|
||||||
|
$this->cache = $cache;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function configureOptions(OptionsResolver $resolver)
|
protected function configureOptions(OptionsResolver $resolver)
|
||||||
{
|
{
|
||||||
$resolver->setDefaults([
|
$resolver->setDefaults([
|
||||||
'limit' => 15, //Given only 15 entries
|
'limit' => 15, //Given only 15 entries
|
||||||
'filename_filter' => '', //Filter the filenames. For example *.jpg to only get jpegs. Can also be an array
|
//'allowed_extensions' => [], //Filter the filenames. For example ['jpg', 'jpeg'] to only get jpegs.
|
||||||
'placeholders' => Attachment::BUILTIN_PLACEHOLDER, //By default use all builtin ressources
|
//'placeholders' => Attachment::BUILTIN_PLACEHOLDER, //By default use all builtin ressources,
|
||||||
|
'empty_returns_all' => false //Return the whole list of ressources when empty keyword is given
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function find(string $keyword, array $options = []) : array
|
/**
|
||||||
|
* Returns a list of all builtin ressources.
|
||||||
|
* The array is a list of the relative filenames using the %PLACEHOLDERS%.
|
||||||
|
* The list contains the files from all configured valid ressoureces.
|
||||||
|
* @return array The list of the ressources, or an empty array if an error happened.
|
||||||
|
*/
|
||||||
|
public function getListOfRessources() : array
|
||||||
{
|
{
|
||||||
$finder = new Finder();
|
try {
|
||||||
|
return $this->cache->get('attachment_builtin_ressources', function () {
|
||||||
|
$results = [];
|
||||||
|
|
||||||
|
$finder = new Finder();
|
||||||
|
//We search only files
|
||||||
|
$finder->files();
|
||||||
|
//Add the folder for each placeholder
|
||||||
|
foreach (Attachment::BUILTIN_PLACEHOLDER as $placeholder) {
|
||||||
|
$tmp = $this->pathResolver->placeholderToRealPath($placeholder);
|
||||||
|
//Ignore invalid/deactivated placeholders:
|
||||||
|
if ($tmp !== null) {
|
||||||
|
$finder->in($tmp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($finder as $file) {
|
||||||
|
$results[] = $this->pathResolver->realPathToPlaceholder($file->getPathname());
|
||||||
|
}
|
||||||
|
|
||||||
|
//Sort results ascending
|
||||||
|
sort($results);
|
||||||
|
|
||||||
|
return $results;
|
||||||
|
});
|
||||||
|
} catch (\Psr\Cache\InvalidArgumentException $ex) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find all ressources which are matching the given keyword and the specified options
|
||||||
|
* @param string $keyword The keyword you want to search for.
|
||||||
|
* @param array $options Here you can specify some options (see configureOptions for list of options)
|
||||||
|
* @param array|null $base_list The list from which should be used as base for filtering.
|
||||||
|
* @return array The list of the results matching the specified keyword and options
|
||||||
|
*/
|
||||||
|
public function find(string $keyword, array $options = [], ?array $base_list = []) : array
|
||||||
|
{
|
||||||
|
if (empty($base_list)) {
|
||||||
|
$base_list = $this->getListOfRessources();
|
||||||
|
}
|
||||||
|
|
||||||
$resolver = new OptionsResolver();
|
$resolver = new OptionsResolver();
|
||||||
$this->configureOptions($resolver);
|
$this->configureOptions($resolver);
|
||||||
$options = $resolver->resolve($options);
|
$options = $resolver->resolve($options);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
if (empty($options['placeholders'])) {
|
if (empty($options['placeholders'])) {
|
||||||
return [];
|
return [];
|
||||||
}
|
} */
|
||||||
|
|
||||||
//We search only files
|
if ($keyword === '') {
|
||||||
$finder->files();
|
if ($options['empty_returns_all']) {
|
||||||
//Add the folder for each placeholder
|
$keyword = '.*';
|
||||||
foreach ($options['placeholders'] as $placeholder) {
|
} else {
|
||||||
$tmp = $this->pathResolver->placeholderToRealPath($placeholder);
|
return [];
|
||||||
//Ignore invalid/deactivated placeholders:
|
|
||||||
if ($tmp !== null) {
|
|
||||||
$finder->in($tmp);
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
//Quote all values in the keyword (user is not allowed to use regex characters)
|
||||||
|
$keyword = preg_quote($keyword, '/');
|
||||||
}
|
}
|
||||||
|
|
||||||
//Apply filter if needed
|
/*TODO: Implement placheolder and extension filter */
|
||||||
if (!empty($options['filename_filter'])) {
|
/* if (!empty($options['allowed_extensions'])) {
|
||||||
$finder->name($options['filename_filter']);
|
$keyword .= "\.(";
|
||||||
}
|
foreach ($options['allowed_extensions'] as $extension) {
|
||||||
|
$keyword .= preg_quote($extension, '/') . '|';
|
||||||
$finder->path($keyword);
|
|
||||||
|
|
||||||
$arr = [];
|
|
||||||
|
|
||||||
$limit = $options['limit'];
|
|
||||||
|
|
||||||
foreach ($finder as $file) {
|
|
||||||
if ($limit <= 0) {
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
$arr[] = $this->pathResolver->realPathToPlaceholder($file->getPathname());
|
$keyword .= ')$';
|
||||||
$limit--;
|
} */
|
||||||
}
|
|
||||||
|
|
||||||
return $arr;
|
//Ignore case
|
||||||
|
$regex = '/.*' . $keyword . '.*/i';
|
||||||
|
|
||||||
|
return preg_grep($regex, $base_list);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
81
tests/Services/Attachments/BuiltinAttachmentsFinderTest.php
Normal file
81
tests/Services/Attachments/BuiltinAttachmentsFinderTest.php
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* part-db version 0.1
|
||||||
|
* Copyright (C) 2005 Christoph Lechner
|
||||||
|
* http://www.cl-projects.de/
|
||||||
|
*
|
||||||
|
* part-db version 0.2+
|
||||||
|
* Copyright (C) 2009 K. Jacobs and others (see authors.php)
|
||||||
|
* http://code.google.com/p/part-db/
|
||||||
|
*
|
||||||
|
* Part-DB Version 0.4+
|
||||||
|
* Copyright (C) 2016 - 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\Tests\Services\Attachments;
|
||||||
|
|
||||||
|
use App\Services\BuiltinAttachmentsFinder;
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
|
||||||
|
|
||||||
|
class BuiltinAttachmentsFinderTest extends WebTestCase
|
||||||
|
{
|
||||||
|
/** @var BuiltinAttachmentsFinder */
|
||||||
|
protected static $service;
|
||||||
|
|
||||||
|
protected static $mock_list = [
|
||||||
|
'%FOOTPRINTS%/test/test.jpg', '%FOOTPRINTS%/test/test.png', '%FOOTPRINTS%/123.jpg', '%FOOTPRINTS%/123.jpeg',
|
||||||
|
'%FOOTPRINTS_3D%/test.jpg', '%FOOTPRINTS_3D%/hallo.txt'
|
||||||
|
];
|
||||||
|
|
||||||
|
public static function setUpBeforeClass()
|
||||||
|
{
|
||||||
|
//Get an service instance.
|
||||||
|
self::bootKernel();
|
||||||
|
self::$service = self::$container->get(BuiltinAttachmentsFinder::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function dataProvider()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
//No value should return empty array
|
||||||
|
['', [], []],
|
||||||
|
['', ['empty_returns_all' => true], static::$mock_list],
|
||||||
|
//Basic search for keyword
|
||||||
|
['test', [], ['%FOOTPRINTS%/test/test.jpg', '%FOOTPRINTS%/test/test.png', '%FOOTPRINTS_3D%/test.jpg']],
|
||||||
|
['%FOOTPRINTS_3D%', [], ['%FOOTPRINTS_3D%/test.jpg', '%FOOTPRINTS_3D%/hallo.txt']],
|
||||||
|
['.txt', [], ['%FOOTPRINTS_3D%/hallo.txt'] ],
|
||||||
|
//Filter extensions
|
||||||
|
//['test', ['allowed_extensions' => ['jpeg', 'jpg']], ['%FOOTPRINTS%/test/test.jpg', '%FOOTPRINTS%/123.jpeg', '%FOOTPRINTS_3D%/test.jpg']],
|
||||||
|
//['test.jpg', ['allowed_extensions' => ['jpeg', 'jpg']], ['%FOOTPRINTS%/test/test.jpg', '%FOOTPRINTS_3D%/test.jpg']]
|
||||||
|
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider dataProvider
|
||||||
|
*/
|
||||||
|
public function testFind($keyword, $options, $expected)
|
||||||
|
{
|
||||||
|
$value = static::$service->find($keyword, $options, static::$mock_list);
|
||||||
|
//$this->assertEquals($expected, static::$service->find($keyword, $options, static::$mock_list));
|
||||||
|
$this->assertEquals([], array_diff($value, $expected), 'Additional');
|
||||||
|
$this->assertEquals([], array_diff($expected, $value), 'Missing:');
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue