refactored search with free search term support (#1064)
This commit is contained in:
48
src/Form/DataTransformer/SearchTermTransformer.php
Normal file
48
src/Form/DataTransformer/SearchTermTransformer.php
Normal file
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Form\DataTransformer;
|
||||
|
||||
use App\Utils\SearchTerm;
|
||||
use Symfony\Component\Form\DataTransformerInterface;
|
||||
use Symfony\Component\Form\Exception\TransformationFailedException;
|
||||
|
||||
class SearchTermTransformer implements DataTransformerInterface
|
||||
{
|
||||
/**
|
||||
* Transforms a SearchTerm object to a string.
|
||||
*
|
||||
* @param SearchTerm|null $searchTerm
|
||||
* @return string
|
||||
*/
|
||||
public function transform($searchTerm)
|
||||
{
|
||||
if (empty($searchTerm) || !$searchTerm instanceof SearchTerm) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $searchTerm->getOriginalSearch();
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms a string to a SearchTerm object.
|
||||
*
|
||||
* @param string $searchTerm
|
||||
* @return SearchTerm|null
|
||||
* @throws TransformationFailedException if object (issue) is not found
|
||||
*/
|
||||
public function reverseTransform($searchTerm)
|
||||
{
|
||||
if (empty($searchTerm)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new SearchTerm($searchTerm);
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@ use Symfony\Component\Form\AbstractTypeExtension;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\Form\FormView;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
/**
|
||||
* Converts normal select boxes into javascript enhanced versions.
|
||||
@@ -49,6 +50,10 @@ class EnhancedChoiceTypeExtension extends AbstractTypeExtension
|
||||
return;
|
||||
}
|
||||
|
||||
if (isset($options['selectpicker']) && false === $options['selectpicker']) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isset($view->vars['attr'])) {
|
||||
$view->vars['attr'] = [];
|
||||
}
|
||||
@@ -58,4 +63,14 @@ class EnhancedChoiceTypeExtension extends AbstractTypeExtension
|
||||
['class' => 'selectpicker', 'data-live-search' => true, 'data-width' => '100%']
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param OptionsResolver $resolver
|
||||
*/
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$resolver->setDefined(['selectpicker']);
|
||||
$resolver->setAllowedTypes('selectpicker', 'boolean');
|
||||
$resolver->setDefault('selectpicker', true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ use App\Form\Type\CustomerType;
|
||||
use App\Form\Type\DateRangeType;
|
||||
use App\Form\Type\PageSizeType;
|
||||
use App\Form\Type\ProjectType;
|
||||
use App\Form\Type\SearchTermType;
|
||||
use App\Form\Type\TagsInputType;
|
||||
use App\Form\Type\UserRoleType;
|
||||
use App\Form\Type\UserType;
|
||||
@@ -50,11 +51,17 @@ abstract class AbstractToolbarForm extends AbstractType
|
||||
return '';
|
||||
}
|
||||
|
||||
protected function getSelectpickerConfig(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function addUserChoice(FormBuilderInterface $builder)
|
||||
{
|
||||
$builder->add('user', UserType::class, [
|
||||
'label' => 'label.user',
|
||||
'required' => false,
|
||||
'selectpicker' => $this->getSelectpickerConfig(),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -64,6 +71,7 @@ abstract class AbstractToolbarForm extends AbstractType
|
||||
'label' => 'label.user',
|
||||
'multiple' => true,
|
||||
'required' => false,
|
||||
'selectpicker' => $this->getSelectpickerConfig(),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -88,6 +96,7 @@ abstract class AbstractToolbarForm extends AbstractType
|
||||
|
||||
return $repo->getQueryBuilderForFormType($query);
|
||||
},
|
||||
'selectpicker' => $this->getSelectpickerConfig(),
|
||||
]);
|
||||
}
|
||||
);
|
||||
@@ -98,7 +107,8 @@ abstract class AbstractToolbarForm extends AbstractType
|
||||
$builder->add('visibility', VisibilityType::class, [
|
||||
'required' => false,
|
||||
'placeholder' => null,
|
||||
'label' => $label
|
||||
'label' => $label,
|
||||
'selectpicker' => $this->getSelectpickerConfig(),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -106,6 +116,7 @@ abstract class AbstractToolbarForm extends AbstractType
|
||||
{
|
||||
$builder->add('pageSize', PageSizeType::class, [
|
||||
'required' => false,
|
||||
'selectpicker' => $this->getSelectpickerConfig(),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -113,6 +124,7 @@ abstract class AbstractToolbarForm extends AbstractType
|
||||
{
|
||||
$builder->add('role', UserRoleType::class, [
|
||||
'required' => false,
|
||||
'selectpicker' => $this->getSelectpickerConfig(),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -149,6 +161,7 @@ abstract class AbstractToolbarForm extends AbstractType
|
||||
|
||||
return $repo->getQueryBuilderForFormType($query);
|
||||
},
|
||||
'selectpicker' => $this->getSelectpickerConfig(),
|
||||
]);
|
||||
}
|
||||
);
|
||||
@@ -177,6 +190,7 @@ abstract class AbstractToolbarForm extends AbstractType
|
||||
|
||||
return $repo->getQueryBuilderForFormType($query);
|
||||
},
|
||||
'selectpicker' => $this->getSelectpickerConfig(),
|
||||
]);
|
||||
}
|
||||
);
|
||||
@@ -196,6 +210,11 @@ abstract class AbstractToolbarForm extends AbstractType
|
||||
]);
|
||||
}
|
||||
|
||||
protected function addSearchTermInputField(FormBuilderInterface $builder)
|
||||
{
|
||||
$builder->add('searchTerm', SearchTermType::class);
|
||||
}
|
||||
|
||||
protected function addTimesheetStateChoice(FormBuilderInterface $builder)
|
||||
{
|
||||
$builder->add('state', ChoiceType::class, [
|
||||
@@ -207,6 +226,7 @@ abstract class AbstractToolbarForm extends AbstractType
|
||||
'entryState.running' => TimesheetQuery::STATE_RUNNING,
|
||||
'entryState.stopped' => TimesheetQuery::STATE_STOPPED
|
||||
],
|
||||
'selectpicker' => $this->getSelectpickerConfig(),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -221,6 +241,7 @@ abstract class AbstractToolbarForm extends AbstractType
|
||||
'entryState.exported' => TimesheetQuery::STATE_EXPORTED,
|
||||
'entryState.not_exported' => TimesheetQuery::STATE_NOT_EXPORTED
|
||||
],
|
||||
'selectpicker' => $this->getSelectpickerConfig(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,8 +24,9 @@ class ActivityToolbarForm extends AbstractToolbarForm
|
||||
*/
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
$this->addPageSizeChoice($builder);
|
||||
$this->addVisibilityChoice($builder);
|
||||
$this->addSearchTermInputField($builder);
|
||||
$this->addCustomerChoice($builder);
|
||||
$this->addProjectChoice($builder);
|
||||
$builder->add('globalsOnly', ChoiceType::class, [
|
||||
'choices' => [
|
||||
'yes' => 1,
|
||||
@@ -34,9 +35,10 @@ class ActivityToolbarForm extends AbstractToolbarForm
|
||||
'placeholder' => null,
|
||||
'required' => false,
|
||||
'label' => 'label.globalsOnly',
|
||||
'selectpicker' => $this->getSelectpickerConfig(),
|
||||
]);
|
||||
$this->addCustomerChoice($builder);
|
||||
$this->addProjectChoice($builder);
|
||||
$this->addVisibilityChoice($builder);
|
||||
$this->addPageSizeChoice($builder);
|
||||
$this->addHiddenPagination($builder);
|
||||
}
|
||||
|
||||
|
||||
@@ -23,8 +23,9 @@ class CustomerToolbarForm extends AbstractToolbarForm
|
||||
*/
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
$this->addPageSizeChoice($builder);
|
||||
$this->addSearchTermInputField($builder);
|
||||
$this->addVisibilityChoice($builder);
|
||||
$this->addPageSizeChoice($builder);
|
||||
$this->addHiddenPagination($builder);
|
||||
}
|
||||
|
||||
|
||||
@@ -21,11 +21,17 @@ use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
*/
|
||||
class ExportToolbarForm extends AbstractToolbarForm
|
||||
{
|
||||
protected function getSelectpickerConfig(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
$this->addSearchTermInputField($builder);
|
||||
$this->addExportStateChoice($builder);
|
||||
$this->addTimesheetStateChoice($builder);
|
||||
$this->addUsersChoice($builder);
|
||||
|
||||
@@ -21,11 +21,17 @@ use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
*/
|
||||
class InvoiceToolbarForm extends AbstractToolbarForm
|
||||
{
|
||||
protected function getSelectpickerConfig(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
$this->addSearchTermInputField($builder);
|
||||
$this->addTemplateChoice($builder);
|
||||
$this->addUsersChoice($builder);
|
||||
$this->addDateRangeChoice($builder);
|
||||
@@ -52,6 +58,7 @@ class InvoiceToolbarForm extends AbstractToolbarForm
|
||||
$builder->add('template', InvoiceTemplateType::class, [
|
||||
'required' => true,
|
||||
'placeholder' => null,
|
||||
'selectpicker' => $this->getSelectpickerConfig(),
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -23,9 +23,10 @@ class ProjectToolbarForm extends AbstractToolbarForm
|
||||
*/
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
$this->addPageSizeChoice($builder);
|
||||
$this->addVisibilityChoice($builder);
|
||||
$this->addSearchTermInputField($builder);
|
||||
$this->addCustomerChoice($builder);
|
||||
$this->addVisibilityChoice($builder);
|
||||
$this->addPageSizeChoice($builder);
|
||||
$this->addHiddenPagination($builder);
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ class TagToolbarForm extends AbstractToolbarForm
|
||||
*/
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
$this->addSearchTermInputField($builder);
|
||||
$this->addPageSizeChoice($builder);
|
||||
$this->addHiddenPagination($builder);
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ class TeamToolbarForm extends AbstractToolbarForm
|
||||
*/
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
$this->addSearchTermInputField($builder);
|
||||
$this->addPageSizeChoice($builder);
|
||||
$this->addHiddenPagination($builder);
|
||||
}
|
||||
|
||||
@@ -23,8 +23,7 @@ class TimesheetToolbarForm extends AbstractToolbarForm
|
||||
*/
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
$this->addTimesheetStateChoice($builder);
|
||||
$this->addPageSizeChoice($builder);
|
||||
$this->addSearchTermInputField($builder);
|
||||
if ($options['include_user']) {
|
||||
$this->addUserChoice($builder);
|
||||
}
|
||||
@@ -33,6 +32,8 @@ class TimesheetToolbarForm extends AbstractToolbarForm
|
||||
$this->addProjectChoice($builder);
|
||||
$this->addActivityChoice($builder);
|
||||
$this->addTagInputField($builder);
|
||||
$this->addTimesheetStateChoice($builder);
|
||||
$this->addPageSizeChoice($builder);
|
||||
$this->addHiddenPagination($builder);
|
||||
}
|
||||
|
||||
|
||||
@@ -23,9 +23,10 @@ class UserToolbarForm extends AbstractToolbarForm
|
||||
*/
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
$this->addPageSizeChoice($builder);
|
||||
$this->addVisibilityChoice($builder, 'label.active');
|
||||
$this->addSearchTermInputField($builder);
|
||||
$this->addUserRoleChoice($builder);
|
||||
$this->addVisibilityChoice($builder, 'label.active');
|
||||
$this->addPageSizeChoice($builder);
|
||||
$this->addHiddenPagination($builder);
|
||||
}
|
||||
|
||||
|
||||
51
src/Form/Type/SearchTermType.php
Normal file
51
src/Form/Type/SearchTermType.php
Normal file
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Form\Type;
|
||||
|
||||
use App\Form\DataTransformer\SearchTermTransformer;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use Symfony\Component\Validator\Constraints\Length;
|
||||
|
||||
class SearchTermType extends AbstractType
|
||||
{
|
||||
/**
|
||||
* @param FormBuilderInterface $builder
|
||||
* @param array $options
|
||||
*/
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
$builder->addModelTransformer(new SearchTermTransformer());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'label' => 'search',
|
||||
'required' => false,
|
||||
'constraints' => [
|
||||
new Length(['min' => 3])
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getParent()
|
||||
{
|
||||
return TextType::class;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user