mirror of
https://github.com/MarijnDoeve/TijdVoorDeTest.git
synced 2026-03-06 04:44:19 +01:00
Add quiz clearing and deletion functionality with UI enhancements
This commit introduces the ability to clear quiz results and delete quizzes directly from the backoffice. It includes new routes, controllers, modals for user confirmation, and updates to translations. The `QuizRepository` now supports dedicated methods for clearing results and deleting quizzes along with error handling. Related database migrations and front-end adjustments are also included.
This commit is contained in:
@@ -10,6 +10,7 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController as AbstractBase
|
||||
abstract class AbstractController extends AbstractBaseController
|
||||
{
|
||||
protected const string SEASON_CODE_REGEX = '[A-Za-z\d]{5}';
|
||||
|
||||
protected const string CANDIDATE_HASH_REGEX = '[\w\-=]+';
|
||||
|
||||
#[\Override]
|
||||
|
||||
@@ -39,9 +39,10 @@ final class PrepareEliminationController extends AbstractController
|
||||
$elimination->updateFromInputBag($request->request);
|
||||
$em->flush();
|
||||
|
||||
if (true === $request->request->getBoolean('start')) {
|
||||
if ($request->request->getBoolean('start')) {
|
||||
return $this->redirectToRoute('app_elimination', ['elimination' => $elimination->getId()]);
|
||||
}
|
||||
|
||||
$this->addFlash('success', 'Elimination updated');
|
||||
}
|
||||
|
||||
|
||||
@@ -8,8 +8,10 @@ use App\Controller\AbstractController;
|
||||
use App\Entity\Candidate;
|
||||
use App\Entity\Quiz;
|
||||
use App\Entity\Season;
|
||||
use App\Exception\ErrorClearingQuizException;
|
||||
use App\Repository\CandidateRepository;
|
||||
use App\Repository\QuizCandidateRepository;
|
||||
use App\Repository\QuizRepository;
|
||||
use App\Security\Voter\SeasonVoter;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
@@ -19,6 +21,7 @@ use Symfony\Component\HttpKernel\Attribute\AsController;
|
||||
use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
use Symfony\Component\Security\Http\Attribute\IsGranted;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
#[AsController]
|
||||
#[IsGranted('ROLE_USER')]
|
||||
@@ -26,6 +29,7 @@ class QuizController extends AbstractController
|
||||
{
|
||||
public function __construct(
|
||||
private readonly CandidateRepository $candidateRepository,
|
||||
private readonly TranslatorInterface $translator,
|
||||
) {}
|
||||
|
||||
#[Route(
|
||||
@@ -61,6 +65,37 @@ class QuizController extends AbstractController
|
||||
return $this->redirectToRoute('app_backoffice_season', ['seasonCode' => $season->getSeasonCode()]);
|
||||
}
|
||||
|
||||
#[Route(
|
||||
'/backoffice/quiz/{quiz}/clear',
|
||||
name: 'app_backoffice_quiz_clear',
|
||||
)]
|
||||
#[IsGranted(SeasonVoter::EDIT, subject: 'quiz')]
|
||||
public function clearQuiz(Quiz $quiz, QuizRepository $quizRepository): RedirectResponse
|
||||
{
|
||||
try {
|
||||
$quizRepository->clearQuiz($quiz);
|
||||
$this->addFlash('success', $this->translator->trans('Quiz cleared'));
|
||||
} catch (ErrorClearingQuizException) {
|
||||
$this->addFlash('error', $this->translator->trans('Error clearing quiz'));
|
||||
}
|
||||
|
||||
return $this->redirectToRoute('app_backoffice_quiz', ['seasonCode' => $quiz->getSeason()->getSeasonCode(), 'quiz' => $quiz->getId()]);
|
||||
}
|
||||
|
||||
#[Route(
|
||||
'/backoffice/quiz/{quiz}/delete',
|
||||
name: 'app_backoffice_quiz_delete',
|
||||
)]
|
||||
#[IsGranted(SeasonVoter::DELETE, subject: 'quiz')]
|
||||
public function deleteQuiz(Quiz $quiz, QuizRepository $quizRepository): RedirectResponse
|
||||
{
|
||||
$quizRepository->deleteQuiz($quiz);
|
||||
|
||||
$this->addFlash('success', $this->translator->trans('Quiz deleted'));
|
||||
|
||||
return $this->redirectToRoute('app_backoffice_season', ['seasonCode' => $quiz->getSeason()->getSeasonCode()]);
|
||||
}
|
||||
|
||||
#[Route(
|
||||
'/backoffice/quiz/{quiz}/modify_correction/{candidate}',
|
||||
name: 'app_backoffice_modify_correction',
|
||||
|
||||
@@ -26,7 +26,7 @@ use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
#[IsGranted('ROLE_USER')]
|
||||
class SeasonController extends AbstractController
|
||||
{
|
||||
public function __construct(private readonly TranslatorInterface $translator, private EntityManagerInterface $em,
|
||||
public function __construct(private readonly TranslatorInterface $translator, private readonly EntityManagerInterface $em,
|
||||
) {}
|
||||
|
||||
#[Route(
|
||||
|
||||
@@ -18,6 +18,7 @@ use Symfony\Component\Uid\Uuid;
|
||||
class Elimination
|
||||
{
|
||||
public const string SCREEN_GREEN = 'green';
|
||||
|
||||
public const string SCREEN_RED = 'red';
|
||||
|
||||
#[ORM\Id]
|
||||
@@ -35,7 +36,7 @@ class Elimination
|
||||
|
||||
public function __construct(
|
||||
#[ORM\ManyToOne(inversedBy: 'eliminations')]
|
||||
#[ORM\JoinColumn(nullable: false)]
|
||||
#[ORM\JoinColumn(nullable: false, onDelete: 'CASCADE')]
|
||||
private Quiz $quiz,
|
||||
) {}
|
||||
|
||||
@@ -66,7 +67,7 @@ class Elimination
|
||||
/** @param InputBag<bool|float|int|string> $inputBag */
|
||||
public function updateFromInputBag(InputBag $inputBag): self
|
||||
{
|
||||
foreach ($this->data as $name => $screenColour) {
|
||||
foreach (array_keys($this->data) as $name) {
|
||||
$newColour = $inputBag->get('colour-'.mb_strtolower($name));
|
||||
if (\is_string($newColour)) {
|
||||
$this->data[$name] = $inputBag->get('colour-'.mb_strtolower($name));
|
||||
|
||||
@@ -31,7 +31,7 @@ class GivenAnswer
|
||||
private Candidate $candidate,
|
||||
|
||||
#[ORM\ManyToOne]
|
||||
#[ORM\JoinColumn(nullable: false)]
|
||||
#[ORM\JoinColumn(nullable: false, onDelete: 'CASCADE')]
|
||||
private Quiz $quiz,
|
||||
|
||||
#[ORM\ManyToOne(inversedBy: 'givenAnswers')]
|
||||
|
||||
@@ -43,6 +43,7 @@ class Season
|
||||
private Collection $owners;
|
||||
|
||||
#[ORM\ManyToOne]
|
||||
#[ORM\JoinColumn(nullable: true, onDelete: 'SET NULL')]
|
||||
private ?Quiz $ActiveQuiz = null;
|
||||
|
||||
public function __construct()
|
||||
|
||||
7
src/Exception/ErrorClearingQuizException.php
Normal file
7
src/Exception/ErrorClearingQuizException.php
Normal file
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Exception;
|
||||
|
||||
class ErrorClearingQuizException extends \Exception {}
|
||||
@@ -4,17 +4,59 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Repository;
|
||||
|
||||
use App\Entity\Elimination;
|
||||
use App\Entity\GivenAnswer;
|
||||
use App\Entity\Quiz;
|
||||
use App\Entity\QuizCandidate;
|
||||
use App\Exception\ErrorClearingQuizException;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
/**
|
||||
* @extends ServiceEntityRepository<Quiz>
|
||||
*/
|
||||
class QuizRepository extends ServiceEntityRepository
|
||||
{
|
||||
public function __construct(ManagerRegistry $registry)
|
||||
public function __construct(ManagerRegistry $registry, private readonly LoggerInterface $logger)
|
||||
{
|
||||
parent::__construct($registry, Quiz::class);
|
||||
}
|
||||
|
||||
/** @throws ErrorClearingQuizException */
|
||||
public function clearQuiz(Quiz $quiz): void
|
||||
{
|
||||
$em = $this->getEntityManager();
|
||||
$em->beginTransaction();
|
||||
try {
|
||||
$em->createQueryBuilder()
|
||||
->delete()->from(QuizCandidate::class, 'qc')
|
||||
->where('qc.quiz = :quiz')
|
||||
->setParameter('quiz', $quiz)
|
||||
->getQuery()->execute();
|
||||
|
||||
$em->createQueryBuilder()
|
||||
->delete()->from(GivenAnswer::class, 'ga')
|
||||
->where('ga.quiz = :quiz')
|
||||
->setParameter('quiz', $quiz)
|
||||
->getQuery()->execute();
|
||||
$em->createQueryBuilder()
|
||||
->delete()->from(Elimination::class, 'e')
|
||||
->where('e.quiz = :quiz')
|
||||
->setParameter('quiz', $quiz)
|
||||
->getQuery()->execute();
|
||||
} catch (\Throwable $throwable) {
|
||||
$this->logger->error($throwable->getMessage());
|
||||
$em->rollback();
|
||||
throw new ErrorClearingQuizException(previous: $throwable);
|
||||
}
|
||||
|
||||
$em->commit();
|
||||
}
|
||||
|
||||
public function deleteQuiz(Quiz $quiz): void
|
||||
{
|
||||
$this->getEntityManager()->remove($quiz);
|
||||
$this->getEntityManager()->flush();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user