diff --git a/be/src/Controller/PostsController.php b/be/src/Controller/PostsController.php index 461da3a6..f3db0824 100644 --- a/be/src/Controller/PostsController.php +++ b/be/src/Controller/PostsController.php @@ -87,26 +87,15 @@ public function query(Request $request): array $this->stopwatch->stop('fillWithParentPost'); $this->stopwatch->start('queryUsers'); - $latestRepliersId = $result['threads']->map(fn(Thread $thread) => $thread->getLatestReplierId()); - $latestRepliers = collect() - ->concat($this->latestReplierRepository->createQueryBuilder('t') - ->where('t.id IN (:ids)')->setParameter('ids', $latestRepliersId) - ->andWhere('t.uid IS NOT NULL') - ->select('t.id', 't.uid', 't.createdAt', 't.updatedAt') // removeSelect('t.name', 't.displayName') - ->getQuery()->getResult()) - ->concat($this->latestReplierRepository->createQueryBuilder('t') - ->where('t.id IN (:ids)')->setParameter('ids', $latestRepliersId) - ->andWhere('t.uid IS NULL') - ->getQuery()->getResult()); + $latestRepliers = $this->latestReplierRepository->getLatestRepliersWithoutNameWhenHasUid( + $result['threads']->map(fn(Thread $thread) => $thread->getLatestReplierId()), + ); $uids = collect($result) ->only(Helper::POST_TYPES_PLURAL) ->flatMap(static fn(Collection $posts) => $posts->map(fn(Post $post) => $post->getAuthorUid())) - ->concat($latestRepliers->pluck('uid')) - ->filter()->unique(); // remove NULLs - $users = collect($this->userRepository->createQueryBuilder('t') - ->where('t.uid IN (:uids)') - ->setParameter('uids', $uids) - ->getQuery()->getResult()); + ->concat($latestRepliers->pluck('uid')->filter()) // filter() will remove NULLs + ->unique(); + $users = collect($this->userRepository->getUsers($uids)); $this->stopwatch->stop('queryUsers'); $this->stopwatch->start('queryUserRelated'); @@ -127,10 +116,7 @@ public function query(Request $request): array ...$query->getResultPages(), ...Arr::except($result, ['fid', ...Helper::POST_TYPES_PLURAL]), ], - 'forum' => $this->forumRepository->createQueryBuilder('t') - ->where('t.fid = :fid')->setParameter('fid', $fid) - ->select('t.fid', 't.name')->setMaxResults(1) - ->getQuery()->getSingleResult(), + 'forum' => $this->forumRepository->getForum($fid), 'threads' => $query->reOrderNestedPosts($query->nestPostsWithParent(...$result)), 'users' => $users, 'latestRepliers' => $latestRepliers, diff --git a/be/src/PostsQuery/BaseQuery.php b/be/src/PostsQuery/BaseQuery.php index 94872988..17ca3492 100644 --- a/be/src/PostsQuery/BaseQuery.php +++ b/be/src/PostsQuery/BaseQuery.php @@ -179,10 +179,7 @@ static function (string $postIDName) use ($result): array { /** @var Collection $parentThreadsID parent tid of all replies and their sub replies */ $parentThreadsID = $replies->pluck('tid')->concat($subReplies->pluck('tid'))->unique(); /** @var Collection $threads */ - $threads = collect($postModels['thread']->createQueryBuilder('t') - ->where('t.tid IN (:tids)') - ->setParameter('tids', $parentThreadsID->concat($tids)) - ->getQuery()->getResult()) + $threads = collect($postModels['thread']->getPosts($parentThreadsID->concat($tids))) ->each(static fn(Thread $thread) => $thread->setIsMatchQuery($tids->contains($thread->getTid()))); $this->stopwatch->stop('fillWithThreadsFields'); @@ -191,31 +188,23 @@ static function (string $postIDName) use ($result): array { /** @var Collection $parentRepliesID parent pid of all sub replies */ $parentRepliesID = $subReplies->pluck('pid')->unique(); $allRepliesId = $parentRepliesID->concat($pids); - $replies = collect($postModels['reply']->createQueryBuilder('t') - ->where('t.pid IN (:pids)')->setParameter('pids', $allRepliesId) - ->getQuery()->getResult()) + $replies = collect($postModels['reply']->getPosts($allRepliesId)) ->each(static fn(Reply $reply) => $reply->setIsMatchQuery($pids->contains($reply->getPid()))); $this->stopwatch->stop('fillWithRepliesFields'); $this->stopwatch->start('fillWithSubRepliesFields'); - $subReplies = collect($postModels['subReply']->createQueryBuilder('t') - ->where('t.spid IN (:spids)')->setParameter('spids', $spids) - ->getQuery()->getResult()); + $subReplies = collect($postModels['subReply']->getPosts($spids)); $this->stopwatch->stop('fillWithSubRepliesFields'); $this->stopwatch->start('parsePostContentProtoBufBytes'); // not using one-to-one association due to relying on PostRepository->getTableNameSuffix() - $replyContents = collect($this->postRepositoryFactory->newReplyContent($fid)->createQueryBuilder('t') - ->where('t.pid IN (:pids)')->setParameter('pids', $allRepliesId) - ->getQuery()->getResult()) + $replyContents = collect($this->postRepositoryFactory->newReplyContent($fid)->getPosts($allRepliesId)) ->mapWithKeys(fn(ReplyContent $content) => [$content->getPid() => $content->getContent()]); $replies->each(fn(Reply $reply) => $reply->setContent($replyContents->get($reply->getPid()))); - $subReplyContents = collect($this->postRepositoryFactory->newSubReplyContent($fid)->createQueryBuilder('t') - ->where('t.spid IN (:spids)')->setParameter('spids', $spids) - ->getQuery()->getResult()) + $subReplyContents = collect($this->postRepositoryFactory->newSubReplyContent($fid)->getPosts($spids)) ->mapWithKeys(fn(SubReplyContent $content) => [$content->getSpid() => $content->getContent()]); $subReplies->each(fn(SubReply $subReply) => $subReply->setContent($subReplyContents->get($subReply->getSpid()))); diff --git a/be/src/PostsQuery/IndexQuery.php b/be/src/PostsQuery/IndexQuery.php index 265f31d1..0a728b51 100644 --- a/be/src/PostsQuery/IndexQuery.php +++ b/be/src/PostsQuery/IndexQuery.php @@ -50,18 +50,14 @@ public function query(QueryParams $params, ?string $cursor): void ->only($postTypes) ->transform(static fn(PostRepository $repository) => $repository->selectCurrentAndParentPostID()); $getFidByPostIDParam = function (string $postIDName, int $postID): int { - $counts = collect($this->forumRepository->getOrderedForumsId()) - ->map(fn(int $fid) => $this->postRepositoryFactory + $postExistencesKeyByFid = collect($this->forumRepository->getOrderedForumsId()) + ->mapWithKeys(fn(int $fid) => [$fid => $this->postRepositoryFactory ->new($fid, Helper::POST_ID_TO_TYPE[$postIDName]) - ->createQueryBuilder('t') - ->select("$fid AS fid", 'COUNT(t) AS count') - ->where("t.$postIDName = :postID") - ->setParameter('postID', $postID) - ->getQuery()->getSingleResult()) - ->where('count', '!=', 0); - Helper::abortAPIIf(50001, $counts->count() > 1); - Helper::abortAPIIf(40401, $counts->count() === 0); - return $counts->pluck('fid')->first(); + ->isPostExists($postID)]) + ->filter(fn(bool $isExists) => $isExists); + Helper::abortAPIIf(50001, $postExistencesKeyByFid->count() > 1); + Helper::abortAPIIf(40401, $postExistencesKeyByFid->count() === 0); + return $postExistencesKeyByFid->keys()[0]; }; if (\array_key_exists('fid', $flatParams)) { diff --git a/be/src/Repository/ForumRepository.php b/be/src/Repository/ForumRepository.php index e8f9fd33..88e6af10 100644 --- a/be/src/Repository/ForumRepository.php +++ b/be/src/Repository/ForumRepository.php @@ -35,4 +35,11 @@ public function isForumExists(int $fid): bool ->setParameter('fid', $fid) ->getOneOrNullResult(AbstractQuery::HYDRATE_SINGLE_SCALAR) === 1; } + + public function getForum(int $fid): Forum + { + return $this->getEntityManager() + ->createQuery(/** @lang DQL */'SELECT t.fid, t.name FROM App\Entity\Forum t WHERE t.fid = :fid') + ->setMaxResults(1)->setParameter('fid', $fid)->getSingleResult(); + } } diff --git a/be/src/Repository/LatestReplierRepository.php b/be/src/Repository/LatestReplierRepository.php index a9861b27..b6f1c113 100644 --- a/be/src/Repository/LatestReplierRepository.php +++ b/be/src/Repository/LatestReplierRepository.php @@ -5,6 +5,7 @@ use App\Entity\LatestReplier; use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; use Doctrine\Persistence\ManagerRegistry; +use Illuminate\Support\Collection; class LatestReplierRepository extends ServiceEntityRepository { @@ -12,4 +13,21 @@ public function __construct(ManagerRegistry $registry) { parent::__construct($registry, LatestReplier::class); } + + public function getLatestRepliersWithoutNameWhenHasUid(\ArrayAccess $latestRepliersId): Collection + { + $entityManager = $this->getEntityManager(); + return collect($entityManager->createQuery(<<<'DQL' + SELECT t.id, t.uid, t.createdAt, t.updatedAt + FROM App\Entity\LatestReplier t + WHERE t.id IN (:ids) AND t.uid IS NOT NULL + DQL) // removeSelect('t.name', 't.displayName') + ->setParameter('ids', $latestRepliersId)->getResult()) + ->concat( + $entityManager->createQuery(<<<'DQL' + SELECT t FROM App\Entity\LatestReplier t + WHERE t.id IN (:ids) AND t.uid IS NULL + DQL)->setParameter('ids', $latestRepliersId)->getResult(), + ); + } } diff --git a/be/src/Repository/Post/Content/ReplyContentRepository.php b/be/src/Repository/Post/Content/ReplyContentRepository.php index b73941ee..f8c8180d 100644 --- a/be/src/Repository/Post/Content/ReplyContentRepository.php +++ b/be/src/Repository/Post/Content/ReplyContentRepository.php @@ -23,4 +23,13 @@ protected function getTableNameSuffix(): string { return 'reply_content'; } + + public function getPosts(\ArrayAccess $postsId): array + { + return $this->createQueryWithParam( + /** @lang DQL */'SELECT t FROM App\Entity\Post\Content\ReplyContent t WHERE t.pid IN (:pid)', + 'pid', + $postsId + )->getResult(); + } } diff --git a/be/src/Repository/Post/Content/SubReplyContentRepository.php b/be/src/Repository/Post/Content/SubReplyContentRepository.php index ad804160..e4490b2d 100644 --- a/be/src/Repository/Post/Content/SubReplyContentRepository.php +++ b/be/src/Repository/Post/Content/SubReplyContentRepository.php @@ -23,4 +23,13 @@ protected function getTableNameSuffix(): string { return 'subReply_content'; } + + public function getPosts(\ArrayAccess $postsId): array + { + return $this->createQueryWithParam( + /** @lang DQL */'SELECT t FROM App\Entity\Post\Content\SubReplyContent t WHERE t.spid IN (:spid)', + 'spid', + $postsId + )->getResult(); + } } diff --git a/be/src/Repository/Post/PostRepository.php b/be/src/Repository/Post/PostRepository.php index b15178bd..1d63a2d0 100644 --- a/be/src/Repository/Post/PostRepository.php +++ b/be/src/Repository/Post/PostRepository.php @@ -7,6 +7,7 @@ use App\Entity\Post\Post; use App\Helper; use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; +use Doctrine\ORM\AbstractQuery; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\QueryBuilder; use Doctrine\Persistence\ManagerRegistry; @@ -47,4 +48,19 @@ public function selectCurrentAndParentPostID(): QueryBuilder ->map(static fn(string $postIDField) => "t.$postIDField") ->all()); } + + abstract public function getPosts(\ArrayAccess $postsId): array; + + protected function createQueryWithParam(string $dql, string $paramName, int|\ArrayAccess $paramValue): AbstractQuery + { + return $this->getEntityManager()->createQuery($dql)->setParameter($paramName, $paramValue); + } + + abstract public function isPostExists(int $postId): bool; + + protected function isPostExistsWrapper(int $postId, string $dql, string $postIdParamName): bool + { + return $this->createQueryWithParam($dql, $postIdParamName, $postId) + ->getOneOrNullResult(AbstractQuery::HYDRATE_SINGLE_SCALAR) === 1; + } } diff --git a/be/src/Repository/Post/ReplyRepository.php b/be/src/Repository/Post/ReplyRepository.php index 75b2f55d..a1f25774 100644 --- a/be/src/Repository/Post/ReplyRepository.php +++ b/be/src/Repository/Post/ReplyRepository.php @@ -22,4 +22,22 @@ protected function getTableNameSuffix(): string { return 'reply'; } + + public function getPosts(\ArrayAccess $postsId): array + { + return $this->createQueryWithParam( + /** @lang DQL */'SELECT t FROM App\Entity\Post\Reply t WHERE t.pid IN (:pid)', + 'pid', + $postsId + )->getResult(); + } + + public function isPostExists(int $postId): bool + { + return $this->isPostExistsWrapper( + $postId, + /** @lang DQL */'SELECT 1 FROM App\Entity\Post\Reply t WHERE t.pid = :pid', + 'pid' + ); + } } diff --git a/be/src/Repository/Post/SubReplyRepository.php b/be/src/Repository/Post/SubReplyRepository.php index 2b461b36..60a118a6 100644 --- a/be/src/Repository/Post/SubReplyRepository.php +++ b/be/src/Repository/Post/SubReplyRepository.php @@ -22,4 +22,22 @@ protected function getTableNameSuffix(): string { return 'subReply'; } + + public function getPosts(\ArrayAccess $postsId): array + { + return $this->createQueryWithParam( + /** @lang DQL */'SELECT t FROM App\Entity\Post\SubReply t WHERE t.spid IN (:spid)', + 'spid', + $postsId + )->getResult(); + } + + public function isPostExists(int $postId): bool + { + return $this->isPostExistsWrapper( + $postId, + /** @lang DQL */'SELECT 1 FROM App\Entity\Post\SubReply t WHERE t.spid = :spid', + 'spid' + ); + } } diff --git a/be/src/Repository/Post/ThreadRepository.php b/be/src/Repository/Post/ThreadRepository.php index 17dde90c..93fdfbf1 100644 --- a/be/src/Repository/Post/ThreadRepository.php +++ b/be/src/Repository/Post/ThreadRepository.php @@ -23,6 +23,24 @@ protected function getTableNameSuffix(): string return 'thread'; } + public function getPosts(\ArrayAccess $postsId): array + { + return $this->createQueryWithParam( + /** @lang DQL */'SELECT t FROM App\Entity\Post\Thread t WHERE t.tid IN (:tid)', + 'tid', + $postsId + )->getResult(); + } + + public function isPostExists(int $postId): bool + { + return $this->isPostExistsWrapper( + $postId, + /** @lang DQL */'SELECT 1 FROM App\Entity\Post\Thread t WHERE t.tid = :tid', + 'tid' + ); + } + public function getThreadsIdByChunks(int $chunkSize): array { // https://github.com/doctrine/orm/issues/3542 diff --git a/be/src/Repository/UserRepository.php b/be/src/Repository/UserRepository.php index 783a8bc2..3b05a240 100644 --- a/be/src/Repository/UserRepository.php +++ b/be/src/Repository/UserRepository.php @@ -12,4 +12,11 @@ public function __construct(ManagerRegistry $registry) { parent::__construct($registry, User::class); } + + public function getUsers(array $usersId): array + { + return $this->getEntityManager() + ->createQuery(/** @lang DQL */'SELECT t FROM App\Entity\User t WHERE t.uid IN (:usersId)') + ->setParameter('usersId', $usersId)->getResult(); + } }