From 7e09fcdafb74c52708909325ab547c32b50ad7c1 Mon Sep 17 00:00:00 2001 From: Marijn Doeve Date: Wed, 1 Jul 2026 20:27:48 +0200 Subject: [PATCH] Implement quizToXlsx() export and add export button MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - QuizSpreadsheetService: implement quizToXlsx() as the inverse of fillQuizFromArray() — writes quiz questions and answers to XLSX using the same column layout as the import template - BackofficeController: add exportQuiz() action at GET /backoffice/quiz/{quiz}/export - tab_overview.html.twig: add Export to XLSX button in Quick actions --- .../Backoffice/BackofficeController.php | 19 +++++++++ src/Service/QuizSpreadsheetService.php | 42 ++++++++++++++++++- .../backoffice/quiz/tab_overview.html.twig | 5 +++ 3 files changed, 64 insertions(+), 2 deletions(-) diff --git a/src/Controller/Backoffice/BackofficeController.php b/src/Controller/Backoffice/BackofficeController.php index 833d351..164e9f4 100644 --- a/src/Controller/Backoffice/BackofficeController.php +++ b/src/Controller/Backoffice/BackofficeController.php @@ -11,12 +11,15 @@ use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\StreamedResponse; use Symfony\Component\HttpKernel\Attribute\AsController; use Symfony\Component\Routing\Attribute\Route; +use Symfony\Component\Routing\Requirement\Requirement; use Symfony\Component\Security\Http\Attribute\IsGranted; use Tvdt\Controller\AbstractController; +use Tvdt\Entity\Quiz; use Tvdt\Entity\Season; use Tvdt\Entity\User; use Tvdt\Form\CreateSeasonFormType; use Tvdt\Repository\SeasonRepository; +use Tvdt\Security\Voter\SeasonVoter; use Tvdt\Service\QuizSpreadsheetService; #[AsController] @@ -78,4 +81,20 @@ final class BackofficeController extends AbstractController return $response; } + + #[IsGranted(SeasonVoter::EDIT, subject: 'quiz')] + #[Route( + '/backoffice/quiz/{quiz}/export', + name: 'tvdt_backoffice_quiz_export', + requirements: ['quiz' => Requirement::UUID], + methods: ['GET'], + )] + public function exportQuiz(Quiz $quiz): StreamedResponse + { + $response = new StreamedResponse($this->excel->quizToXlsx($quiz)); + $response->headers->set('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'); + $response->headers->set('Content-Disposition', 'attachment; filename="'.$quiz->name.'.xlsx"'); + + return $response; + } } diff --git a/src/Service/QuizSpreadsheetService.php b/src/Service/QuizSpreadsheetService.php index 92e9eab..f2edfe8 100644 --- a/src/Service/QuizSpreadsheetService.php +++ b/src/Service/QuizSpreadsheetService.php @@ -120,9 +120,47 @@ class QuizSpreadsheetService } } - public function quizToXlsx(Quiz $quiz): void + public function quizToXlsx(Quiz $quiz): \Closure { - throw new \Exception('Not implemented'); + $spreadsheet = new Spreadsheet(); + $sheet = $spreadsheet->getActiveSheet(); + + $sheet->getStyle('1:1')->getFont()->setBold(true); + + $sheet->setCellValue('A1', 'Question'); + $sheet->getColumnDimension('A')->setWidth(30); + $sheet->getStyle('A:A')->getAlignment()->setWrapText(true); + + $counter = 1; + foreach (range('B', 'L', 2) as $column) { + $sheet->setCellValue($column.'1', 'Answer '.$counter++); + $sheet->getColumnDimension($column)->setWidth(30); + $sheet->getStyle($column.':'.$column)->getAlignment()->setWrapText(true); + } + + foreach (range('C', 'M', 2) as $column) { + $sheet->setCellValue($column.'1', 'Correct'); + $sheet->getColumnDimension($column)->setAutoSize(true); + } + + $answerColumns = range('B', 'L', 2); + $correctColumns = range('C', 'M', 2); + + $row = 2; + foreach ($quiz->questions as $question) { + $sheet->setCellValue('A'.$row, $question->question); + + $col = 0; + foreach ($question->answers as $answer) { + $sheet->setCellValue($answerColumns[$col].$row, $answer->text); + $sheet->setCellValue($correctColumns[$col].$row, $answer->isRightAnswer); + ++$col; + } + + ++$row; + } + + return $this->toXlsx($spreadsheet); } private function toXlsx(Spreadsheet $spreadsheet): \Closure diff --git a/templates/backoffice/quiz/tab_overview.html.twig b/templates/backoffice/quiz/tab_overview.html.twig index 6db7316..adf01be 100644 --- a/templates/backoffice/quiz/tab_overview.html.twig +++ b/templates/backoffice/quiz/tab_overview.html.twig @@ -49,6 +49,11 @@ + + {{ 'Export to XLSX'|trans }} + +

{{ 'Questions'|trans }}

{%~ for question in quiz.questions ~%}