Loading app/src/Messenger/Handler/QuestEndMessageHandler.php +31 −1 Original line number Diff line number Diff line Loading @@ -5,6 +5,8 @@ namespace App\Messenger\Handler; use App\Entity\Quest; use App\Messenger\Message\QuestMessage; use App\Messenger\Objects\QuestsEnd; use DateInterval; use Doctrine\Common\Collections\Criteria; use Doctrine\Persistence\ManagerRegistry; use Symfony\Component\Messenger\Attribute\AsMessageHandler; use Symfony\Component\Messenger\MessageBusInterface; Loading @@ -29,7 +31,7 @@ class QuestEndMessageHandler */ public function __invoke(QuestsEnd $message): void { $quests = $this->doctrine->getRepository(Quest::class)->getEndQuests(); $quests = $this->doctrine->getRepository(Quest::class)->getByCriteria($this->getCriteria()); foreach ($quests as $quest) { $appointments = $quest->getAppointments(); Loading Loading @@ -59,4 +61,32 @@ class QuestEndMessageHandler } } } /** * Фильтр квестов * * @return Criteria */ public function getCriteria(): Criteria { $criteria = Criteria::create(); $expr = Criteria::expr(); if (!$expr) { return $criteria; } $startDate = new \DateTime(); $startDate->setTime(0,0); $startDate->sub(new DateInterval('P1D')); $endDate = new \DateTime(); $endDate->setTime(23,59, 59); $endDate->sub(new DateInterval('P1D')); $criteria ->andWhere($expr->gte('date', $startDate)) ->andWhere($expr->lte('date', $endDate)); return $criteria; } } No newline at end of file app/src/Messenger/Handler/QuestStartMessageHandler.php +31 −1 Original line number Diff line number Diff line Loading @@ -5,6 +5,8 @@ namespace App\Messenger\Handler; use App\Entity\Quest; use App\Messenger\Message\QuestMessage; use App\Messenger\Objects\QuestsStart; use DateInterval; use Doctrine\Common\Collections\Criteria; use Doctrine\Persistence\ManagerRegistry; use Symfony\Component\Messenger\Attribute\AsMessageHandler; use Symfony\Component\Messenger\MessageBusInterface; Loading @@ -29,7 +31,7 @@ class QuestStartMessageHandler */ public function __invoke(QuestsStart $message): void { $quests = $this->doctrine->getRepository(Quest::class)->getStartQuests(); $quests = $this->doctrine->getRepository(Quest::class)->getByCriteria($this->getCriteria()); foreach ($quests as $quest) { $appointments = $quest->getAppointments(); Loading Loading @@ -59,4 +61,32 @@ class QuestStartMessageHandler } } } /** * Фильтр квестов * * @return Criteria */ public function getCriteria(): Criteria { $criteria = Criteria::create(); $expr = Criteria::expr(); if (!$expr) { return $criteria; } $startDate = new \DateTime(); $startDate->setTime(0,0); $startDate->add(new DateInterval('P3D')); $endDate = new \DateTime(); $endDate->setTime(23,59, 59); $endDate->add(new DateInterval('P3D')); $criteria ->andWhere($expr->gte('date', $startDate)) ->andWhere($expr->lte('date', $endDate)); return $criteria; } } No newline at end of file app/src/Repository/QuestRepository.php +88 −93 Original line number Diff line number Diff line Loading @@ -8,6 +8,8 @@ use App\Service\Dto\Classes\FilterDto; use App\Service\Response\Classes\Data\Pagination; use DateInterval; use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; use Doctrine\Common\Collections\Criteria; use Doctrine\ORM\Query\QueryException; use Doctrine\ORM\QueryBuilder; use Doctrine\ORM\Tools\Pagination\Paginator; use Doctrine\Persistence\ManagerRegistry; Loading @@ -17,27 +19,33 @@ use Doctrine\Persistence\ManagerRegistry; */ class QuestRepository extends ServiceEntityRepository { public const SORT_TYPES = [ 'По возрастанию' => 'ASC', 'По убыванию' => 'DESC' ]; public const SORT_FIELDS = [ 'Название' => 'name', 'Дата проведения' => 'date', 'Дата финальной записи' => 'final_date' ]; public function __construct(ManagerRegistry $registry) { parent::__construct($registry, Quest::class); } /** * Получение всех квестов по фильтру * * @param int $userId * * @return array */ public function findAllByFilter(int $userId): array { $queryBuilder = $this->getFilterQuery($userId); return $queryBuilder->getQuery()->getResult(); } /** * Получение всех квестов по фильтру постранично * * @param int $userId * @param int $page * @param int $limit * * @return Pagination */ public function findAllByFilterPaged(int $userId, int $page = 1, int $limit = 10): Pagination { $queryBuilder = $this->getFilterQuery($userId); Loading @@ -53,6 +61,7 @@ class QuestRepository extends ServiceEntityRepository if ($page > $maxPage) { $page = (int)$maxPage; } if ($page > 0) { $result->page = $page; $result->maxPage = $maxPage; $result->limit = $limit; Loading @@ -60,30 +69,55 @@ class QuestRepository extends ServiceEntityRepository $paginator->getQuery() ->setFirstResult(($page - 1) * $limit) ->setMaxResults($limit); } $result->quests = $paginator->getQuery()->getResult(); return $result; } /** * Получение пройденных квестов * * @param int $userId * * @return array * * @throws QueryException */ public function findCompletedByFilter(int $userId): array { $queryBuilder = $this->getFilterQuery($userId); $queryBuilder = $this->getBaseQuery(); $criteria = Criteria::create(); $expr = Criteria::expr(); if (!$expr) { return []; } $currentDate = new \DateTime(); $queryBuilder->setParameter('current', $currentDate) ->andWhere('q.date < :current'); $criteria ->andWhere($expr->lt('date', $currentDate)) ->andWhere($expr->eq('appointments.related_user', $userId)); $queryBuilder->setParameter('user_id', $userId) ->leftJoin('q.appointments', 'appointments') ->andWhere('appointments.related_user = :user_id'); $queryBuilder->addCriteria($criteria); return $queryBuilder->getQuery()->getResult(); } /** * Получение квеста * * @param int $id * @param int $userId * @param bool $cache * * @return Quest|null */ public function findOneById(int $id, int $userId, bool $cache = true): ?Quest { $queryBuilder = $this->getBaseQuery($userId); $queryBuilder = $this->getBaseQuery(); $queryBuilder->setParameter('detail_id', $id) ->andWhere('q.id = :detail_id'); $query = $queryBuilder->getQuery(); Loading @@ -93,87 +127,48 @@ class QuestRepository extends ServiceEntityRepository return $query->getOneOrNullResult(); } private function getBaseQuery(int $userId): QueryBuilder /** * Получение квестов по критерии * * @param Criteria $criteria * * @return array */ public function getByCriteria(Criteria $criteria): array { return $this->createQueryBuilder('q'); return $this->getQueryByCriteria($criteria)->getQuery()->getResult(); } private function getFilterQuery(int $userId): QueryBuilder private function getBaseQuery(): QueryBuilder { $queryBuilder = $this->getBaseQuery($userId); $redisFilter = new RedisFilter($userId); $filter = $redisFilter->get(); if ($filter instanceof FilterDto) { if (!empty($filter->search)) { $text = $filter->search; $queryBuilder->setParameter('search', "%$text%") ->orWhere('q.name LIKE :search') ->orWhere('q.short_description LIKE :search') ->orWhere('q.full_description LIKE :search'); } if ($genres = $filter->genres) { $queryBuilder->setParameter('genres', $genres) return $this->createQueryBuilder('q') ->leftJoin('q.genre', 'genre') ->andWhere('genre.name IN (:genres)'); } if ($themes = $filter->themes) { $queryBuilder->setParameter('themes', $themes) ->leftJoin('q.theme', 'theme') ->andWhere('theme.name IN (:themes)'); } if ($tags = $filter->tags) { $queryBuilder->setParameter('tags', $tags) ->leftJoin('q.tags', 'tags') ->andWhere('tags.name IN (:tags)'); } if ($filter->sort && $filter->sortField && in_array($filter->sort, self::SORT_TYPES, true) && in_array($filter->sortField, self::SORT_FIELDS, true) ) { $queryBuilder->orderBy('q.'.$filter->sortField, $filter->sort); } } return $queryBuilder; ->leftJoin('q.appointments', 'appointments') ->leftJoin('q.reviews', 'reviews'); } public function getStartQuests(): array private function getQueryByCriteria(Criteria $criteria): QueryBuilder { $queryBuilder = $this->createQueryBuilder('q'); $startDate = new \DateTime(); $startDate->setTime(0,0); $startDate->add(new DateInterval('P3D')); $endDate = new \DateTime(); $endDate->setTime(23,59, 59); $endDate->add(new DateInterval('P3D')); $queryBuilder = $this->getBaseQuery(); $queryBuilder->setParameter('start', $startDate) ->setParameter('end', $endDate) ->andWhere('q.date >= :start') ->andWhere('q.date <= :end'); $queryBuilder->addCriteria($criteria); return $queryBuilder->getQuery()->getResult(); return $queryBuilder; } public function getEndQuests(): array private function getFilterQuery(int $userId): QueryBuilder { $queryBuilder = $this->createQueryBuilder('q'); $startDate = new \DateTime(); $startDate->setTime(0,0); $startDate->sub(new DateInterval('P1D')); $endDate = new \DateTime(); $endDate->setTime(23,59, 59); $endDate->sub(new DateInterval('P1D')); $queryBuilder->setParameter('start', $startDate) ->setParameter('end', $endDate) ->andWhere('q.date >= :start') ->andWhere('q.date <= :end'); return $queryBuilder->getQuery()->getResult(); $redisFilter = new RedisFilter($userId); $filter = $redisFilter->get(); if ($filter instanceof FilterDto) { $criteria = $filter->getCriteria(); $queryBuilder = $this->getQueryByCriteria($criteria); } else { $queryBuilder = $this->getBaseQuery(); } return $queryBuilder; } // /** Loading app/src/Service/Dto/Classes/FilterDto.php +51 −0 Original line number Diff line number Diff line Loading @@ -3,12 +3,23 @@ namespace App\Service\Dto\Classes; use App\Service\Dto\BaseDto; use Doctrine\Common\Collections\Criteria; use Symfony\Component\DependencyInjection\Attribute\AsAlias; use Symfony\Component\Serializer\Annotation\Groups; #[AsAlias(id: 'dto.filter', public: true)] class FilterDto extends BaseDto { public const SORT_TYPES = [ 'По возрастанию' => 'ASC', 'По убыванию' => 'DESC' ]; public const SORT_FIELDS = [ 'Название' => 'name', 'Дата проведения' => 'date', 'Дата финальной записи' => 'final_date' ]; /** * @var string|null */ Loading Loading @@ -44,4 +55,44 @@ class FilterDto extends BaseDto */ #[Groups(['data'])] public array $themes; /** * Получение фильтра ORM * * @return Criteria */ public function getCriteria(): Criteria { $criteria = Criteria::create(); $expr = Criteria::expr(); if (!$expr) { return $criteria; } if (!empty($this->search)) { $text = $this->search; $criteria->orWhere($expr->contains('name', $text)) ->orWhere($expr->contains('short_description', $text)) ->orWhere($expr->contains('full_description', $text)); } if ($genres = $this->genres) { $criteria->andWhere($expr->in('genre.name', $genres)); } if ($themes = $this->themes) { $criteria->andWhere($expr->in('theme.name', $themes)); } if ($tags = $this->tags) { $criteria->andWhere($expr->in('tags.name', $tags)); } if ($this->sort && $this->sortField && in_array($this->sort, self::SORT_TYPES, true) && in_array($this->sortField, self::SORT_FIELDS, true) ) { $criteria->orderBy([$this->sortField => $this->sort]); } return $criteria; } } No newline at end of file app/src/Service/Response/Classes/Data/Filter.php +3 −3 Original line number Diff line number Diff line Loading @@ -5,7 +5,7 @@ namespace App\Service\Response\Classes\Data; use App\Entity\Genre; use App\Entity\Tag; use App\Entity\Theme; use App\Repository\QuestRepository; use App\Service\Dto\Classes\FilterDto; use Symfony\Component\Serializer\Annotation\Groups; class Filter Loading @@ -32,11 +32,11 @@ class Filter * @var string[] */ #[Groups(['filter'])] public array $sortFields = QuestRepository::SORT_FIELDS; public array $sortFields = FilterDto::SORT_FIELDS; /** * @var string[] */ #[Groups(['filter'])] public array $sorts = QuestRepository::SORT_TYPES; public array $sorts = FilterDto::SORT_TYPES; } No newline at end of file Loading
app/src/Messenger/Handler/QuestEndMessageHandler.php +31 −1 Original line number Diff line number Diff line Loading @@ -5,6 +5,8 @@ namespace App\Messenger\Handler; use App\Entity\Quest; use App\Messenger\Message\QuestMessage; use App\Messenger\Objects\QuestsEnd; use DateInterval; use Doctrine\Common\Collections\Criteria; use Doctrine\Persistence\ManagerRegistry; use Symfony\Component\Messenger\Attribute\AsMessageHandler; use Symfony\Component\Messenger\MessageBusInterface; Loading @@ -29,7 +31,7 @@ class QuestEndMessageHandler */ public function __invoke(QuestsEnd $message): void { $quests = $this->doctrine->getRepository(Quest::class)->getEndQuests(); $quests = $this->doctrine->getRepository(Quest::class)->getByCriteria($this->getCriteria()); foreach ($quests as $quest) { $appointments = $quest->getAppointments(); Loading Loading @@ -59,4 +61,32 @@ class QuestEndMessageHandler } } } /** * Фильтр квестов * * @return Criteria */ public function getCriteria(): Criteria { $criteria = Criteria::create(); $expr = Criteria::expr(); if (!$expr) { return $criteria; } $startDate = new \DateTime(); $startDate->setTime(0,0); $startDate->sub(new DateInterval('P1D')); $endDate = new \DateTime(); $endDate->setTime(23,59, 59); $endDate->sub(new DateInterval('P1D')); $criteria ->andWhere($expr->gte('date', $startDate)) ->andWhere($expr->lte('date', $endDate)); return $criteria; } } No newline at end of file
app/src/Messenger/Handler/QuestStartMessageHandler.php +31 −1 Original line number Diff line number Diff line Loading @@ -5,6 +5,8 @@ namespace App\Messenger\Handler; use App\Entity\Quest; use App\Messenger\Message\QuestMessage; use App\Messenger\Objects\QuestsStart; use DateInterval; use Doctrine\Common\Collections\Criteria; use Doctrine\Persistence\ManagerRegistry; use Symfony\Component\Messenger\Attribute\AsMessageHandler; use Symfony\Component\Messenger\MessageBusInterface; Loading @@ -29,7 +31,7 @@ class QuestStartMessageHandler */ public function __invoke(QuestsStart $message): void { $quests = $this->doctrine->getRepository(Quest::class)->getStartQuests(); $quests = $this->doctrine->getRepository(Quest::class)->getByCriteria($this->getCriteria()); foreach ($quests as $quest) { $appointments = $quest->getAppointments(); Loading Loading @@ -59,4 +61,32 @@ class QuestStartMessageHandler } } } /** * Фильтр квестов * * @return Criteria */ public function getCriteria(): Criteria { $criteria = Criteria::create(); $expr = Criteria::expr(); if (!$expr) { return $criteria; } $startDate = new \DateTime(); $startDate->setTime(0,0); $startDate->add(new DateInterval('P3D')); $endDate = new \DateTime(); $endDate->setTime(23,59, 59); $endDate->add(new DateInterval('P3D')); $criteria ->andWhere($expr->gte('date', $startDate)) ->andWhere($expr->lte('date', $endDate)); return $criteria; } } No newline at end of file
app/src/Repository/QuestRepository.php +88 −93 Original line number Diff line number Diff line Loading @@ -8,6 +8,8 @@ use App\Service\Dto\Classes\FilterDto; use App\Service\Response\Classes\Data\Pagination; use DateInterval; use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; use Doctrine\Common\Collections\Criteria; use Doctrine\ORM\Query\QueryException; use Doctrine\ORM\QueryBuilder; use Doctrine\ORM\Tools\Pagination\Paginator; use Doctrine\Persistence\ManagerRegistry; Loading @@ -17,27 +19,33 @@ use Doctrine\Persistence\ManagerRegistry; */ class QuestRepository extends ServiceEntityRepository { public const SORT_TYPES = [ 'По возрастанию' => 'ASC', 'По убыванию' => 'DESC' ]; public const SORT_FIELDS = [ 'Название' => 'name', 'Дата проведения' => 'date', 'Дата финальной записи' => 'final_date' ]; public function __construct(ManagerRegistry $registry) { parent::__construct($registry, Quest::class); } /** * Получение всех квестов по фильтру * * @param int $userId * * @return array */ public function findAllByFilter(int $userId): array { $queryBuilder = $this->getFilterQuery($userId); return $queryBuilder->getQuery()->getResult(); } /** * Получение всех квестов по фильтру постранично * * @param int $userId * @param int $page * @param int $limit * * @return Pagination */ public function findAllByFilterPaged(int $userId, int $page = 1, int $limit = 10): Pagination { $queryBuilder = $this->getFilterQuery($userId); Loading @@ -53,6 +61,7 @@ class QuestRepository extends ServiceEntityRepository if ($page > $maxPage) { $page = (int)$maxPage; } if ($page > 0) { $result->page = $page; $result->maxPage = $maxPage; $result->limit = $limit; Loading @@ -60,30 +69,55 @@ class QuestRepository extends ServiceEntityRepository $paginator->getQuery() ->setFirstResult(($page - 1) * $limit) ->setMaxResults($limit); } $result->quests = $paginator->getQuery()->getResult(); return $result; } /** * Получение пройденных квестов * * @param int $userId * * @return array * * @throws QueryException */ public function findCompletedByFilter(int $userId): array { $queryBuilder = $this->getFilterQuery($userId); $queryBuilder = $this->getBaseQuery(); $criteria = Criteria::create(); $expr = Criteria::expr(); if (!$expr) { return []; } $currentDate = new \DateTime(); $queryBuilder->setParameter('current', $currentDate) ->andWhere('q.date < :current'); $criteria ->andWhere($expr->lt('date', $currentDate)) ->andWhere($expr->eq('appointments.related_user', $userId)); $queryBuilder->setParameter('user_id', $userId) ->leftJoin('q.appointments', 'appointments') ->andWhere('appointments.related_user = :user_id'); $queryBuilder->addCriteria($criteria); return $queryBuilder->getQuery()->getResult(); } /** * Получение квеста * * @param int $id * @param int $userId * @param bool $cache * * @return Quest|null */ public function findOneById(int $id, int $userId, bool $cache = true): ?Quest { $queryBuilder = $this->getBaseQuery($userId); $queryBuilder = $this->getBaseQuery(); $queryBuilder->setParameter('detail_id', $id) ->andWhere('q.id = :detail_id'); $query = $queryBuilder->getQuery(); Loading @@ -93,87 +127,48 @@ class QuestRepository extends ServiceEntityRepository return $query->getOneOrNullResult(); } private function getBaseQuery(int $userId): QueryBuilder /** * Получение квестов по критерии * * @param Criteria $criteria * * @return array */ public function getByCriteria(Criteria $criteria): array { return $this->createQueryBuilder('q'); return $this->getQueryByCriteria($criteria)->getQuery()->getResult(); } private function getFilterQuery(int $userId): QueryBuilder private function getBaseQuery(): QueryBuilder { $queryBuilder = $this->getBaseQuery($userId); $redisFilter = new RedisFilter($userId); $filter = $redisFilter->get(); if ($filter instanceof FilterDto) { if (!empty($filter->search)) { $text = $filter->search; $queryBuilder->setParameter('search', "%$text%") ->orWhere('q.name LIKE :search') ->orWhere('q.short_description LIKE :search') ->orWhere('q.full_description LIKE :search'); } if ($genres = $filter->genres) { $queryBuilder->setParameter('genres', $genres) return $this->createQueryBuilder('q') ->leftJoin('q.genre', 'genre') ->andWhere('genre.name IN (:genres)'); } if ($themes = $filter->themes) { $queryBuilder->setParameter('themes', $themes) ->leftJoin('q.theme', 'theme') ->andWhere('theme.name IN (:themes)'); } if ($tags = $filter->tags) { $queryBuilder->setParameter('tags', $tags) ->leftJoin('q.tags', 'tags') ->andWhere('tags.name IN (:tags)'); } if ($filter->sort && $filter->sortField && in_array($filter->sort, self::SORT_TYPES, true) && in_array($filter->sortField, self::SORT_FIELDS, true) ) { $queryBuilder->orderBy('q.'.$filter->sortField, $filter->sort); } } return $queryBuilder; ->leftJoin('q.appointments', 'appointments') ->leftJoin('q.reviews', 'reviews'); } public function getStartQuests(): array private function getQueryByCriteria(Criteria $criteria): QueryBuilder { $queryBuilder = $this->createQueryBuilder('q'); $startDate = new \DateTime(); $startDate->setTime(0,0); $startDate->add(new DateInterval('P3D')); $endDate = new \DateTime(); $endDate->setTime(23,59, 59); $endDate->add(new DateInterval('P3D')); $queryBuilder = $this->getBaseQuery(); $queryBuilder->setParameter('start', $startDate) ->setParameter('end', $endDate) ->andWhere('q.date >= :start') ->andWhere('q.date <= :end'); $queryBuilder->addCriteria($criteria); return $queryBuilder->getQuery()->getResult(); return $queryBuilder; } public function getEndQuests(): array private function getFilterQuery(int $userId): QueryBuilder { $queryBuilder = $this->createQueryBuilder('q'); $startDate = new \DateTime(); $startDate->setTime(0,0); $startDate->sub(new DateInterval('P1D')); $endDate = new \DateTime(); $endDate->setTime(23,59, 59); $endDate->sub(new DateInterval('P1D')); $queryBuilder->setParameter('start', $startDate) ->setParameter('end', $endDate) ->andWhere('q.date >= :start') ->andWhere('q.date <= :end'); return $queryBuilder->getQuery()->getResult(); $redisFilter = new RedisFilter($userId); $filter = $redisFilter->get(); if ($filter instanceof FilterDto) { $criteria = $filter->getCriteria(); $queryBuilder = $this->getQueryByCriteria($criteria); } else { $queryBuilder = $this->getBaseQuery(); } return $queryBuilder; } // /** Loading
app/src/Service/Dto/Classes/FilterDto.php +51 −0 Original line number Diff line number Diff line Loading @@ -3,12 +3,23 @@ namespace App\Service\Dto\Classes; use App\Service\Dto\BaseDto; use Doctrine\Common\Collections\Criteria; use Symfony\Component\DependencyInjection\Attribute\AsAlias; use Symfony\Component\Serializer\Annotation\Groups; #[AsAlias(id: 'dto.filter', public: true)] class FilterDto extends BaseDto { public const SORT_TYPES = [ 'По возрастанию' => 'ASC', 'По убыванию' => 'DESC' ]; public const SORT_FIELDS = [ 'Название' => 'name', 'Дата проведения' => 'date', 'Дата финальной записи' => 'final_date' ]; /** * @var string|null */ Loading Loading @@ -44,4 +55,44 @@ class FilterDto extends BaseDto */ #[Groups(['data'])] public array $themes; /** * Получение фильтра ORM * * @return Criteria */ public function getCriteria(): Criteria { $criteria = Criteria::create(); $expr = Criteria::expr(); if (!$expr) { return $criteria; } if (!empty($this->search)) { $text = $this->search; $criteria->orWhere($expr->contains('name', $text)) ->orWhere($expr->contains('short_description', $text)) ->orWhere($expr->contains('full_description', $text)); } if ($genres = $this->genres) { $criteria->andWhere($expr->in('genre.name', $genres)); } if ($themes = $this->themes) { $criteria->andWhere($expr->in('theme.name', $themes)); } if ($tags = $this->tags) { $criteria->andWhere($expr->in('tags.name', $tags)); } if ($this->sort && $this->sortField && in_array($this->sort, self::SORT_TYPES, true) && in_array($this->sortField, self::SORT_FIELDS, true) ) { $criteria->orderBy([$this->sortField => $this->sort]); } return $criteria; } } No newline at end of file
app/src/Service/Response/Classes/Data/Filter.php +3 −3 Original line number Diff line number Diff line Loading @@ -5,7 +5,7 @@ namespace App\Service\Response\Classes\Data; use App\Entity\Genre; use App\Entity\Tag; use App\Entity\Theme; use App\Repository\QuestRepository; use App\Service\Dto\Classes\FilterDto; use Symfony\Component\Serializer\Annotation\Groups; class Filter Loading @@ -32,11 +32,11 @@ class Filter * @var string[] */ #[Groups(['filter'])] public array $sortFields = QuestRepository::SORT_FIELDS; public array $sortFields = FilterDto::SORT_FIELDS; /** * @var string[] */ #[Groups(['filter'])] public array $sorts = QuestRepository::SORT_TYPES; public array $sorts = FilterDto::SORT_TYPES; } No newline at end of file