Refactor repositories to use DQL queries, simplify logic, and enhance query efficiency

This commit is contained in:
2025-06-09 14:19:10 +02:00
parent 366bc36520
commit d5566d4737
6 changed files with 48 additions and 67 deletions

View File

@@ -34,12 +34,14 @@ class CandidateRepository extends ServiceEntityRepository
return null;
}
return $this->createQueryBuilder('c')
->where('c.season = :season')
->andWhere('lower(c.name) = lower(:name)')
->setParameter('season', $season)
return $this->getEntityManager()->createQuery(<<<DQL
select c from App\Entity\Candidate c
where c.season = :season
and lower(c.name) = lower(:name)
DQL
)->setParameter('season', $season)
->setParameter('name', $name)
->getQuery()->getOneOrNullResult();
->getOneOrNullResult();
}
public function save(Candidate $candidate, bool $flush = true): void
@@ -54,44 +56,22 @@ class CandidateRepository extends ServiceEntityRepository
/** @return ResultList */
public function getScores(Quiz $quiz): array
{
$qb = $this->createQueryBuilder('c', 'c.id')
->select('c.id', 'c.name', 'sum(case when a.isRightAnswer = true then 1 else 0 end) as correct', 'qc.corrections', 'max(ga.created) - qc.created as time')
->join('c.givenAnswers', 'ga')
->join('ga.answer', 'a')
->join('c.quizData', 'qc')
->where('qc.quiz = :quiz')
->groupBy('ga.quiz', 'c.id', 'qc.id')
->setParameter('quiz', $quiz);
return $this->sortResults(
$this->calculateScore(
$qb->getQuery()->getResult(),
),
);
}
/**
* @param array<string, array{id: Uuid, name: string, correct: int, time: \DateInterval, corrections: float}> $in
*
* @return array<string, Result>
*/
private function calculateScore(array $in): array
{
return array_map(static fn ($candidate): array => [
...$candidate,
'score' => $candidate['correct'] + $candidate['corrections'],
], $in);
}
/**
* @param array<string, Result> $results
*
* @return ResultList
* */
private function sortResults(array $results): array
{
usort($results, static fn ($a, $b): int => $b['score'] <=> $a['score']);
return $results;
return $this->getEntityManager()->createQuery(<<<DQL
select
c.id,
c.name,
sum(case when a.isRightAnswer = true then 1 else 0 end) as correct,
qc.corrections,
max(ga.created) - qc.created as time,
(sum(case when a.isRightAnswer = true then 1 else 0 end) + qc.corrections) as score
from App\Entity\Candidate c
join c.givenAnswers ga
join ga.answer a
join c.quizData qc
where qc.quiz = :quiz
group by ga.quiz, c.id, qc.id
order by score desc, time desc
DQL
)->setParameter('quiz', $quiz)->getResult();
}
}

View File

@@ -5,7 +5,6 @@ declare(strict_types=1);
namespace App\Repository;
use App\Entity\Candidate;
use App\Entity\GivenAnswer;
use App\Entity\Question;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
@@ -22,22 +21,21 @@ class QuestionRepository extends ServiceEntityRepository
public function findNextQuestionForCandidate(Candidate $candidate): ?Question
{
$qb = $this->createQueryBuilder('q');
return $qb->join('q.quiz', 'qz')
->andWhere($qb->expr()->notIn('q.id', $this->getEntityManager()->createQueryBuilder()
->select('q1')
->from(GivenAnswer::class, 'ga')
->join('ga.answer', 'a')
->join('a.question', 'q1')
->andWhere($qb->expr()->isNotNull('ga.answer'))
->andWhere('ga.candidate = :candidate')
->andWhere('q1.quiz = :quiz')
->getDQL()))
->andWhere('qz = :quiz')
return $this->getEntityManager()->createQuery(<<<DQL
select q from App\Entity\Question q
join q.quiz qz
where q.id not in (
select q1.id from App\Entity\GivenAnswer ga
join ga.answer a
join a.question q1
where ga.candidate = :candidate
and q1.quiz = :quiz
)
and qz = :quiz
DQL)
->setMaxResults(1)
->setParameter('candidate', $candidate)
->setParameter('quiz', $candidate->getSeason()->getActiveQuiz())
->getQuery()->getOneOrNullResult();
->getOneOrNullResult();
}
}

View File

@@ -22,11 +22,9 @@ class SeasonRepository extends ServiceEntityRepository
/** @return list<Season> Returns an array of Season objects */
public function getSeasonsForUser(User $user): array
{
$qb = $this->createQueryBuilder('s')
->where(':user MEMBER OF s.owners')
->orderBy('s.name')
->setParameter('user', $user);
return $qb->getQuery()->getResult();
return $this->getEntityManager()->createQuery(<<<DQL
select s from App\Entity\Season s where :user member of s.owners order by s.name
DQL
)->setParameter('user', $user)->getResult();
}
}