Improve label for aliases in "Filter by Activity" and use of new-style

EntityRepository for ActivityType

* [activity][export] Feature: improve label for aliases in "Filter by activity type"
* [activity][export] DX/Feature: use of an `ActivityTypeRepositoryInterface` instead of the old-style EntityRepository
This commit is contained in:
Julien Fastré 2022-09-21 11:10:35 +02:00
parent 42c395ecc9
commit c059b7700e
9 changed files with 121 additions and 67 deletions

View File

@ -12,6 +12,8 @@ and this project adheres to
<!-- write down unreleased development here --> <!-- write down unreleased development here -->
* [person][export] Fixed: rename the alias for `accompanying_period` to `acp` in filter associated with person * [person][export] Fixed: rename the alias for `accompanying_period` to `acp` in filter associated with person
* [activity][export] Feature: improve label for aliases in "Filter by activity type"
* [activity][export] DX/Feature: use of an `ActivityTypeRepositoryInterface` instead of the old-style EntityRepository
## Test releases ## Test releases

View File

@ -17,7 +17,7 @@ use Chill\ActivityBundle\Form\ActivityType;
use Chill\ActivityBundle\Repository\ActivityACLAwareRepositoryInterface; use Chill\ActivityBundle\Repository\ActivityACLAwareRepositoryInterface;
use Chill\ActivityBundle\Repository\ActivityRepository; use Chill\ActivityBundle\Repository\ActivityRepository;
use Chill\ActivityBundle\Repository\ActivityTypeCategoryRepository; use Chill\ActivityBundle\Repository\ActivityTypeCategoryRepository;
use Chill\ActivityBundle\Repository\ActivityTypeRepository; use Chill\ActivityBundle\Repository\ActivityTypeRepositoryInterface;
use Chill\ActivityBundle\Security\Authorization\ActivityVoter; use Chill\ActivityBundle\Security\Authorization\ActivityVoter;
use Chill\MainBundle\Entity\Embeddable\CommentEmbeddable; use Chill\MainBundle\Entity\Embeddable\CommentEmbeddable;
use Chill\MainBundle\Repository\LocationRepository; use Chill\MainBundle\Repository\LocationRepository;
@ -55,7 +55,7 @@ final class ActivityController extends AbstractController
private ActivityTypeCategoryRepository $activityTypeCategoryRepository; private ActivityTypeCategoryRepository $activityTypeCategoryRepository;
private ActivityTypeRepository $activityTypeRepository; private ActivityTypeRepositoryInterface $activityTypeRepository;
private CenterResolverManagerInterface $centerResolver; private CenterResolverManagerInterface $centerResolver;
@ -75,7 +75,7 @@ final class ActivityController extends AbstractController
public function __construct( public function __construct(
ActivityACLAwareRepositoryInterface $activityACLAwareRepository, ActivityACLAwareRepositoryInterface $activityACLAwareRepository,
ActivityTypeRepository $activityTypeRepository, ActivityTypeRepositoryInterface $activityTypeRepository,
ActivityTypeCategoryRepository $activityTypeCategoryRepository, ActivityTypeCategoryRepository $activityTypeCategoryRepository,
PersonRepository $personRepository, PersonRepository $personRepository,
ThirdPartyRepository $thirdPartyRepository, ThirdPartyRepository $thirdPartyRepository,

View File

@ -516,6 +516,11 @@ class ActivityType
return $this->userVisible; return $this->userVisible;
} }
public function hasCategory(): bool
{
return null !== $this->getCategory();
}
/** /**
* Is active * Is active
* return true if the type is active. * return true if the type is active.

View File

@ -12,7 +12,7 @@ declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Aggregator; namespace Chill\ActivityBundle\Export\Aggregator;
use Chill\ActivityBundle\Export\Declarations; use Chill\ActivityBundle\Export\Declarations;
use Chill\ActivityBundle\Repository\ActivityTypeRepository; use Chill\ActivityBundle\Repository\ActivityTypeRepositoryInterface;
use Chill\MainBundle\Export\AggregatorInterface; use Chill\MainBundle\Export\AggregatorInterface;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface; use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Closure; use Closure;
@ -25,12 +25,12 @@ class ActivityTypeAggregator implements AggregatorInterface
{ {
public const KEY = 'activity_type_aggregator'; public const KEY = 'activity_type_aggregator';
protected ActivityTypeRepository $activityTypeRepository; protected ActivityTypeRepositoryInterface $activityTypeRepository;
protected TranslatableStringHelperInterface $translatableStringHelper; protected TranslatableStringHelperInterface $translatableStringHelper;
public function __construct( public function __construct(
ActivityTypeRepository $activityTypeRepository, ActivityTypeRepositoryInterface $activityTypeRepository,
TranslatableStringHelperInterface $translatableStringHelper TranslatableStringHelperInterface $translatableStringHelper
) { ) {
$this->activityTypeRepository = $activityTypeRepository; $this->activityTypeRepository = $activityTypeRepository;

View File

@ -13,8 +13,9 @@ namespace Chill\ActivityBundle\Export\Filter\ACPFilters;
use Chill\ActivityBundle\Entity\Activity; use Chill\ActivityBundle\Entity\Activity;
use Chill\ActivityBundle\Entity\ActivityType; use Chill\ActivityBundle\Entity\ActivityType;
use Chill\ActivityBundle\Repository\ActivityTypeRepositoryInterface;
use Chill\MainBundle\Export\FilterInterface; use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Templating\TranslatableStringHelper; use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Chill\PersonBundle\Export\Declarations; use Chill\PersonBundle\Export\Declarations;
use Doctrine\ORM\Query\Expr; use Doctrine\ORM\Query\Expr;
use Doctrine\ORM\Query\Expr\Andx; use Doctrine\ORM\Query\Expr\Andx;
@ -22,15 +23,17 @@ use Doctrine\ORM\QueryBuilder;
use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
/**
* TODO merge with ActivityTypeFilter in ChillActivity (!?).
*/
class ActivityTypeFilter implements FilterInterface class ActivityTypeFilter implements FilterInterface
{ {
private TranslatableStringHelper $translatableStringHelper; private ActivityTypeRepositoryInterface $activityTypeRepository;
public function __construct(TranslatableStringHelper $translatableStringHelper) private TranslatableStringHelperInterface $translatableStringHelper;
{
public function __construct(
ActivityTypeRepositoryInterface $activityTypeRepository,
TranslatableStringHelperInterface $translatableStringHelper
) {
$this->activityTypeRepository = $activityTypeRepository;
$this->translatableStringHelper = $translatableStringHelper; $this->translatableStringHelper = $translatableStringHelper;
} }
@ -45,21 +48,10 @@ class ActivityTypeFilter implements FilterInterface
$qb->join(Activity::class, 'activity', Expr\Join::WITH, 'activity.accompanyingPeriod = acp'); $qb->join(Activity::class, 'activity', Expr\Join::WITH, 'activity.accompanyingPeriod = acp');
} }
if (!in_array('acttype', $qb->getAllAliases(), true)) { $clause = $qb->expr()->in('activity.activityType', ':selected_activity_types');
$qb->join('activity.activityType', 'acttype');
}
$where = $qb->getDQLPart('where'); $qb->andWhere($clause);
$clause = $qb->expr()->in('acttype.id', ':activitytypes'); $qb->setParameter('selected_activity_types', $data['types']);
if ($where instanceof Andx) {
$where->add($clause);
} else {
$where = $qb->expr()->andX($clause);
}
$qb->add('where', $where);
$qb->setParameter('activitytypes', $data['accepted_activitytypes']);
} }
public function applyOn() public function applyOn()
@ -71,8 +63,12 @@ class ActivityTypeFilter implements FilterInterface
{ {
$builder->add('accepted_activitytypes', EntityType::class, [ $builder->add('accepted_activitytypes', EntityType::class, [
'class' => ActivityType::class, 'class' => ActivityType::class,
'choices' => $this->activityTypeRepository->findAllActive(),
'choice_label' => function (ActivityType $aty) { 'choice_label' => function (ActivityType $aty) {
return $this->translatableStringHelper->localize($aty->getName()); return
($aty->hasCategory() ? $this->translatableStringHelper->localize($aty->getCategory()->getName()) . ' > ' : '')
.
$this->translatableStringHelper->localize($aty->getName());
}, },
'multiple' => true, 'multiple' => true,
'expanded' => true, 'expanded' => true,
@ -88,7 +84,7 @@ class ActivityTypeFilter implements FilterInterface
} }
return ['Filtered by activity types: only %activitytypes%', [ return ['Filtered by activity types: only %activitytypes%', [
'%activitytypes%' => implode(', ou ', $types), '%activitytypes%' => implode(', ', $types),
]]; ]];
} }

View File

@ -13,7 +13,7 @@ namespace Chill\ActivityBundle\Export\Filter;
use Chill\ActivityBundle\Entity\ActivityType; use Chill\ActivityBundle\Entity\ActivityType;
use Chill\ActivityBundle\Export\Declarations; use Chill\ActivityBundle\Export\Declarations;
use Chill\ActivityBundle\Repository\ActivityTypeRepository; use Chill\ActivityBundle\Repository\ActivityTypeRepositoryInterface;
use Chill\MainBundle\Export\ExportElementValidatedInterface; use Chill\MainBundle\Export\ExportElementValidatedInterface;
use Chill\MainBundle\Export\FilterInterface; use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface; use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
@ -28,13 +28,13 @@ use function count;
class ActivityTypeFilter implements ExportElementValidatedInterface, FilterInterface class ActivityTypeFilter implements ExportElementValidatedInterface, FilterInterface
{ {
protected ActivityTypeRepository $activityTypeRepository; protected ActivityTypeRepositoryInterface $activityTypeRepository;
protected TranslatableStringHelperInterface $translatableStringHelper; protected TranslatableStringHelperInterface $translatableStringHelper;
public function __construct( public function __construct(
TranslatableStringHelperInterface $translatableStringHelper, TranslatableStringHelperInterface $translatableStringHelper,
ActivityTypeRepository $activityTypeRepository ActivityTypeRepositoryInterface $activityTypeRepository
) { ) {
$this->translatableStringHelper = $translatableStringHelper; $this->translatableStringHelper = $translatableStringHelper;
$this->activityTypeRepository = $activityTypeRepository; $this->activityTypeRepository = $activityTypeRepository;
@ -47,16 +47,9 @@ class ActivityTypeFilter implements ExportElementValidatedInterface, FilterInter
public function alterQuery(QueryBuilder $qb, $data) public function alterQuery(QueryBuilder $qb, $data)
{ {
$where = $qb->getDQLPart('where');
$clause = $qb->expr()->in('activity.activityType', ':selected_activity_types'); $clause = $qb->expr()->in('activity.activityType', ':selected_activity_types');
if ($where instanceof Expr\Andx) { $qb->andWhere($clause);
$where->add($clause);
} else {
$where = $qb->expr()->andX($clause);
}
$qb->add('where', $where);
$qb->setParameter('selected_activity_types', $data['types']); $qb->setParameter('selected_activity_types', $data['types']);
} }
@ -68,11 +61,26 @@ class ActivityTypeFilter implements ExportElementValidatedInterface, FilterInter
public function buildForm(FormBuilderInterface $builder) public function buildForm(FormBuilderInterface $builder)
{ {
$builder->add('types', EntityType::class, [ $builder->add('types', EntityType::class, [
'choices' => $this->activityTypeRepository->findAllActive(),
'class' => ActivityType::class, 'class' => ActivityType::class,
'choice_label' => fn (ActivityType $type) => $this->translatableStringHelper->localize($type->getName()), 'choice_label' => function (ActivityType $aty) {
'group_by' => fn (ActivityType $type) => $this->translatableStringHelper->localize($type->getCategory()->getName()), return
($aty->hasCategory() ? $this->translatableStringHelper->localize($aty->getCategory()->getName()) . ' > ' : '')
.
$this->translatableStringHelper->localize($aty->getName());
},
'group_by' => function (ActivityType $type) {
if (!$type->hasCategory()) {
return null;
}
return $this->translatableStringHelper->localize($type->getCategory()->getName());
},
'multiple' => true, 'multiple' => true,
'expanded' => false, 'expanded' => false,
'attr' => [
'class' => 'select2'
]
]); ]);
} }

View File

@ -12,7 +12,7 @@ declare(strict_types=1);
namespace Chill\ActivityBundle\Form\Type; namespace Chill\ActivityBundle\Form\Type;
use Chill\ActivityBundle\Entity\ActivityType; use Chill\ActivityBundle\Entity\ActivityType;
use Chill\ActivityBundle\Repository\ActivityTypeRepository; use Chill\ActivityBundle\Repository\ActivityTypeRepositoryInterface;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface; use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Doctrine\DBAL\Types\Types; use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\QueryBuilder; use Doctrine\ORM\QueryBuilder;
@ -23,37 +23,25 @@ use Symfony\Component\OptionsResolver\OptionsResolver;
class TranslatableActivityType extends AbstractType class TranslatableActivityType extends AbstractType
{ {
protected ActivityTypeRepository $activityTypeRepository; protected ActivityTypeRepositoryInterface $activityTypeRepository;
protected TranslatableStringHelperInterface $translatableStringHelper; protected TranslatableStringHelperInterface $translatableStringHelper;
public function __construct( public function __construct(
TranslatableStringHelperInterface $helper, TranslatableStringHelperInterface $helper,
ActivityTypeRepository $activityTypeRepository ActivityTypeRepositoryInterface $activityTypeRepository
) { ) {
$this->translatableStringHelper = $helper; $this->translatableStringHelper = $helper;
$this->activityTypeRepository = $activityTypeRepository; $this->activityTypeRepository = $activityTypeRepository;
} }
public function buildForm(FormBuilderInterface $builder, array $options)
{
/** @var QueryBuilder $qb */
$qb = $options['query_builder'];
if (true === $options['active_only']) {
$qb->where($qb->expr()->eq('at.active', ':active'));
$qb->setParameter('active', true, Types::BOOLEAN);
}
}
public function configureOptions(OptionsResolver $resolver) public function configureOptions(OptionsResolver $resolver)
{ {
$resolver->setDefaults( $resolver->setDefaults(
[ [
'class' => ActivityType::class, 'class' => ActivityType::class,
'active_only' => true, 'active_only' => true,
'query_builder' => $this->activityTypeRepository 'choices' => $this->activityTypeRepository->findAllActive(),
->createQueryBuilder('at'),
'choice_label' => function (ActivityType $type) { 'choice_label' => function (ActivityType $type) {
return $this->translatableStringHelper->localize($type->getName()); return $this->translatableStringHelper->localize($type->getName());
}, },

View File

@ -13,18 +13,58 @@ namespace Chill\ActivityBundle\Repository;
use Chill\ActivityBundle\Entity\ActivityType; use Chill\ActivityBundle\Entity\ActivityType;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use Doctrine\Persistence\ManagerRegistry; use Doctrine\Persistence\ManagerRegistry;
use UnexpectedValueException;
final class ActivityTypeRepository implements ActivityTypeRepositoryInterface
{
private EntityRepository $repository;
public function __construct(EntityManagerInterface $em)
{
$this->repository = $em->getRepository(ActivityType::class);
}
/** /**
* @method ActivityType|null find($id, $lockMode = null, $lockVersion = null) * @return array|ActivityType[]
* @method ActivityType|null findOneBy(array $criteria, array $orderBy = null)
* @method ActivityType[] findAll()
* @method ActivityType[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/ */
class ActivityTypeRepository extends ServiceEntityRepository public function findAllActive(): array
{ {
public function __construct(ManagerRegistry $registry) return $this->findBy(['active' => true]);
}
public function find($id): ?ActivityType
{ {
parent::__construct($registry, ActivityType::class); return $this->repository->find($id);
} }
/**
* @return array|ActivityType[]
*/
public function findAll(): array
{
return $this->repository->findAll();
}
/**
* @return array|ActivityType[]
*/
public function findBy(array $criteria, ?array $orderBy = null, ?int $limit = null, ?int $offset = null): array
{
return $this->repository->findBy($criteria, $orderBy, $limit, $offset);
}
public function findOneBy(array $criteria): ?ActivityType
{
return $this->repository->findOneBy($criteria);
}
public function getClassName(): string
{
return ActivityType::class;
}
} }

View File

@ -0,0 +1,15 @@
<?php
namespace Chill\ActivityBundle\Repository;
use Chill\ActivityBundle\Entity\ActivityType;
use Doctrine\Persistence\ObjectRepository;
interface ActivityTypeRepositoryInterface extends ObjectRepository
{
/**
* @return array|ActivityType[]
*/
public function findAllActive(): array;
}