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

5
.idea/php.xml generated
View File

@@ -26,6 +26,11 @@
<phpcs_fixer_by_interpreter asDefaultInterpreter="true" deletedFromTheList="true" interpreter_id="96512cb2-7b9e-4e1d-bfa2-bf7f3be424c8" standards="DoctrineAnnotation;PER;PER-CS;PER-CS1.0;PER-CS2.0;PHP54Migration;PHP56Migration;PHP70Migration;PHP71Migration;PHP73Migration;PHP74Migration;PHP80Migration;PHP81Migration;PHP82Migration;PHP83Migration;PHP84Migration;PHPUnit100Migration;PHPUnit30Migration;PHPUnit32Migration;PHPUnit35Migration;PHPUnit43Migration;PHPUnit48Migration;PHPUnit50Migration;PHPUnit52Migration;PHPUnit54Migration;PHPUnit55Migration;PHPUnit56Migration;PHPUnit57Migration;PHPUnit60Migration;PHPUnit75Migration;PHPUnit84Migration;PHPUnit91Migration;PSR1;PSR12;PSR2;PhpCsFixer;Symfony" tool_path="vendor/bin/php-cs-fixer" timeout="30000" /> <phpcs_fixer_by_interpreter asDefaultInterpreter="true" deletedFromTheList="true" interpreter_id="96512cb2-7b9e-4e1d-bfa2-bf7f3be424c8" standards="DoctrineAnnotation;PER;PER-CS;PER-CS1.0;PER-CS2.0;PHP54Migration;PHP56Migration;PHP70Migration;PHP71Migration;PHP73Migration;PHP74Migration;PHP80Migration;PHP81Migration;PHP82Migration;PHP83Migration;PHP84Migration;PHPUnit100Migration;PHPUnit30Migration;PHPUnit32Migration;PHPUnit35Migration;PHPUnit43Migration;PHPUnit48Migration;PHPUnit50Migration;PHPUnit52Migration;PHPUnit54Migration;PHPUnit55Migration;PHPUnit56Migration;PHPUnit57Migration;PHPUnit60Migration;PHPUnit75Migration;PHPUnit84Migration;PHPUnit91Migration;PSR1;PSR12;PSR2;PhpCsFixer;Symfony" tool_path="vendor/bin/php-cs-fixer" timeout="30000" />
</phpcsfixer_settings> </phpcsfixer_settings>
</component> </component>
<component name="PhpCodeSniffer">
<phpcs_settings>
<phpcs_by_interpreter asDefaultInterpreter="true" interpreter_id="96512cb2-7b9e-4e1d-bfa2-bf7f3be424c8" timeout="30000" />
</phpcs_settings>
</component>
<component name="PhpExternalFormatter"> <component name="PhpExternalFormatter">
<option name="externalFormatter" value="PHP_CS_FIXER" /> <option name="externalFormatter" value="PHP_CS_FIXER" />
</component> </component>

View File

@@ -37,7 +37,7 @@ security:
# Note: Only the *first* access control that matches will be used # Note: Only the *first* access control that matches will be used
access_control: access_control:
- { path: ^/admin, roles: ROLE_ADMIN } - { path: ^/admin, roles: ROLE_ADMIN }
# - { path: ^/profile, roles: ROLE_USER } - { path: ^/backoffice, roles: ROLE_USER }
when@test: when@test:
security: security:

View File

@@ -51,7 +51,7 @@ class QuizController extends AbstractController
#[Route( #[Route(
'/backoffice/season/{seasonCode:season}/quiz/{quiz}/enable', '/backoffice/season/{seasonCode:season}/quiz/{quiz}/enable',
name: 'app_backoffice_enable', name: 'app_backoffice_enable',
requirements: ['seasonCode' => self::SEASON_CODE_REGEX, 'quiz' => Requirement::UUID], requirements: ['seasonCode' => self::SEASON_CODE_REGEX, 'quiz' => Requirement::UUID.'|null'],
)] )]
#[IsGranted(SeasonVoter::EDIT, subject: 'season')] #[IsGranted(SeasonVoter::EDIT, subject: 'season')]
public function enableQuiz(Season $season, ?Quiz $quiz, EntityManagerInterface $em): RedirectResponse public function enableQuiz(Season $season, ?Quiz $quiz, EntityManagerInterface $em): RedirectResponse

View File

@@ -34,12 +34,14 @@ class CandidateRepository extends ServiceEntityRepository
return null; return null;
} }
return $this->createQueryBuilder('c') return $this->getEntityManager()->createQuery(<<<DQL
->where('c.season = :season') select c from App\Entity\Candidate c
->andWhere('lower(c.name) = lower(:name)') where c.season = :season
->setParameter('season', $season) and lower(c.name) = lower(:name)
DQL
)->setParameter('season', $season)
->setParameter('name', $name) ->setParameter('name', $name)
->getQuery()->getOneOrNullResult(); ->getOneOrNullResult();
} }
public function save(Candidate $candidate, bool $flush = true): void public function save(Candidate $candidate, bool $flush = true): void
@@ -54,44 +56,22 @@ class CandidateRepository extends ServiceEntityRepository
/** @return ResultList */ /** @return ResultList */
public function getScores(Quiz $quiz): array public function getScores(Quiz $quiz): array
{ {
$qb = $this->createQueryBuilder('c', 'c.id') 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') select
->join('c.givenAnswers', 'ga') c.id,
->join('ga.answer', 'a') c.name,
->join('c.quizData', 'qc') sum(case when a.isRightAnswer = true then 1 else 0 end) as correct,
->where('qc.quiz = :quiz') qc.corrections,
->groupBy('ga.quiz', 'c.id', 'qc.id') max(ga.created) - qc.created as time,
->setParameter('quiz', $quiz); (sum(case when a.isRightAnswer = true then 1 else 0 end) + qc.corrections) as score
from App\Entity\Candidate c
return $this->sortResults( join c.givenAnswers ga
$this->calculateScore( join ga.answer a
$qb->getQuery()->getResult(), 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();
* @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;
} }
} }

View File

@@ -5,7 +5,6 @@ declare(strict_types=1);
namespace App\Repository; namespace App\Repository;
use App\Entity\Candidate; use App\Entity\Candidate;
use App\Entity\GivenAnswer;
use App\Entity\Question; use App\Entity\Question;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry; use Doctrine\Persistence\ManagerRegistry;
@@ -22,22 +21,21 @@ class QuestionRepository extends ServiceEntityRepository
public function findNextQuestionForCandidate(Candidate $candidate): ?Question public function findNextQuestionForCandidate(Candidate $candidate): ?Question
{ {
$qb = $this->createQueryBuilder('q'); return $this->getEntityManager()->createQuery(<<<DQL
select q from App\Entity\Question q
return $qb->join('q.quiz', 'qz') join q.quiz qz
->andWhere($qb->expr()->notIn('q.id', $this->getEntityManager()->createQueryBuilder() where q.id not in (
->select('q1') select q1.id from App\Entity\GivenAnswer ga
->from(GivenAnswer::class, 'ga') join ga.answer a
->join('ga.answer', 'a') join a.question q1
->join('a.question', 'q1') where ga.candidate = :candidate
->andWhere($qb->expr()->isNotNull('ga.answer')) and q1.quiz = :quiz
->andWhere('ga.candidate = :candidate') )
->andWhere('q1.quiz = :quiz') and qz = :quiz
->getDQL())) DQL)
->andWhere('qz = :quiz')
->setMaxResults(1) ->setMaxResults(1)
->setParameter('candidate', $candidate) ->setParameter('candidate', $candidate)
->setParameter('quiz', $candidate->getSeason()->getActiveQuiz()) ->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 */ /** @return list<Season> Returns an array of Season objects */
public function getSeasonsForUser(User $user): array public function getSeasonsForUser(User $user): array
{ {
$qb = $this->createQueryBuilder('s') return $this->getEntityManager()->createQuery(<<<DQL
->where(':user MEMBER OF s.owners') select s from App\Entity\Season s where :user member of s.owners order by s.name
->orderBy('s.name') DQL
->setParameter('user', $user); )->setParameter('user', $user)->getResult();
return $qb->getQuery()->getResult();
} }
} }