From 23a5ba3ddbae5cf314054c801c6d2f8ef235cde8 Mon Sep 17 00:00:00 2001
From: Leon <lvilents@gmail.com>
Date: Sun, 12 Jan 2020 15:21:42 +0100
Subject: [PATCH] Add ext-json; Add generator for next question

---
 composer.json                         |   1 +
 composer.lock                         | 137 +++++++++++++-------------
 public/js/index.js                    |  13 ++-
 src/Controller/MainController.php     |  24 ++++-
 src/Entity/Answer.php                 |   7 +-
 src/Entity/Question.php               |  58 ++++++++++-
 src/Repository/AnswerRepository.php   |   2 +-
 src/Repository/QuestionRepository.php |  11 ++-
 src/Service/QuestionService.php       |  71 +++++++++++++
 templates/base.html.twig              |  12 ---
 10 files changed, 241 insertions(+), 95 deletions(-)
 create mode 100644 src/Service/QuestionService.php
 delete mode 100644 templates/base.html.twig

diff --git a/composer.json b/composer.json
index 23b1d64..7088849 100644
--- a/composer.json
+++ b/composer.json
@@ -5,6 +5,7 @@
         "php": "^7.4",
         "ext-ctype": "*",
         "ext-iconv": "*",
+        "ext-json": "*",
         "symfony/asset": "5.0.*",
         "symfony/console": "5.0.*",
         "symfony/dotenv": "5.0.*",
diff --git a/composer.lock b/composer.lock
index 28e2958..6164a8b 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
         "This file is @generated automatically"
     ],
-    "content-hash": "2ae7f8988695aac7fdae21de9d92974f",
+    "content-hash": "c651c3f1932ebc2ce2b615802374a1cd",
     "packages": [
         {
             "name": "doctrine/annotations",
@@ -228,16 +228,16 @@
         },
         {
             "name": "doctrine/common",
-            "version": "v2.11.0",
+            "version": "2.12.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/doctrine/common.git",
-                "reference": "b8ca1dcf6b0dc8a2af7a09baac8d0c48345df4ff"
+                "reference": "2053eafdf60c2172ee1373d1b9289ba1db7f1fc6"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/doctrine/common/zipball/b8ca1dcf6b0dc8a2af7a09baac8d0c48345df4ff",
-                "reference": "b8ca1dcf6b0dc8a2af7a09baac8d0c48345df4ff",
+                "url": "https://api.github.com/repos/doctrine/common/zipball/2053eafdf60c2172ee1373d1b9289ba1db7f1fc6",
+                "reference": "2053eafdf60c2172ee1373d1b9289ba1db7f1fc6",
                 "shasum": ""
             },
             "require": {
@@ -307,7 +307,7 @@
                 "doctrine",
                 "php"
             ],
-            "time": "2019-09-10T10:10:14+00:00"
+            "time": "2020-01-10T15:49:25+00:00"
         },
         {
             "name": "doctrine/dbal",
@@ -823,16 +823,16 @@
         },
         {
             "name": "doctrine/migrations",
-            "version": "2.2.0",
+            "version": "2.2.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/doctrine/migrations.git",
-                "reference": "8e124252d2f6be1124017d746d5994dd4095d66f"
+                "reference": "a3987131febeb0e9acb3c47ab0df0af004588934"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/doctrine/migrations/zipball/8e124252d2f6be1124017d746d5994dd4095d66f",
-                "reference": "8e124252d2f6be1124017d746d5994dd4095d66f",
+                "url": "https://api.github.com/repos/doctrine/migrations/zipball/a3987131febeb0e9acb3c47ab0df0af004588934",
+                "reference": "a3987131febeb0e9acb3c47ab0df0af004588934",
                 "shasum": ""
             },
             "require": {
@@ -901,7 +901,7 @@
                 "migrations",
                 "php"
             ],
-            "time": "2019-11-13T11:06:31+00:00"
+            "time": "2019-12-04T06:09:14+00:00"
         },
         {
             "name": "doctrine/orm",
@@ -988,16 +988,16 @@
         },
         {
             "name": "doctrine/persistence",
-            "version": "1.3.3",
+            "version": "1.3.4",
             "source": {
                 "type": "git",
                 "url": "https://github.com/doctrine/persistence.git",
-                "reference": "99b196bbd4715a94fa100fac664a351ffa46d6a5"
+                "reference": "ff7e08b0f814be2cd20c52dc3c8a262579376b94"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/doctrine/persistence/zipball/99b196bbd4715a94fa100fac664a351ffa46d6a5",
-                "reference": "99b196bbd4715a94fa100fac664a351ffa46d6a5",
+                "url": "https://api.github.com/repos/doctrine/persistence/zipball/ff7e08b0f814be2cd20c52dc3c8a262579376b94",
+                "reference": "ff7e08b0f814be2cd20c52dc3c8a262579376b94",
                 "shasum": ""
             },
             "require": {
@@ -1067,20 +1067,20 @@
                 "orm",
                 "persistence"
             ],
-            "time": "2019-12-13T10:43:02+00:00"
+            "time": "2020-01-09T19:49:17+00:00"
         },
         {
             "name": "doctrine/reflection",
-            "version": "v1.0.0",
+            "version": "v1.1.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/doctrine/reflection.git",
-                "reference": "02538d3f95e88eb397a5f86274deb2c6175c2ab6"
+                "reference": "bc420ead87fdfe08c03ecc3549db603a45b06d4c"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/doctrine/reflection/zipball/02538d3f95e88eb397a5f86274deb2c6175c2ab6",
-                "reference": "02538d3f95e88eb397a5f86274deb2c6175c2ab6",
+                "url": "https://api.github.com/repos/doctrine/reflection/zipball/bc420ead87fdfe08c03ecc3549db603a45b06d4c",
+                "reference": "bc420ead87fdfe08c03ecc3549db603a45b06d4c",
                 "shasum": ""
             },
             "require": {
@@ -1088,13 +1088,15 @@
                 "ext-tokenizer": "*",
                 "php": "^7.1"
             },
+            "conflict": {
+                "doctrine/common": "<2.9"
+            },
             "require-dev": {
-                "doctrine/coding-standard": "^4.0",
-                "doctrine/common": "^2.8",
-                "phpstan/phpstan": "^0.9.2",
-                "phpstan/phpstan-phpunit": "^0.9.4",
-                "phpunit/phpunit": "^7.0",
-                "squizlabs/php_codesniffer": "^3.0"
+                "doctrine/coding-standard": "^5.0",
+                "doctrine/common": "^2.10",
+                "phpstan/phpstan": "^0.11.0",
+                "phpstan/phpstan-phpunit": "^0.11.0",
+                "phpunit/phpunit": "^7.0"
             },
             "type": "library",
             "extra": {
@@ -1112,6 +1114,10 @@
                 "MIT"
             ],
             "authors": [
+                {
+                    "name": "Guilherme Blanco",
+                    "email": "guilhermeblanco@gmail.com"
+                },
                 {
                     "name": "Roman Borschel",
                     "email": "roman@code-factory.org"
@@ -1120,10 +1126,6 @@
                     "name": "Benjamin Eberlei",
                     "email": "kontakt@beberlei.de"
                 },
-                {
-                    "name": "Guilherme Blanco",
-                    "email": "guilhermeblanco@gmail.com"
-                },
                 {
                     "name": "Jonathan Wage",
                     "email": "jonwage@gmail.com"
@@ -1137,12 +1139,13 @@
                     "email": "ocramius@gmail.com"
                 }
             ],
-            "description": "Doctrine Reflection component",
+            "description": "The Doctrine Reflection project is a simple library used by the various Doctrine projects which adds some additional functionality on top of the reflection functionality that comes with PHP. It allows you to get the reflection information about classes, methods and properties statically.",
             "homepage": "https://www.doctrine-project.org/projects/reflection.html",
             "keywords": [
-                "reflection"
+                "reflection",
+                "static"
             ],
-            "time": "2018-06-14T14:45:07+00:00"
+            "time": "2020-01-08T19:53:19+00:00"
         },
         {
             "name": "jdorn/sql-formatter",
@@ -1738,16 +1741,16 @@
         },
         {
             "name": "symfony/cache",
-            "version": "v5.0.1",
+            "version": "v5.0.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/cache.git",
-                "reference": "d8df2c5a3ba88430c559145bc8b944cb578f9d65"
+                "reference": "6e8d978878ae5de705ec9fabbb6011cc18776bc9"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/cache/zipball/d8df2c5a3ba88430c559145bc8b944cb578f9d65",
-                "reference": "d8df2c5a3ba88430c559145bc8b944cb578f9d65",
+                "url": "https://api.github.com/repos/symfony/cache/zipball/6e8d978878ae5de705ec9fabbb6011cc18776bc9",
+                "reference": "6e8d978878ae5de705ec9fabbb6011cc18776bc9",
                 "shasum": ""
             },
             "require": {
@@ -1813,7 +1816,7 @@
                 "caching",
                 "psr6"
             ],
-            "time": "2019-11-28T14:20:16+00:00"
+            "time": "2019-12-12T13:03:32+00:00"
         },
         {
             "name": "symfony/cache-contracts",
@@ -1939,16 +1942,16 @@
         },
         {
             "name": "symfony/console",
-            "version": "v5.0.1",
+            "version": "v5.0.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/console.git",
-                "reference": "dae5ef273d700771168ab889d9f8a19b2d206656"
+                "reference": "fe6e3cd889ca64172d7a742a2eb058541404ef47"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/console/zipball/dae5ef273d700771168ab889d9f8a19b2d206656",
-                "reference": "dae5ef273d700771168ab889d9f8a19b2d206656",
+                "url": "https://api.github.com/repos/symfony/console/zipball/fe6e3cd889ca64172d7a742a2eb058541404ef47",
+                "reference": "fe6e3cd889ca64172d7a742a2eb058541404ef47",
                 "shasum": ""
             },
             "require": {
@@ -2011,7 +2014,7 @@
             ],
             "description": "Symfony Console Component",
             "homepage": "https://symfony.com",
-            "time": "2019-12-01T10:51:15+00:00"
+            "time": "2019-12-17T13:20:22+00:00"
         },
         {
             "name": "symfony/dependency-injection",
@@ -2184,16 +2187,16 @@
         },
         {
             "name": "symfony/dotenv",
-            "version": "v5.0.1",
+            "version": "v5.0.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/dotenv.git",
-                "reference": "55a0afb9bf641e4cff66a0bc14918a9d9f6ecf2f"
+                "reference": "7e1bc9024edd9157264e388080df2533306894d3"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/dotenv/zipball/55a0afb9bf641e4cff66a0bc14918a9d9f6ecf2f",
-                "reference": "55a0afb9bf641e4cff66a0bc14918a9d9f6ecf2f",
+                "url": "https://api.github.com/repos/symfony/dotenv/zipball/7e1bc9024edd9157264e388080df2533306894d3",
+                "reference": "7e1bc9024edd9157264e388080df2533306894d3",
                 "shasum": ""
             },
             "require": {
@@ -2237,7 +2240,7 @@
                 "env",
                 "environment"
             ],
-            "time": "2019-11-26T23:25:11+00:00"
+            "time": "2019-12-19T16:01:11+00:00"
         },
         {
             "name": "symfony/error-handler",
@@ -2474,7 +2477,7 @@
         },
         {
             "name": "symfony/finder",
-            "version": "v5.0.1",
+            "version": "v5.0.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/finder.git",
@@ -2656,16 +2659,16 @@
         },
         {
             "name": "symfony/framework-bundle",
-            "version": "v5.0.1",
+            "version": "v5.0.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/framework-bundle.git",
-                "reference": "ef6f8cc6a13488f00f18e56dcd49272a538a82f3"
+                "reference": "36e51776b231d8e224da4ce4c60079540acd1c55"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/ef6f8cc6a13488f00f18e56dcd49272a538a82f3",
-                "reference": "ef6f8cc6a13488f00f18e56dcd49272a538a82f3",
+                "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/36e51776b231d8e224da4ce4c60079540acd1c55",
+                "reference": "36e51776b231d8e224da4ce4c60079540acd1c55",
                 "shasum": ""
             },
             "require": {
@@ -2683,6 +2686,7 @@
                 "symfony/routing": "^5.0"
             },
             "conflict": {
+                "doctrine/persistence": "<1.3",
                 "phpdocumentor/reflection-docblock": "<3.0",
                 "phpdocumentor/type-resolver": "<0.2.1",
                 "phpunit/phpunit": "<5.4.3",
@@ -2781,7 +2785,7 @@
             ],
             "description": "Symfony FrameworkBundle",
             "homepage": "https://symfony.com",
-            "time": "2019-11-28T14:20:16+00:00"
+            "time": "2019-12-17T10:33:13+00:00"
         },
         {
             "name": "symfony/http-foundation",
@@ -3517,16 +3521,16 @@
         },
         {
             "name": "symfony/routing",
-            "version": "v5.0.1",
+            "version": "v5.0.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/routing.git",
-                "reference": "145b6f63a0cc781c3300f48d9f250f9aded1e416"
+                "reference": "120c5fa4f4ef5466cbb510ece8126e0007285301"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/routing/zipball/145b6f63a0cc781c3300f48d9f250f9aded1e416",
-                "reference": "145b6f63a0cc781c3300f48d9f250f9aded1e416",
+                "url": "https://api.github.com/repos/symfony/routing/zipball/120c5fa4f4ef5466cbb510ece8126e0007285301",
+                "reference": "120c5fa4f4ef5466cbb510ece8126e0007285301",
                 "shasum": ""
             },
             "require": {
@@ -3589,7 +3593,7 @@
                 "uri",
                 "url"
             ],
-            "time": "2019-12-01T08:48:26+00:00"
+            "time": "2019-12-12T13:03:32+00:00"
         },
         {
             "name": "symfony/service-contracts",
@@ -4009,7 +4013,7 @@
         },
         {
             "name": "symfony/var-exporter",
-            "version": "v5.0.1",
+            "version": "v5.0.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/var-exporter.git",
@@ -4069,16 +4073,16 @@
         },
         {
             "name": "symfony/yaml",
-            "version": "v5.0.1",
+            "version": "v5.0.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/yaml.git",
-                "reference": "51b684480184fa767b97e28eaca67664e48dd3e9"
+                "reference": "847661e77afa48d99ecfa508e8b60f0b029a19c0"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/yaml/zipball/51b684480184fa767b97e28eaca67664e48dd3e9",
-                "reference": "51b684480184fa767b97e28eaca67664e48dd3e9",
+                "url": "https://api.github.com/repos/symfony/yaml/zipball/847661e77afa48d99ecfa508e8b60f0b029a19c0",
+                "reference": "847661e77afa48d99ecfa508e8b60f0b029a19c0",
                 "shasum": ""
             },
             "require": {
@@ -4124,7 +4128,7 @@
             ],
             "description": "Symfony Yaml Component",
             "homepage": "https://symfony.com",
-            "time": "2019-11-18T17:27:11+00:00"
+            "time": "2019-12-10T11:06:55+00:00"
         },
         {
             "name": "twig/twig",
@@ -4246,7 +4250,8 @@
     "platform": {
         "php": "^7.4",
         "ext-ctype": "*",
-        "ext-iconv": "*"
+        "ext-iconv": "*",
+        "ext-json": "*"
     },
     "platform-dev": []
 }
diff --git a/public/js/index.js b/public/js/index.js
index e83e18e..7a3e6f6 100644
--- a/public/js/index.js
+++ b/public/js/index.js
@@ -1,4 +1,10 @@
-let dataset = { t: null, a: 0, c: 0, w: 0};
+let dataset = {
+    t: null,
+    aIds: [],
+    a: 0,
+    c: 0,
+    w: 0
+};
 
 /**
  * @param {string} slctr
@@ -90,8 +96,9 @@ function getNextQuestion() {
             alert('Error! Returned status ' + this.status.toString(10));
         }
     };
-    request.open("GET", "/next/" + encodeURIComponent(dataset.t), true);
-    request.send();
+    request.open("POST", "/next", true);
+    request.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
+    request.send(JSON.stringify({ t: dataset.t, ids: dataset.aIds }));
 }
 
 function startQuiz() {
diff --git a/src/Controller/MainController.php b/src/Controller/MainController.php
index a5495ad..2d1b1ae 100644
--- a/src/Controller/MainController.php
+++ b/src/Controller/MainController.php
@@ -2,12 +2,16 @@
 
 namespace App\Controller;
 
+use App\Entity\Question;
 use App\Entity\Topic;
+use App\Repository\QuestionRepository;
 use App\Repository\TopicRepository;
+use App\Service\QuestionService;
 use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
 use Symfony\Component\HttpFoundation\JsonResponse;
 use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
 use Symfony\Component\Routing\Annotation\Route;
 
 /**
@@ -18,9 +22,12 @@ class MainController extends AbstractController
 {
     private TopicRepository $topicRepository;
 
-    public function __construct(TopicRepository $topicRepository)
+    private QuestionService $questionService;
+
+    public function __construct(TopicRepository $topicRepository, QuestionService $questionService)
     {
         $this->topicRepository = $topicRepository;
+        $this->questionService = $questionService;
     }
 
     /**
@@ -32,18 +39,24 @@ class MainController extends AbstractController
     }
 
     /**
-     * @Route(name="next_question", path="/next/{topic}", methods={"GET"})
+     * @Route(name="next_question", path="/next", methods={"POST"})
      * @param Request $req
-     * @param string $topic
      * @return Response
      */
-    public function nextQuestion(Request $req, string $topic = Topic::TOPIC_ALL) : Response
+    public function nextQuestion(Request $req) : Response
     {
+        /** @var resource|string $content */
+        if (!$content = $req->getContent()) {
+            throw new BadRequestHttpException('No payload!');
+        }
 
+        /** @var array $data */
+        $data = json_decode($content, true);
+        return new JsonResponse($this->questionService->generateNextQuestion($data['t'], $data['ids']));
     }
 
     /**
-     * @Route(name="evaluate_question", path=/eval, methods={"POST"})
+     * @Route(name="evaluate_question", path="/eval", methods={"POST"})
      * @param Request $req
      * @return Response
      */
@@ -56,5 +69,6 @@ class MainController extends AbstractController
          *    <id> : <checked>
          *  }
          */
+        return null;
     }
 }
\ No newline at end of file
diff --git a/src/Entity/Answer.php b/src/Entity/Answer.php
index 8260926..4dea10c 100644
--- a/src/Entity/Answer.php
+++ b/src/Entity/Answer.php
@@ -19,7 +19,7 @@ class Answer
      */
     protected int $id;
 
-    /** @ORM\ManyToOne(targetEntity="App\Entity\Question")  */
+    /** @ORM\ManyToOne(fetch="EAGER", targetEntity="App\Entity\Question", inversedBy="answers")  */
     protected Question $question;
 
     /** @ORM\Column(columnDefinition="VARCHAR(1024)", length=1024)  */
@@ -27,6 +27,11 @@ class Answer
 
     protected bool $correct;
 
+    public function getId() : ?int
+    {
+        return $this->id;
+    }
+
     public function getLabel() : string
     {
         return $this->label;
diff --git a/src/Entity/Question.php b/src/Entity/Question.php
index de7405b..49e3723 100644
--- a/src/Entity/Question.php
+++ b/src/Entity/Question.php
@@ -3,6 +3,7 @@
 
 namespace App\Entity;
 
+use Doctrine\Common\Collections\ArrayCollection;
 use Doctrine\ORM\Mapping as ORM;
 use Symfony\Component\Routing\Exception\InvalidParameterException;
 
@@ -28,7 +29,7 @@ class Question
     ];
 
     const CONTENT_TYPES = [
-        self::QCTYPE_DEFINITION.
+        self::QCTYPE_DEFINITION,
         self::QCTYPE_TARGET
     ];
 
@@ -59,7 +60,17 @@ class Question
      */
     protected string $contentType;
 
-    public function setQSType(string $structureType) : self
+    /**
+     * @ORM\OneToMany(cascade={"persist", "remove"}, fetch="EAGER", mappedBy="question", targetEntity="App\Entity\Answer")
+     */
+    protected ArrayCollection $answers;
+
+    public function __construct()
+    {
+        $this->answers = new ArrayCollection();
+    }
+
+    public function setStructureType(string $structureType): self
     {
         if (!in_array($structureType, self::STRUCTURE_TYPES)) {
             throw new InvalidParameterException(sprintf(
@@ -72,7 +83,7 @@ class Question
         return $this;
     }
 
-    private function setQCType(string $contentType) : self
+    public function setContent(string $contentType): self
     {
         if (!in_array($contentType, self::CONTENT_TYPES)) {
             throw new InvalidParameterException(sprintf(
@@ -84,4 +95,45 @@ class Question
         $this->contentType = $contentType;
         return $this;
     }
+
+    public function addAnswer(Answer $answer) : self
+    {
+        $this->answers->add($answer);
+        return $this;
+    }
+
+    public function getAnswers(): ArrayCollection
+    {
+        return $this->answers;
+    }
+
+    public function getLabel(): ?string
+    {
+        return $this->label;
+    }
+
+    public function getStructureType() : ?string
+    {
+        return $this->structureType;
+    }
+
+    public function getContentType() : ?string
+    {
+        return $this->contentType;
+    }
+
+    public function getId(): ?int
+    {
+        return $this->id;
+    }
+
+    public function isSingleChoice(): bool
+    {
+        return self::QSTYPE_SINGLE_CHOICE === $this->structureType;
+    }
+
+    public function isMultipleChoiceRandomWrongs(): bool
+    {
+        return self::QSTYPE_MULTIPLE_CHOICE_RANDOM_WRONGS === $this->structureType;
+    }
 }
\ No newline at end of file
diff --git a/src/Repository/AnswerRepository.php b/src/Repository/AnswerRepository.php
index 8d4757e..b20092e 100644
--- a/src/Repository/AnswerRepository.php
+++ b/src/Repository/AnswerRepository.php
@@ -17,7 +17,7 @@ class AnswerRepository extends ServiceEntityRepository
         parent::__construct($registry, Answer::class);
     }
 
-    public function findAllByTopicAndContentType($topic, $contentType) : array
+    public function findAllByTopicAndContentType(string $topic, string $contentType) : array
     {
         return $this
             ->createQueryBuilder('a')
diff --git a/src/Repository/QuestionRepository.php b/src/Repository/QuestionRepository.php
index 895da50..232f6b9 100644
--- a/src/Repository/QuestionRepository.php
+++ b/src/Repository/QuestionRepository.php
@@ -17,15 +17,18 @@ class QuestionRepository extends ServiceEntityRepository
         parent::__construct($registry, Question::class);
     }
 
-    public function findAllByTopic(string $topicLabel) : array
+    public function findAllByTopicExcludeIds(string $topicLabel, array $excludeIds = []) : array
     {
-        return $this
+        $qb = $this
             ->createQueryBuilder('q')
             ->leftJoin('q.topic', 't')
             ->where('t.label = ?1')
             ->setParameter(1, $topicLabel)
-            ->getQuery()
-            ->getArrayResult()
         ;
+        if (count($excludeIds) > 0) {
+            $qb->andWhere('q.id NOT IN ?2')->setParameter(2, $excludeIds);
+        }
+
+        return $qb->getQuery()->getArrayResult();
     }
 }
\ No newline at end of file
diff --git a/src/Service/QuestionService.php b/src/Service/QuestionService.php
new file mode 100644
index 0000000..bc9cde0
--- /dev/null
+++ b/src/Service/QuestionService.php
@@ -0,0 +1,71 @@
+<?php
+
+namespace App\Service;
+
+use App\Entity\Answer;
+use App\Entity\Question;
+use App\Repository\AnswerRepository;
+use App\Repository\QuestionRepository;
+use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
+
+/**
+ * Class QuestionService
+ * @package App\Service
+ */
+class QuestionService
+{
+    private QuestionRepository $questionRepository;
+
+    private AnswerRepository $answerRepository;
+
+    public function __construct(QuestionRepository $questionRepository, AnswerRepository $answerRepository)
+    {
+        $this->questionRepository = $questionRepository;
+        $this->answerRepository = $answerRepository;
+    }
+
+    public function generateNextQuestion(string $topic, array $excludeIds = []) : array
+    {
+        /** @var Question[] $questions */
+        $questions = $this->questionRepository->findAllByTopicExcludeIds($topic, $excludeIds);
+        if (count($questions) === 0) {
+            throw new NotFoundHttpException('Next question not found!');
+        }
+
+        /** @var Question $target */
+        $target = $questions[array_rand($questions)];
+
+        if ($target->isSingleChoice() || $target->isMultipleChoiceRandomWrongs()) {
+            /** @var Answer[] $additionalAnswers */
+            $additionalAnswers = $this->answerRepository->findAllByTopicAndContentType($topic, $target->getContentType());
+            if (count($additionalAnswers) === 0) {
+                throw new NotFoundHttpException('Additional answers not found');
+            }
+
+            for ($i = 0; $i < (5 - $target->getAnswers()->count()); $i++) {
+                /** @var int $rnd */
+                $rnd = array_rand($additionalAnswers);
+                $target->addAnswer($additionalAnswers[$rnd]);
+                unset($additionalAnswers[$rnd]);
+            }
+        }
+
+        return [
+            'id' => $target->getId(),
+            'type' => $target->getStructureType(),
+            'label' => $target->getLabel(),
+            'answers' => array_map(function ($answer) {
+                /** @var Answer $answer */
+                return [
+                    'id' => $answer->getId(),
+                    'label' => $answer->getLabel()
+                ];
+            }, $target->getAnswers()->getValues())
+        ];
+    }
+
+    public function evaluateQuestion(int $questionId, array $answers) : bool
+    {
+        return false;
+    }
+}
diff --git a/templates/base.html.twig b/templates/base.html.twig
deleted file mode 100644
index 043f42d..0000000
--- a/templates/base.html.twig
+++ /dev/null
@@ -1,12 +0,0 @@
-<!DOCTYPE html>
-<html>
-    <head>
-        <meta charset="UTF-8">
-        <title>{% block title %}Welcome!{% endblock %}</title>
-        {% block stylesheets %}{% endblock %}
-    </head>
-    <body>
-        {% block body %}{% endblock %}
-        {% block javascripts %}{% endblock %}
-    </body>
-</html>
-- 
GitLab