Create Testcoverage and upgrade Symfomy and PHP
Some checks failed
CI / Tests (push) Failing after 1m8s
CI / Build and deploy to ${{ startsWith(github.ref, 'refs/tags/') && 'production' || (github.ref == 'refs/heads/main' && 'acceptance' || '') }} (push) Has been skipped

* Some tests

* More tests!

* Tests 3

* Move getScores from Candidate to Quiz

* Add some suggestions for future refactoring

* - **Add Gedmo doctrine-extensions and Stof bundle integration**
  - Added `stof/doctrine-extensions-bundle` and `gedmo/doctrine-extensions` dependencies.
  - Integrated `Timestampable` behavior for `Created` fields in entities.
  - Updated `bundles.php` to register StofDoctrineExtensionsBundle.
  - Added configuration for the Stof bundle.
  - Simplified `SeasonVoter` with `match` expression and added new tests.
  - Minor fixes and adjustments across various files.

* WIP

* All the tests

* Base64 tests

* Symfomny 7.4.0

* Update

* Update recipe

* PHP 8.5

* Rector changes

* More 8.5

* Things
This commit is contained in:
2025-11-28 22:56:09 +01:00
committed by GitHub
parent fc273638ad
commit bcd6a157a8
56 changed files with 4324 additions and 1424 deletions

View File

@@ -6,12 +6,8 @@ namespace Tvdt\Repository;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
use Safe\DateTimeImmutable;
use Safe\Exceptions\DatetimeException;
use Safe\Exceptions\UrlException;
use Tvdt\Dto\Result;
use Tvdt\Entity\Candidate;
use Tvdt\Entity\Quiz;
use Tvdt\Entity\Season;
use Tvdt\Helpers\Base64;
@@ -42,49 +38,4 @@ class CandidateRepository extends ServiceEntityRepository
->setParameter('name', $name)
->getOneOrNullResult();
}
public function save(Candidate $candidate, bool $flush = true): void
{
$this->getEntityManager()->persist($candidate);
if ($flush) {
$this->getEntityManager()->flush();
}
}
/**
* @throws DatetimeException
*
* @return list<Result>
*/
public function getScores(Quiz $quiz): array
{
$result = $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) as end_time,
qc.created as start_time,
(sum(case when a.isRightAnswer = true then 1 else 0 end) + qc.corrections) as score
from Tvdt\Entity\Candidate c
join c.givenAnswers ga
join ga.answer a
join c.quizData qc
where qc.quiz = :quiz and ga.quiz = :quiz
group by ga.quiz, c.id, qc.id
order by score desc, max(ga.created) - qc.created asc
DQL
)->setParameter('quiz', $quiz)->getResult();
return array_map(static fn (array $row): Result => new Result(
id: $row['id'],
name: $row['name'],
correct: (int) $row['correct'],
corrections: $row['corrections'],
time: new DateTimeImmutable($row['end_time'])->diff($row['start_time']),
score: $row['score'],
), $result);
}
}

View File

@@ -17,13 +17,4 @@ class GivenAnswerRepository extends ServiceEntityRepository
{
parent::__construct($registry, GivenAnswer::class);
}
public function save(GivenAnswer $givenAnswer, bool $flush = true): void
{
$this->getEntityManager()->persist($givenAnswer);
if ($flush) {
$this->getEntityManager()->flush();
}
}
}

View File

@@ -7,10 +7,10 @@ namespace Tvdt\Repository;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
use Psr\Log\LoggerInterface;
use Tvdt\Entity\Elimination;
use Tvdt\Entity\GivenAnswer;
use Safe\DateTimeImmutable;
use Safe\Exceptions\DatetimeException;
use Tvdt\Dto\Result;
use Tvdt\Entity\Quiz;
use Tvdt\Entity\QuizCandidate;
use Tvdt\Exception\ErrorClearingQuizException;
/**
@@ -29,28 +29,36 @@ class QuizRepository extends ServiceEntityRepository
$em = $this->getEntityManager();
$em->beginTransaction();
try {
$em->createQueryBuilder()
->delete()->from(QuizCandidate::class, 'qc')
->where('qc.quiz = :quiz')
$em->createQuery(<<<DQL
delete from Tvdt\Entity\QuizCandidate qc
where qc.quiz = :quiz
DQL)
->setParameter('quiz', $quiz)
->getQuery()->execute();
->execute();
$em->createQueryBuilder()
->delete()->from(GivenAnswer::class, 'ga')
->where('ga.quiz = :quiz')
$em->createQuery(<<<DQL
delete from Tvdt\Entity\GivenAnswer ga
where ga.quiz = :quiz
DQL)
->setParameter('quiz', $quiz)
->getQuery()->execute();
$em->createQueryBuilder()
->delete()->from(Elimination::class, 'e')
->where('e.quiz = :quiz')
->execute();
$em->createQuery(<<<DQL
delete from Tvdt\Entity\Elimination e
where e.quiz = :quiz
DQL)
->setParameter('quiz', $quiz)
->getQuery()->execute();
} catch (\Throwable $throwable) {
->execute();
}
// @codeCoverageIgnoreStart
catch (\Throwable $throwable) {
$this->logger->error($throwable->getMessage());
$em->rollback();
throw new ErrorClearingQuizException(previous: $throwable);
}
// @codeCoverageIgnoreEnd
$em->commit();
}
@@ -59,4 +67,40 @@ class QuizRepository extends ServiceEntityRepository
$this->getEntityManager()->remove($quiz);
$this->getEntityManager()->flush();
}
/**
* @throws DatetimeException
*
* @return list<Result>
*/
public function getScores(Quiz $quiz): array
{
$result = $this->getEntityManager()->createQuery(<<<DQL
select
c.id,
c.name,
sum(case when a.isRightAnswer = true then 1 else 0 end) as correct,
qd.corrections,
max(ga.created) as end_time,
qd.created as start_time,
(sum(case when a.isRightAnswer = true then 1 else 0 end) + qd.corrections) as score
from Tvdt\Entity\Candidate c
join c.givenAnswers ga
join ga.answer a
join c.quizData qd
where qd.quiz = :quiz and ga.quiz = :quiz
group by ga.quiz, c.id, qd.id
order by score desc, max(ga.created) - qd.created asc
DQL
)->setParameter('quiz', $quiz)->getResult();
return array_map(static fn (array $row): Result => new Result(
id: $row['id'],
name: $row['name'],
correct: (int) $row['correct'],
corrections: $row['corrections'],
time: $row['start_time']->diff(new DateTimeImmutable($row['end_time'])),
score: $row['score'],
), $result);
}
}

View File

@@ -19,6 +19,17 @@ class SeasonRepository extends ServiceEntityRepository
parent::__construct($registry, Season::class);
}
public function findOneBySeasonCode(string $seasonCode): ?Season
{
return $this->getEntityManager()->createQuery(<<<DQL
select s from Tvdt\Entity\Season s
where s.seasonCode = :seasonCode
DQL)
->setParameter('seasonCode', $seasonCode)
->setMaxResults(1)
->getOneOrNullResult();
}
/** @return list<Season> Returns an array of Season objects */
public function getSeasonsForUser(User $user): array
{