From ff24ab56320165fe04fb36faee5a653deb4b2a4b Mon Sep 17 00:00:00 2001 From: Ilya Vasilenko Date: Fri, 20 Sep 2024 12:19:13 +0500 Subject: [PATCH] add criteria --- .../Handler/QuestEndMessageHandler.php | 32 +++- .../Handler/QuestStartMessageHandler.php | 32 +++- app/src/Repository/QuestRepository.php | 181 +++++++++--------- app/src/Service/Dto/Classes/FilterDto.php | 51 +++++ .../Service/Response/Classes/Data/Filter.php | 6 +- 5 files changed, 204 insertions(+), 98 deletions(-) diff --git a/app/src/Messenger/Handler/QuestEndMessageHandler.php b/app/src/Messenger/Handler/QuestEndMessageHandler.php index 367fae3..532b5b9 100644 --- a/app/src/Messenger/Handler/QuestEndMessageHandler.php +++ b/app/src/Messenger/Handler/QuestEndMessageHandler.php @@ -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; @@ -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(); @@ -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 diff --git a/app/src/Messenger/Handler/QuestStartMessageHandler.php b/app/src/Messenger/Handler/QuestStartMessageHandler.php index 4aac3f7..da412fa 100644 --- a/app/src/Messenger/Handler/QuestStartMessageHandler.php +++ b/app/src/Messenger/Handler/QuestStartMessageHandler.php @@ -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; @@ -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(); @@ -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 diff --git a/app/src/Repository/QuestRepository.php b/app/src/Repository/QuestRepository.php index c37ec84..0402923 100644 --- a/app/src/Repository/QuestRepository.php +++ b/app/src/Repository/QuestRepository.php @@ -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; @@ -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); @@ -53,37 +61,63 @@ class QuestRepository extends ServiceEntityRepository if ($page > $maxPage) { $page = (int)$maxPage; } - $result->page = $page; - $result->maxPage = $maxPage; - $result->limit = $limit; - - $paginator->getQuery() - ->setFirstResult(($page - 1) * $limit) - ->setMaxResults($limit); + if ($page > 0) { + $result->page = $page; + $result->maxPage = $maxPage; + $result->limit = $limit; + + $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(); @@ -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) - ->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; + return $this->createQueryBuilder('q') + ->leftJoin('q.genre', 'genre') + ->leftJoin('q.theme', 'theme') + ->leftJoin('q.tags', 'tags') + ->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; } // /** diff --git a/app/src/Service/Dto/Classes/FilterDto.php b/app/src/Service/Dto/Classes/FilterDto.php index c74fce3..d4fdc29 100644 --- a/app/src/Service/Dto/Classes/FilterDto.php +++ b/app/src/Service/Dto/Classes/FilterDto.php @@ -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 */ @@ -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 diff --git a/app/src/Service/Response/Classes/Data/Filter.php b/app/src/Service/Response/Classes/Data/Filter.php index 402484b..3e7226c 100644 --- a/app/src/Service/Response/Classes/Data/Filter.php +++ b/app/src/Service/Response/Classes/Data/Filter.php @@ -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 @@ -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 -- GitLab