added tags for timesheets (#604)
This commit is contained in:
82
src/Form/DataTransformer/TagArrayToStringTransformer.php
Normal file
82
src/Form/DataTransformer/TagArrayToStringTransformer.php
Normal file
@@ -0,0 +1,82 @@
|
||||
<?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\Entity\Tag;
|
||||
use App\Repository\TagRepository;
|
||||
use Symfony\Component\Form\DataTransformerInterface;
|
||||
use Symfony\Component\Form\Exception\TransformationFailedException;
|
||||
|
||||
class TagArrayToStringTransformer implements DataTransformerInterface
|
||||
{
|
||||
/**
|
||||
* @var TagRepository
|
||||
*/
|
||||
private $tagRepository;
|
||||
|
||||
/**
|
||||
* @param TagRepository $tagRepository
|
||||
*/
|
||||
public function __construct(TagRepository $tagRepository)
|
||||
{
|
||||
$this->tagRepository = $tagRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms an array of tags to a string.
|
||||
*
|
||||
* @param Tag[]|null $tags
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function transform($tags): string
|
||||
{
|
||||
if (empty($tags)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return implode(', ', $tags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms a string to an array of tags.
|
||||
*
|
||||
* @param string $stringOfTags
|
||||
*
|
||||
* @return Tag[]
|
||||
* @throws TransformationFailedException if object (issue) is not found
|
||||
*/
|
||||
public function reverseTransform($stringOfTags): array
|
||||
{
|
||||
// check for empty tag list
|
||||
if (empty($stringOfTags)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$names = array_filter(array_unique(array_map('trim', explode(',', $stringOfTags))));
|
||||
|
||||
// Get the current tags and find the new ones that should be created
|
||||
$tags = $this->tagRepository->findBy(['name' => $names]);
|
||||
|
||||
$newNames = array_diff($names, $tags);
|
||||
foreach ($newNames as $name) {
|
||||
$tag = new Tag();
|
||||
$tag->setName($name);
|
||||
$tags[] = $tag;
|
||||
|
||||
// There's no need to persist these new tags because Doctrine does that automatically
|
||||
// thanks to the cascade={"persist"} option in the App\Entity\Timesheet::$tags property.
|
||||
}
|
||||
|
||||
// Return an array of tags to transform them back into a Doctrine Collection.
|
||||
// See Symfony\Bridge\Doctrine\Form\DataTransformer\CollectionToArrayTransformer::reverseTransform()
|
||||
return $tags;
|
||||
}
|
||||
}
|
||||
@@ -92,6 +92,5 @@ class SelectWithApiDataExtension extends AbstractTypeExtension
|
||||
{
|
||||
$resolver->setDefined(['api_data']);
|
||||
$resolver->setAllowedTypes('api_data', 'array');
|
||||
//$resolver->setDefault('api_data', []);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ use App\Form\Type\DurationType;
|
||||
use App\Form\Type\FixedRateType;
|
||||
use App\Form\Type\HourlyRateType;
|
||||
use App\Form\Type\ProjectType;
|
||||
use App\Form\Type\TagsInputType;
|
||||
use App\Form\Type\UserType;
|
||||
use App\Form\Type\YesNoType;
|
||||
use App\Repository\ActivityRepository;
|
||||
@@ -250,8 +251,17 @@ class TimesheetEditForm extends AbstractType
|
||||
->add('description', TextareaType::class, [
|
||||
'label' => 'label.description',
|
||||
'required' => false,
|
||||
])
|
||||
;
|
||||
]);
|
||||
|
||||
$builder
|
||||
->add('tags', TagsInputType::class, [
|
||||
// documentation is for NelmioApiDocBundle
|
||||
'documentation' => [
|
||||
'type' => 'text',
|
||||
'description' => 'Tags for timesheet entry',
|
||||
],
|
||||
'required' => false,
|
||||
]);
|
||||
|
||||
if ($options['include_rate']) {
|
||||
$builder
|
||||
|
||||
@@ -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\TagsInputType;
|
||||
use App\Form\Type\UserRoleType;
|
||||
use App\Form\Type\UserType;
|
||||
use App\Form\Type\VisibilityType;
|
||||
@@ -208,4 +209,14 @@ abstract class AbstractToolbarForm extends AbstractType
|
||||
'empty_data' => 1
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param FormBuilderInterface $builder
|
||||
*/
|
||||
protected function addTagInputField(FormBuilderInterface $builder)
|
||||
{
|
||||
$builder->add('tags', TagsInputType::class, [
|
||||
'required' => false
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ class ExportToolbarForm extends AbstractToolbarForm
|
||||
$this->addProjectChoice($builder);
|
||||
$this->addActivityChoice($builder);
|
||||
$this->addExportType($builder);
|
||||
$this->addTagInputField($builder);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -30,6 +30,7 @@ class InvoiceToolbarForm extends AbstractToolbarForm
|
||||
$this->addCustomerChoice($builder);
|
||||
$this->addProjectChoice($builder);
|
||||
$this->addActivityChoice($builder);
|
||||
$this->addTagInputField($builder);
|
||||
}
|
||||
|
||||
protected function addTemplateChoice(FormBuilderInterface $builder)
|
||||
|
||||
37
src/Form/Toolbar/TagToolbarForm.php
Normal file
37
src/Form/Toolbar/TagToolbarForm.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?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\Toolbar;
|
||||
|
||||
use App\Repository\Query\TagQuery;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
class TagToolbarForm extends AbstractToolbarForm
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
$this->addPageSizeChoice($builder);
|
||||
$this->addHiddenPagination($builder);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'data_class' => TagQuery::class,
|
||||
'csrf_protection' => false,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -33,6 +33,7 @@ class TimesheetToolbarForm extends AbstractToolbarForm
|
||||
$this->addCustomerChoice($builder);
|
||||
$this->addProjectChoice($builder);
|
||||
$this->addActivityChoice($builder);
|
||||
$this->addTagInputField($builder);
|
||||
$this->addHiddenPagination($builder);
|
||||
}
|
||||
|
||||
|
||||
77
src/Form/Type/TagsInputType.php
Normal file
77
src/Form/Type/TagsInputType.php
Normal file
@@ -0,0 +1,77 @@
|
||||
<?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\TagArrayToStringTransformer;
|
||||
use Symfony\Bridge\Doctrine\Form\DataTransformer\CollectionToArrayTransformer;
|
||||
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\Routing\Generator\UrlGeneratorInterface;
|
||||
|
||||
/**
|
||||
* Custom form field type to enter tags or use one of autocompleted field
|
||||
*/
|
||||
class TagsInputType extends AbstractType
|
||||
{
|
||||
/**
|
||||
* @var TagArrayToStringTransformer
|
||||
*/
|
||||
private $transformer;
|
||||
|
||||
/**
|
||||
* @var UrlGeneratorInterface
|
||||
*/
|
||||
private $router;
|
||||
|
||||
/**
|
||||
* @param TagArrayToStringTransformer $transformer
|
||||
* @param UrlGeneratorInterface $router
|
||||
*/
|
||||
public function __construct(TagArrayToStringTransformer $transformer, UrlGeneratorInterface $router)
|
||||
{
|
||||
$this->transformer = $transformer;
|
||||
$this->router = $router;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
$builder
|
||||
->addModelTransformer(new CollectionToArrayTransformer(), true)
|
||||
->addModelTransformer($this->transformer, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'label' => 'label.tag',
|
||||
'attr' => [
|
||||
'data-autocomplete-url' => $this->router->generate('get_tags'),
|
||||
'class' => 'js-autocomplete',
|
||||
'autocomplete' => 'off',
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getParent()
|
||||
{
|
||||
return TextType::class;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user