В первой части этой серии мы начали, используя подход проектирования снизу вверх, с создания сущностей Quiz
Question
Data Mapper для сущностей Quiz
\QuizApp\Service\Quiz
\QuizApp\Service\Quiz
Если вы еще не прочитали первую часть, я предлагаю вам быстро просмотреть ее, прежде чем продолжить со второй частью и / или загрузить код отсюда .
На этот раз мы создадим и <?php
namespace QuizApp\Service;
use QuizApp\Service\Quiz\Result;
// ...
class Quiz implements QuizInterface
{
const CURRENT_QUIZ = 'quizService_currentQuiz';
const CURRENT_QUESTION = 'quizService_currentQuestion';
const CORRECT = 'quizService_correct';
const INCORRECT = 'quizService_incorrect';
private $mapper;
public function __construct(\QuizApp\Mapper\QuizInterface $mapper)
{
$this->mapper = $mapper;
}
/** @return Quiz[] */
public function showAllQuizes()
{
return $this->mapper->findAll();
}
public function startQuiz($quizOrId)
{
if (!($quizOrId instanceof \QuizApp\Entity\Quiz)) {
$quizOrId = $this->mapper->find($quizOrId);
if ($quizOrId === null) {
throw new \InvalidArgumentException('Quiz not found');
}
}
$_SESSION[self::CURRENT_QUIZ] = $quizOrId->getId();
$_SESSION[self::CORRECT] = $_SESSION[self::INCORRECT] = 0;
}
/**
* @return Question
* @throws \LogicException
*/
public function getQuestion()
{
$questions = $this->getCurrentQuiz()->getQuestions();
$currentQuestion = $this->getCurrentQuestionId();
if ($this->isOver()) {
throw new \LogicException();
}
return $questions[$currentQuestion];
}
/** @return bool */
public function checkSolution($solutionId)
{
$result = $this->getQuestion()->isCorrect($solutionId);
$_SESSION[self::CURRENT_QUESTION] = $this->getCurrentQuestionId() + 1;
$this->addResult($result);
if ($this->isOver()) {
$_SESSION[self::CURRENT_QUESTION] = $_SESSION[self::CURRENT_QUIZ] = null;
}
return $result;
}
/** @return bool */
public function isOver()
{
try {
return $this->getCurrentQuestionId() >= count($this->getCurrentQuiz()->getQuestions());
} catch (\LogicException $e) {
return true;
}
}
/** @return Result */
public function getResult()
{
return new Result(
$_SESSION[self::CORRECT], $_SESSION[self::INCORRECT],
($_SESSION[self::CORRECT] + $_SESSION[self::INCORRECT]) / 2
);
}
private function getCurrentQuiz()
{
if (!isset($_SESSION[self::CURRENT_QUIZ])) {
throw new \LogicException();
}
$quiz = $this->mapper->find($_SESSION[self::CURRENT_QUIZ]);
if ($quiz === null) {
throw new \LogicException();
}
return $quiz;
}
private function getCurrentQuestionId()
{
return isset ($_SESSION[self::CURRENT_QUESTION]) ? $_SESSION[self::CURRENT_QUESTION] : 0;
}
private function addResult($isCorrect)
Затем мы напишем наши контроллеры и представления с использованием фреймворка Slim MVC и, наконец, создадим преобразователь MongoDB на место подставного преобразователя, который мы писали в прошлый раз.
{
$type = ($isCorrect ? self::CORRECT : self::INCORRECT);
if (!isset($_SESSION[$type])) {
$_SESSION[$type] = 0;
}
$_SESSION[$type] += 1;
}
}
Кодирование Сервиса:
Хорошо, теперь, когда мы определили интерфейс для картографа и создали классы сущностей, у нас есть все строительные блоки, необходимые для реализации конкретного класса обслуживания.
showAllQuizes()
Это долго. Давайте пройдемся по этому методу методом.
Метод QuizMapper::findAll()
$mapper
Мы могли бы сделать startQuiz()
Метод $mapper
Он принимает либо объект сущности викторины, либо идентификатор викторины. В последнем случае он пытается найти тест, используя $_SESSION
В этом методе используется $_SESSION
Если бы мы хотели, чтобы служба работала в командной строке, мы извлекли бы операции, которые мы использовали для хранения данных в сеансе, в интерфейс. Веб-контроллер будет передавать реализацию, которая внутренне использует getQuestion()
Метод checkSolution()
Метод isOver()
Метод getResult()
Метод \QuizApp\Service\Quiz\Result
index.php
Контроллеры и представления с Slim:
Теперь, когда мы завершили настройку «M» нашего приложения MVC, пришло время написать наши контроллеры и представления. Мы используем фреймворк Slim, но его легко заменить на любой другой фреймворк MVC, поскольку наш код отделен. Создайте файл <?php
require 'vendor/autoload.php';
session_start();
$service = new \QuizApp\Service\Quiz(
new \QuizApp\Mapper\HardCoded()
);
$app = new \Slim\Slim();
$app->config(['templates.path' => './views']);
// Controller actions here
$app->run();
$_SESSION
Это основа нашего приложения Slim. Мы создаем наш сервис и запускаем сеанс PHP, так как мы используем index.php
Наконец, мы настроили наше приложение Slim. Для получения дополнительной информации о Slim ознакомьтесь с обширной документацией на веб-сайте проекта Slim .
Давайте сначала создадим домашнюю страницу. На главной странице будут перечислены тесты, которые может принять пользователь. Код контроллера для этого прост. Добавьте следующий комментарий в нашем файле $app->get('/', function () use ($service, $app) {
$app->render('choose-quiz.phtml', [
'quizes' => $service->showAllQuizes()
]);}
);
$app->get()
Мы определяем маршрут домашней страницы с помощью choose-quiz.phtml
Мы передаем маршрут в качестве первого параметра и передаем код для запуска в качестве второго параметра в форме анонимной функции. В функции мы визуализируем файл представления <h3>choose a quiz</h3>
Давайте закодируем представление.
<ul>
<?php foreach ($quizes as $quiz) : ?>
<li><a href="choose-quiz/<?php echo $quiz->getId();?>"><?php echo $quiz->getTitle(); ?></a></li>
<?php endforeach; ?>
</ul>
choose-quiz/:id
На этом этапе, если вы перейдете на домашнюю страницу приложения с помощью браузера, вы увидите два теста, которые мы жестко запрограммировали ранее: «Тест 1» и «Тест 2».
Ссылки на викторину на домашней странице указывают на :id
index.php
Этот маршрут должен запустить тест, выбранный пользователем, и перенаправить его на свой первый вопрос. Добавьте следующий маршрут к $app->get('/choose-quiz/:id', function($id) use ($service, $app) {
$service->startQuiz($id);
$app->redirect('/solve-question');
});