mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-06-07 18:44:08 +00:00
Merge branch 'issue120_filter_social_actions' into 'master'
Add filter to social actions See merge request Chill-Projet/chill-bundles!570
This commit is contained in:
commit
e6da727a11
5
.changes/unreleased/Feature-20230706-134010.yaml
Normal file
5
.changes/unreleased/Feature-20230706-134010.yaml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
kind: Feature
|
||||||
|
body: Adding OrderFilter to the list of social actions.
|
||||||
|
time: 2023-07-06T13:40:10.339001208+02:00
|
||||||
|
custom:
|
||||||
|
Issue: "120"
|
@ -14,19 +14,11 @@ namespace Chill\MainBundle\Templating\Listing;
|
|||||||
use Chill\MainBundle\Entity\User;
|
use Chill\MainBundle\Entity\User;
|
||||||
use Chill\MainBundle\Form\Type\Listing\FilterOrderType;
|
use Chill\MainBundle\Form\Type\Listing\FilterOrderType;
|
||||||
use DateTimeImmutable;
|
use DateTimeImmutable;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\FormType;
|
|
||||||
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
|
|
||||||
use Symfony\Component\Form\FormFactoryInterface;
|
use Symfony\Component\Form\FormFactoryInterface;
|
||||||
use Symfony\Component\Form\FormInterface;
|
use Symfony\Component\Form\FormInterface;
|
||||||
use Symfony\Component\HttpFoundation\RequestStack;
|
use Symfony\Component\HttpFoundation\RequestStack;
|
||||||
|
|
||||||
use Symfony\Component\PropertyAccess\PropertyAccessor;
|
|
||||||
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
|
|
||||||
use Symfony\Component\PropertyAccess\PropertyPath;
|
|
||||||
use Symfony\Component\PropertyAccess\PropertyPathInterface;
|
|
||||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
|
||||||
use function array_merge;
|
use function array_merge;
|
||||||
use function count;
|
|
||||||
|
|
||||||
final class FilterOrderHelper
|
final class FilterOrderHelper
|
||||||
{
|
{
|
||||||
@ -54,7 +46,6 @@ final class FilterOrderHelper
|
|||||||
*/
|
*/
|
||||||
private array $entityChoices = [];
|
private array $entityChoices = [];
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var array<string, array{label: string, options: array}>
|
* @var array<string, array{label: string, options: array}>
|
||||||
*/
|
*/
|
||||||
|
@ -11,45 +11,39 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Chill\PersonBundle\Controller;
|
namespace Chill\PersonBundle\Controller;
|
||||||
|
|
||||||
|
use Chill\MainBundle\Entity\UserJob;
|
||||||
use Chill\MainBundle\Pagination\PaginatorFactory;
|
use Chill\MainBundle\Pagination\PaginatorFactory;
|
||||||
|
use Chill\MainBundle\Templating\Listing\FilterOrderHelper;
|
||||||
|
use Chill\MainBundle\Templating\Listing\FilterOrderHelperFactoryInterface;
|
||||||
|
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||||
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork;
|
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork;
|
||||||
|
use Chill\PersonBundle\Entity\Person;
|
||||||
|
use Chill\PersonBundle\Entity\SocialWork\SocialAction;
|
||||||
use Chill\PersonBundle\Repository\AccompanyingPeriod\AccompanyingPeriodWorkRepository;
|
use Chill\PersonBundle\Repository\AccompanyingPeriod\AccompanyingPeriodWorkRepository;
|
||||||
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodWorkVoter;
|
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodWorkVoter;
|
||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
|
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
|
||||||
use Symfony\Component\Form\Form;
|
use Symfony\Component\Form\Form;
|
||||||
|
use Symfony\Component\Form\FormInterface;
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
use Symfony\Component\Routing\Annotation\Route;
|
use Symfony\Component\Routing\Annotation\Route;
|
||||||
use Symfony\Component\Serializer\SerializerInterface;
|
use Symfony\Component\Serializer\SerializerInterface;
|
||||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||||
|
|
||||||
class AccompanyingCourseWorkController extends AbstractController
|
final class AccompanyingCourseWorkController extends AbstractController
|
||||||
{
|
{
|
||||||
private LoggerInterface $chillLogger;
|
|
||||||
|
|
||||||
private PaginatorFactory $paginator;
|
|
||||||
|
|
||||||
private SerializerInterface $serializer;
|
|
||||||
|
|
||||||
private TranslatorInterface $trans;
|
|
||||||
|
|
||||||
private AccompanyingPeriodWorkRepository $workRepository;
|
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
TranslatorInterface $trans,
|
private readonly TranslatorInterface $trans,
|
||||||
SerializerInterface $serializer,
|
private readonly SerializerInterface $serializer,
|
||||||
AccompanyingPeriodWorkRepository $workRepository,
|
private readonly AccompanyingPeriodWorkRepository $workRepository,
|
||||||
PaginatorFactory $paginator,
|
private readonly PaginatorFactory $paginator,
|
||||||
LoggerInterface $chillLogger
|
private readonly LoggerInterface $chillLogger,
|
||||||
|
private readonly TranslatableStringHelperInterface $translatableStringHelper,
|
||||||
|
private readonly FilterOrderHelperFactoryInterface $filterOrderHelperFactory
|
||||||
) {
|
) {
|
||||||
$this->trans = $trans;
|
|
||||||
$this->serializer = $serializer;
|
|
||||||
$this->workRepository = $workRepository;
|
|
||||||
$this->paginator = $paginator;
|
|
||||||
$this->chillLogger = $chillLogger;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -162,11 +156,21 @@ class AccompanyingCourseWorkController extends AbstractController
|
|||||||
{
|
{
|
||||||
$this->denyAccessUnlessGranted(AccompanyingPeriodWorkVoter::SEE, $period);
|
$this->denyAccessUnlessGranted(AccompanyingPeriodWorkVoter::SEE, $period);
|
||||||
|
|
||||||
|
$filter = $this->buildFilterOrder($period);
|
||||||
|
|
||||||
|
$filterData = [
|
||||||
|
'types' => $filter->hasEntityChoice('typesFilter') ? $filter->getEntityChoiceData('typesFilter') : [],
|
||||||
|
'before' => $filter->getDateRangeData('dateFilter')['to'],
|
||||||
|
'after' => $filter->getDateRangeData('dateFilter')['from'],
|
||||||
|
'user' => $filter->getUserPickerData('userFilter')
|
||||||
|
];
|
||||||
|
|
||||||
$totalItems = $this->workRepository->countByAccompanyingPeriod($period);
|
$totalItems = $this->workRepository->countByAccompanyingPeriod($period);
|
||||||
$paginator = $this->paginator->create($totalItems);
|
$paginator = $this->paginator->create($totalItems);
|
||||||
|
|
||||||
$works = $this->workRepository->findByAccompanyingPeriodOpenFirst(
|
$works = $this->workRepository->findByAccompanyingPeriodOpenFirst(
|
||||||
$period,
|
$period,
|
||||||
|
$filterData,
|
||||||
$paginator->getItemsPerPage(),
|
$paginator->getItemsPerPage(),
|
||||||
$paginator->getCurrentPageFirstItemNumber()
|
$paginator->getCurrentPageFirstItemNumber()
|
||||||
);
|
);
|
||||||
@ -175,6 +179,7 @@ class AccompanyingCourseWorkController extends AbstractController
|
|||||||
'accompanyingCourse' => $period,
|
'accompanyingCourse' => $period,
|
||||||
'works' => $works,
|
'works' => $works,
|
||||||
'paginator' => $paginator,
|
'paginator' => $paginator,
|
||||||
|
'filter' => $filter
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -199,7 +204,7 @@ class AccompanyingCourseWorkController extends AbstractController
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function createDeleteForm(int $id): Form
|
private function createDeleteForm(int $id): FormInterface
|
||||||
{
|
{
|
||||||
$params = [];
|
$params = [];
|
||||||
$params['id'] = $id;
|
$params['id'] = $id;
|
||||||
@ -210,4 +215,26 @@ class AccompanyingCourseWorkController extends AbstractController
|
|||||||
->add('submit', SubmitType::class, ['label' => 'Delete'])
|
->add('submit', SubmitType::class, ['label' => 'Delete'])
|
||||||
->getForm();
|
->getForm();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function buildFilterOrder($associatedPeriod): FilterOrderHelper
|
||||||
|
{
|
||||||
|
$filterBuilder = $this->filterOrderHelperFactory->create(self::class);
|
||||||
|
$types = $this->workRepository->findActionTypeByPeriod($associatedPeriod);
|
||||||
|
|
||||||
|
$filterBuilder
|
||||||
|
->addDateRange('dateFilter', 'accompanying_course_work.date_filter');
|
||||||
|
|
||||||
|
if (1 < count($types)) {
|
||||||
|
$filterBuilder
|
||||||
|
->addEntityChoice('typesFilter', 'accompanying_course_work.types_filter', \Chill\PersonBundle\Entity\SocialWork\SocialAction::class, $types, [
|
||||||
|
'choice_label' => fn (SocialAction $sa) => $this->translatableStringHelper->localize($sa->getTitle())
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$filterBuilder
|
||||||
|
->addUserPicker('userFilter', 'accompanying_course_work.user_filter', ['required' => false])
|
||||||
|
;
|
||||||
|
|
||||||
|
return $filterBuilder->build();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -95,29 +95,103 @@ final class AccompanyingPeriodWorkRepository implements ObjectRepository
|
|||||||
* * then, closed works
|
* * then, closed works
|
||||||
*
|
*
|
||||||
* @return AccompanyingPeriodWork[]
|
* @return AccompanyingPeriodWork[]
|
||||||
|
* @param array{types?: list<SocialAction>, user?: list<User>, after?: null|\DateTimeImmutable, before?: null|\DateTimeImmutable} $filters
|
||||||
*/
|
*/
|
||||||
public function findByAccompanyingPeriodOpenFirst(AccompanyingPeriod $period, int $limit = 10, int $offset = 0): array
|
public function findByAccompanyingPeriodOpenFirst(AccompanyingPeriod $period, array $filters, int $limit = 10, int $offset = 0): array
|
||||||
{
|
{
|
||||||
$rsm = new ResultSetMappingBuilder($this->em);
|
$rsm = new ResultSetMappingBuilder($this->em);
|
||||||
$rsm->addRootEntityFromClassMetadata(AccompanyingPeriodWork::class, 'w');
|
$rsm->addRootEntityFromClassMetadata(AccompanyingPeriodWork::class, 'w');
|
||||||
|
|
||||||
$sql = "SELECT {$rsm} FROM chill_person_accompanying_period_work w
|
$sql = "SELECT {$rsm} FROM chill_person_accompanying_period_work w
|
||||||
WHERE accompanyingPeriod_id = :periodId
|
LEFT JOIN chill_person_accompanying_period_work_referrer AS rw ON accompanyingperiodwork_id = w.id
|
||||||
ORDER BY
|
WHERE accompanyingPeriod_id = :periodId";
|
||||||
CASE WHEN enddate IS NULL THEN '-infinity'::timestamp ELSE 'infinity'::timestamp END ASC,
|
|
||||||
startdate DESC,
|
// implement filters
|
||||||
enddate DESC,
|
|
||||||
id DESC
|
if ([] !== ($filters['types'] ?? [])) {
|
||||||
LIMIT :limit OFFSET :offset";
|
$sql .= " AND w.socialaction_id IN (:types)";
|
||||||
|
}
|
||||||
|
|
||||||
|
if ([] !== ($filters['user'] ?? [])) {
|
||||||
|
$sql .= " AND rw.user_id IN ("
|
||||||
|
. implode(
|
||||||
|
', ',
|
||||||
|
// we add a user_xx for each key of the 'user' list
|
||||||
|
array_map(fn (User $u, int $idx) => ':user_' . $idx, $filters['user'], array_keys($filters['user']))
|
||||||
|
)
|
||||||
|
. ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
$sql .= " AND daterange(:after::date, :before::date) && daterange(w.startDate, w.endDate)";
|
||||||
|
|
||||||
|
// if the start and end date were inversed, we inverse the order to avoid an error
|
||||||
|
if (null !== ($filters['after'] ?? null) && null !== ($filters['before']) && $filters['after'] > $filters['before']) {
|
||||||
|
$before = $filters['after'];
|
||||||
|
$after = $filters['before'];
|
||||||
|
} else {
|
||||||
|
$before = $filters['before'];
|
||||||
|
$after = $filters['after'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// set limit and offset
|
||||||
|
$sql .= " ORDER BY
|
||||||
|
CASE WHEN enddate IS NULL THEN '-infinity'::timestamp ELSE 'infinity'::timestamp END ASC,
|
||||||
|
startdate DESC,
|
||||||
|
enddate DESC,
|
||||||
|
id DESC";
|
||||||
|
|
||||||
|
$sql .= " LIMIT :limit OFFSET :offset";
|
||||||
|
|
||||||
|
$typeIds = [];
|
||||||
|
foreach ($filters['types'] as $type) {
|
||||||
|
$typeIds[] = $type->getId();
|
||||||
|
}
|
||||||
|
|
||||||
$nq = $this->em->createNativeQuery($sql, $rsm)
|
$nq = $this->em->createNativeQuery($sql, $rsm)
|
||||||
->setParameter('periodId', $period->getId(), Types::INTEGER)
|
->setParameter('periodId', $period->getId(), Types::INTEGER)
|
||||||
|
->setParameter('types', $typeIds)
|
||||||
|
->setParameter('after', $after)
|
||||||
|
->setParameter('before', $before)
|
||||||
->setParameter('limit', $limit, Types::INTEGER)
|
->setParameter('limit', $limit, Types::INTEGER)
|
||||||
->setParameter('offset', $offset, Types::INTEGER);
|
->setParameter('offset', $offset, Types::INTEGER);
|
||||||
|
|
||||||
|
foreach ($filters['user'] as $key => $user) {
|
||||||
|
$nq->setParameter('user_' . $key, $user);
|
||||||
|
}
|
||||||
|
|
||||||
return $nq->getResult();
|
return $nq->getResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a list of types of social actions associated to the accompanying period
|
||||||
|
*
|
||||||
|
* @return array<SocialAction>
|
||||||
|
*/
|
||||||
|
public function findActionTypeByPeriod(AccompanyingPeriod $period): array
|
||||||
|
{
|
||||||
|
$in = $this->em->createQueryBuilder();
|
||||||
|
$in
|
||||||
|
->select('1')
|
||||||
|
->from(AccompanyingPeriodWork::class, 'apw');
|
||||||
|
|
||||||
|
|
||||||
|
$in->andWhere('apw.accompanyingPeriod = :period')->setParameter('period', $period);
|
||||||
|
|
||||||
|
|
||||||
|
// join between the embedded exist query and the main query
|
||||||
|
$in->andWhere('apw.socialAction = sa');
|
||||||
|
|
||||||
|
$qb = $this->em->createQueryBuilder()->setParameters($in->getParameters());
|
||||||
|
$qb
|
||||||
|
->select('sa')
|
||||||
|
->from(SocialAction::class, 'sa')
|
||||||
|
->where(
|
||||||
|
$qb->expr()->exists($in->getDQL())
|
||||||
|
);
|
||||||
|
|
||||||
|
return $qb->getQuery()->getResult();
|
||||||
|
}
|
||||||
|
|
||||||
public function findNearEndDateByUser(User $user, DateTimeImmutable $since, DateTimeImmutable $until, int $limit = 20, int $offset = 0): array
|
public function findNearEndDateByUser(User $user, DateTimeImmutable $since, DateTimeImmutable $until, int $limit = 20, int $offset = 0): array
|
||||||
{
|
{
|
||||||
return $this->buildQueryNearEndDateByUser($user, $since, $until)
|
return $this->buildQueryNearEndDateByUser($user, $since, $until)
|
||||||
|
@ -5,18 +5,23 @@
|
|||||||
{% block js %}
|
{% block js %}
|
||||||
{{ parent() }}
|
{{ parent() }}
|
||||||
{{ encore_entry_script_tags('mod_entity_workflow_pick') }}
|
{{ encore_entry_script_tags('mod_entity_workflow_pick') }}
|
||||||
|
{{ encore_entry_script_tags('mod_pickentity_type') }}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block css %}
|
{% block css %}
|
||||||
{{ parent() }}
|
{{ parent() }}
|
||||||
{{ encore_entry_link_tags('mod_entity_workflow_pick') }}
|
{{ encore_entry_link_tags('mod_entity_workflow_pick') }}
|
||||||
|
{{ encore_entry_link_tags('mod_pickentity_type') }}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="accompanying-course-work">
|
<div class="accompanying-course-work">
|
||||||
|
|
||||||
<h1>{{ block('title') }}</h1>
|
<h1>{{ block('title') }}</h1>
|
||||||
|
|
||||||
|
{{ filter|chill_render_filter_order_helper }}
|
||||||
|
|
||||||
{% if works|length == 0 %}
|
{% if works|length == 0 %}
|
||||||
<p class="chill-no-data-statement">{{ 'accompanying_course_work.Any work'|trans }}</p>
|
<p class="chill-no-data-statement">{{ 'accompanying_course_work.Any work'|trans }}</p>
|
||||||
{% else %}
|
{% else %}
|
||||||
|
@ -913,6 +913,9 @@ accompanying_course_work:
|
|||||||
social_evaluation: Évaluation
|
social_evaluation: Évaluation
|
||||||
private_comment: Commentaire privé
|
private_comment: Commentaire privé
|
||||||
timeSpent: Temps de rédaction
|
timeSpent: Temps de rédaction
|
||||||
|
date_filter: Filtrer par date
|
||||||
|
types_filter: Filtrer par type d'action
|
||||||
|
user_filter: Filtrer par intervenant
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
|
Loading…
x
Reference in New Issue
Block a user