This commit is contained in:
2025-04-22 23:42:07 +02:00
parent 7b05e52d95
commit 0f07e7eabf
15 changed files with 51 additions and 43 deletions

View File

@@ -17,17 +17,17 @@ use SymfonyCasts\Bundle\VerifyEmail\SymfonyCastsVerifyEmailBundle;
use Twig\Extra\TwigExtraBundle\TwigExtraBundle; use Twig\Extra\TwigExtraBundle\TwigExtraBundle;
return [ return [
DoctrineBundle::class => ['all' => true],
DoctrineFixturesBundle::class => ['dev' => true, 'test' => true],
DoctrineMigrationsBundle::class => ['all' => true],
EasyAdminBundle::class => ['all' => true],
FrameworkBundle::class => ['all' => true], FrameworkBundle::class => ['all' => true],
DoctrineBundle::class => ['all' => true],
DoctrineMigrationsBundle::class => ['all' => true],
MakerBundle::class => ['dev' => true], MakerBundle::class => ['dev' => true],
SecurityBundle::class => ['all' => true],
SentryBundle::class => ['prod' => true],
SymfonyCastsVerifyEmailBundle::class => ['all' => true],
TwigBundle::class => ['all' => true], TwigBundle::class => ['all' => true],
TwigComponentBundle::class => ['all' => true], SecurityBundle::class => ['all' => true],
TwigExtraBundle::class => ['all' => true],
WebProfilerBundle::class => ['dev' => true, 'test' => true], WebProfilerBundle::class => ['dev' => true, 'test' => true],
TwigExtraBundle::class => ['all' => true],
TwigComponentBundle::class => ['all' => true],
EasyAdminBundle::class => ['all' => true],
DoctrineFixturesBundle::class => ['dev' => true, 'test' => true],
SymfonyCastsVerifyEmailBundle::class => ['all' => true],
SentryBundle::class => ['prod' => true],
]; ];

View File

@@ -9,7 +9,7 @@ framework:
enabled: true enabled: true
#esi: true #esi: true
#fragments: true #fragments: true
when@prod:
# shortcut for private IP address ranges of your proxy # shortcut for private IP address ranges of your proxy
trusted_proxies: 'private_ranges' trusted_proxies: 'private_ranges'
# or, if your proxy instead uses the "Forwarded" header # or, if your proxy instead uses the "Forwarded" header

View File

@@ -1,4 +1,5 @@
parameters: parameters:
editorUrl: 'phpstorm://open?file=%%file%%&line=%%line%%'
level: 8 level: 8
paths: paths:
- bin/ - bin/

View File

@@ -18,15 +18,11 @@ use Symfony\Component\Console\Style\SymfonyStyle;
)] )]
class MakeAdminCommand extends Command class MakeAdminCommand extends Command
{ {
public function __construct(private UserRepository $userRepository) public function __construct(private readonly UserRepository $userRepository)
{ {
parent::__construct(); parent::__construct();
} }
protected function configure(): void protected function configure(): void
{ {
$this $this

View File

@@ -42,16 +42,15 @@ final class QuizController extends AbstractController
$form->handleRequest($request); $form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) { if ($form->isSubmitted() && $form->isValid()) {
$data = $form->getData(); $seasonCode = $form->get('season_code')->getData();
$season_code = $data['season_code'];
if ([] === $seasonRepository->findBy(['seasonCode' => $season_code])) { if ([] === $seasonRepository->findBy(['seasonCode' => $seasonCode])) {
$this->addFlash(FlashType::Warning, $this->translator->trans('Invalid season code')); $this->addFlash(FlashType::Warning, $this->translator->trans('Invalid season code'));
return $this->redirectToRoute('app_quiz_selectseason'); return $this->redirectToRoute('app_quiz_selectseason');
} }
return $this->redirectToRoute('app_quiz_entername', ['seasonCode' => $season_code]); return $this->redirectToRoute('app_quiz_entername', ['seasonCode' => $seasonCode]);
} }
return $this->render('quiz/select_season.html.twig', ['form' => $form]); return $this->render('quiz/select_season.html.twig', ['form' => $form]);
@@ -68,8 +67,7 @@ final class QuizController extends AbstractController
$form->handleRequest($request); $form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) { if ($form->isSubmitted() && $form->isValid()) {
$data = $form->getData(); $name = $form->get('name')->getData();
$name = $data['name'];
return $this->redirectToRoute('app_quiz_quizpage', ['seasonCode' => $season->getSeasonCode(), 'nameHash' => Base64::base64UrlEncode($name)]); return $this->redirectToRoute('app_quiz_quizpage', ['seasonCode' => $season->getSeasonCode(), 'nameHash' => Base64::base64UrlEncode($name)]);
} }

View File

@@ -72,7 +72,7 @@ class Answer
return $this; return $this;
} }
public function isRightAnswer(): ?bool public function isRightAnswer(): bool
{ {
return $this->isRightAnswer; return $this->isRightAnswer;
} }

View File

@@ -18,21 +18,24 @@ class Elimination
#[ORM\Column(type: UuidType::NAME, unique: true)] #[ORM\Column(type: UuidType::NAME, unique: true)]
#[ORM\GeneratedValue(strategy: 'CUSTOM')] #[ORM\GeneratedValue(strategy: 'CUSTOM')]
#[ORM\CustomIdGenerator(class: UuidGenerator::class)] #[ORM\CustomIdGenerator(class: UuidGenerator::class)]
private ?Uuid $id = null; private Uuid $id;
/** @var array<string, mixed> */
#[ORM\Column(type: Types::JSON)] #[ORM\Column(type: Types::JSON)]
private array $data = []; private array $data = [];
public function getId(): ?int public function getId(): Uuid
{ {
return $this->id; return $this->id;
} }
/** @return array<string, mixed> */
public function getData(): array public function getData(): array
{ {
return $this->data; return $this->data;
} }
/** @param array<string, mixed> $data */
public function setData(array $data): static public function setData(array $data): static
{ {
$this->data = $data; $this->data = $data;

View File

@@ -20,7 +20,7 @@ class GivenAnswer
#[ORM\Column(type: UuidType::NAME, unique: true)] #[ORM\Column(type: UuidType::NAME, unique: true)]
#[ORM\GeneratedValue(strategy: 'CUSTOM')] #[ORM\GeneratedValue(strategy: 'CUSTOM')]
#[ORM\CustomIdGenerator(class: UuidGenerator::class)] #[ORM\CustomIdGenerator(class: UuidGenerator::class)]
private ?Uuid $id = null; private Uuid $id;
#[ORM\ManyToOne(inversedBy: 'givenAnswers')] #[ORM\ManyToOne(inversedBy: 'givenAnswers')]
#[ORM\JoinColumn(nullable: false)] #[ORM\JoinColumn(nullable: false)]
@@ -35,7 +35,7 @@ class GivenAnswer
private ?Answer $answer = null; private ?Answer $answer = null;
#[ORM\Column(type: Types::DATETIME_IMMUTABLE, nullable: false)] #[ORM\Column(type: Types::DATETIME_IMMUTABLE, nullable: false)]
private \DateTimeInterface $created; private \DateTimeImmutable $created;
public function getId(): ?Uuid public function getId(): ?Uuid
{ {
@@ -78,7 +78,7 @@ class GivenAnswer
return $this; return $this;
} }
public function getCreated(): \DateTimeInterface public function getCreated(): \DateTimeImmutable
{ {
return $this->created; return $this->created;
} }

View File

@@ -103,7 +103,7 @@ class Question
return 'This question has no answers'; return 'This question has no answers';
} }
$correctAnswers = $this->answers->filter(static fn (Answer $answer): ?bool => $answer->isRightAnswer())->count(); $correctAnswers = $this->answers->filter(static fn (Answer $answer): bool => $answer->isRightAnswer())->count();
if (0 === $correctAnswers) { if (0 === $correctAnswers) {
return 'This question has no correct answers'; return 'This question has no correct answers';

View File

@@ -76,13 +76,16 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface
*/ */
public function getUserIdentifier(): string public function getUserIdentifier(): string
{ {
return $this->email; /** @var non-empty-string $identifier */
$identifier = $this->email;
return $identifier;
} }
/** /**
* @see UserInterface * @see UserInterface
* *
* @return non-empty-list<string> * @return non-empty-array<int<0, max>, string>
*/ */
public function getRoles(): array public function getRoles(): array
{ {

View File

@@ -9,7 +9,6 @@ use App\Entity\Correction;
use App\Entity\Quiz; use App\Entity\Quiz;
use App\Entity\Season; use App\Entity\Season;
use App\Helpers\Base64; use App\Helpers\Base64;
use DateInterval;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\ORM\Query\Expr\Join; use Doctrine\ORM\Query\Expr\Join;
use Doctrine\Persistence\ManagerRegistry; use Doctrine\Persistence\ManagerRegistry;
@@ -18,7 +17,8 @@ use Safe\Exceptions\UrlException;
/** /**
* @extends ServiceEntityRepository<Candidate> * @extends ServiceEntityRepository<Candidate>
* *
* @phpstan-type ResultArray array<string, array{0: Candidate, correct: int, time: DateInterval, corrections?: float, score: float}> * @phpstan-type Result array{0: Candidate, correct: int, time: \DateInterval, corrections?: float, score: float}
* @phpstan-type ResultList list<Result>
*/ */
class CandidateRepository extends ServiceEntityRepository class CandidateRepository extends ServiceEntityRepository
{ {
@@ -52,7 +52,7 @@ class CandidateRepository extends ServiceEntityRepository
} }
} }
/** @return ResultArray */ /** @return ResultList */
public function getScores(Quiz $quiz): array public function getScores(Quiz $quiz): array
{ {
$scoreTimeQb = $this->createQueryBuilder('c', 'c.id') $scoreTimeQb = $this->createQueryBuilder('c', 'c.id')
@@ -76,8 +76,8 @@ class CandidateRepository extends ServiceEntityRepository
/** /**
* @param array<string, array{0: Candidate, correct: int, time: \DateInterval, corrections?: float}> $in * @param array<string, array{0: Candidate, correct: int, time: \DateInterval, corrections?: float}> $in
* *
* @return ResultArray * @return array<string, Result>
*/ * */
private function calculateScore(array $in): array private function calculateScore(array $in): array
{ {
return array_map(static fn ($candidate): array => [ return array_map(static fn ($candidate): array => [
@@ -87,10 +87,10 @@ class CandidateRepository extends ServiceEntityRepository
} }
/** /**
* @param ResultArray $results * @param array<string, Result> $results
* *
* @return ResultArray * @return ResultList
*/ * */
private function sortResults(array $results): array private function sortResults(array $results): array
{ {
usort($results, static fn ($a, $b): int => $b['score'] <=> $a['score']); usort($results, static fn ($a, $b): int => $b['score'] <=> $a['score']);

View File

@@ -7,10 +7,10 @@ namespace App\Service;
use App\Repository\CandidateRepository; use App\Repository\CandidateRepository;
/** /**
* @phpstan-import-type ResultArray from CandidateRepository * @phpstan-import-type ResultList from CandidateRepository
*/ */
class EliminationService class EliminationService
{ {
/** @phpstan-param ResultArray $result */ /** @phpstan-param ResultList $result */
public function createEliminationFromResult(array $result): void {} public function createEliminationFromResult(array $result): void {}
} }

View File

@@ -17,6 +17,12 @@
href="{{ path('app_backoffice_index') }}">{{ 'Seasons'|trans }}</a> href="{{ path('app_backoffice_index') }}">{{ 'Seasons'|trans }}</a>
</li> </li>
</ul> </ul>
<ul class="navbar-nav mb-auto me-2 me-lg-0">
<li class="nav-item">
<a class="nav-link"
href="{{ path('app_login_logout') }}">{{ 'Logout'|trans }}</a>
</li>
</ul>
</div> </div>
</div> </div>
</nav> </nav>

View File

@@ -1,12 +1,12 @@
<?php <?php
declare(strict_types=1);
use Symfony\Component\Dotenv\Dotenv; use Symfony\Component\Dotenv\Dotenv;
require dirname(__DIR__).'/vendor/autoload.php'; require dirname(__DIR__).'/vendor/autoload.php';
if (method_exists(Dotenv::class, 'bootEnv')) { (new Dotenv())->bootEnv(dirname(__DIR__).'/.env');
(new Dotenv())->bootEnv(dirname(__DIR__).'/.env');
}
if ($_SERVER['APP_DEBUG']) { if ($_SERVER['APP_DEBUG']) {
umask(0000); umask(0000);

View File

@@ -11,6 +11,7 @@ Email: E-mail
'Enter your name': 'Voor je naam in' 'Enter your name': 'Voor je naam in'
'Invalid season code': 'Ongeldige seizoenscode' 'Invalid season code': 'Ongeldige seizoenscode'
'Load Prepared Elimination': 'Laad voorbereide eliminatie' 'Load Prepared Elimination': 'Laad voorbereide eliminatie'
Logout: Uitloggen
Manage: Beheren Manage: Beheren
Name: Naam Name: Naam
'No active quiz': 'Geen actieve test' 'No active quiz': 'Geen actieve test'