quiz_maker-1.0.6/src/Form/QuizTakeForm.php

src/Form/QuizTakeForm.php
<?php

namespace Drupal\quiz_maker\Form;

use Drupal\Component\Datetime\TimeInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\quiz_maker\Event\QuestionNavigationEvent;
use Drupal\quiz_maker\Event\QuizTakeEvents;
use Drupal\quiz_maker\QuizInterface;
use Drupal\quiz_maker\Service\QuizSession;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;

/**
 * Class of Quiz take form.
 */
class QuizTakeForm extends FormBase {

  use StringTranslationTrait;

  /**
   * Current request.
   *
   * @var \Symfony\Component\HttpFoundation\Request|null
   */
  protected ?Request $currentRequest;

  /**
   * Time when quiz was started.
   *
   * @var int
   *   The timestamp.
   */
  protected int $started;

  /**
   * Constructs a new \Drupal\quiz_maker\Form\QuizTakeForm object.
   *
   * @param \Symfony\Component\HttpFoundation\RequestStack $requestStack
   *   The request stack service.
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager
   *   The entity type manager.
   * @param \Drupal\Component\Datetime\TimeInterface $time
   *   The time service.
   * @param \Symfony\Contracts\EventDispatcher\EventDispatcherInterface $eventDispatcher
   *   The event dispatcher service.
   * @param \Drupal\quiz_maker\Service\QuizSession $quizSession
   *   The quiz session service.
   */
  public function __construct(
    RequestStack $requestStack,
    protected EntityTypeManagerInterface $entityTypeManager,
    protected TimeInterface $time,
    protected EventDispatcherInterface $eventDispatcher,
    protected QuizSession $quizSession,
  ) {
    $this->currentRequest = $requestStack->getCurrentRequest();
    $this->started = $this->time->getCurrentTime();
  }

  /**
   * {@inheritDoc}
   */
  public static function create(ContainerInterface $container) {
    return new static(
      $container->get('request_stack'),
      $container->get('entity_type.manager'),
      $container->get('datetime.time'),
      $container->get('event_dispatcher'),
      $container->get('quiz_maker.quiz_session'),
    );
  }

  /**
   * {@inheritDoc}
   */
  public function getFormId(): string {
    return 'quiz_take_form';
  }

  /**
   * {@inheritDoc}
   */
  public function buildForm(array $form, FormStateInterface $form_state, QuizInterface|null $quiz = NULL): array {

    $form['question'] = [
      '#type' => 'container',
      '#attributes' => [
        'id' => 'question',
      ],
      '#weight' => 1,
    ];

    if ($quiz) {
      // Start quiz session if it isn't exist.
      if ($this->quizSession->hasSession($quiz) === FALSE) {
        $this->quizSession->startSession($quiz);
      }

      $questions = $this->quizSession->getQuestions($quiz);
      $time_limit = $quiz->getTimeLimit();

      if ($time_limit) {
        $time_left = $this->quizSession->getTimeLeft($quiz, $time_limit);
        $form['timer_block'] = [
          '#type' => 'container',
          '#attributes' => [
            'id' => 'quiz-timer',
            'class' => ['quiz-timer'],
          ],
          '#weight' => 0,
        ];

        $form['timer_block']['value'] = [
          '#theme' => 'timer',
          '#hours' => gmdate('H', $time_left),
          '#minutes' => gmdate('i', $time_left),
          '#seconds' => gmdate('s', $time_left),
        ];

        $form['#attached']['drupalSettings']['time_limit'] = $time_limit * 1000;
        $form['#attached']['drupalSettings']['started_time'] = (int) $this->quizSession->getQuizResult($quiz)->get('created')->value * 1000;
        $form['#attached']['library'][] = 'quiz_maker/quiz_timer';
      }

      // Set data from user input.
      $user_input = $form_state->getUserInput();
      if ($user_input && isset($user_input['question_navigation'])) {
        $question_number = $user_input['question_navigation'];
        $this->quizSession->setCurrentQuestionNumber($quiz, $question_number);
      }
      else {
        $this->quizSession->setCurrentQuestionNumber($quiz, 0);
      }

      $current_question = $this->quizSession->getCurrentQuestion($quiz);

      $form['question']['number'] = [
        '#type' => 'html_tag',
        '#tag' => 'div',
        '#value' => $this->t('Question @current/@all', [
          '@current' => $this->quizSession->getCurrentQuestionNumber($quiz) + 1,
          '@all' => count($questions),
        ]),
        '#attributes' => [
          'class' => 'question-number',
        ],
      ];

      $form['question']['question_navigation'] = [
        '#type' => 'radios',
        '#options' => range(1, count($questions)),
        '#default_value' => $this->quizSession->getCurrentQuestionNumber($quiz),
        '#prefix' => '<div class="question-navigation">',
        '#suffix' => '</div>',
        '#ajax' => [
          'callback' => '::getQuestion',
          'wrapper' => 'question',
          'progress' => 'none',
        ],
        '#disabled' => !$quiz->allowJumping() || !$quiz->allowBackwardNavigation(),
      ];

      if (!$quiz->requireManualAssessment()) {
        // Add class to question number if it has response.
        $form['question']['question_navigation']['#after_build'] = ['::getQuestionNumberClass'];
      }

      $form['question']['title'] = [
        '#type' => 'html_tag',
        '#tag' => 'h3',
        '#value' => $current_question->getQuestion(),
      ];

      $current_question_response = $this->quizSession->getQuizResult($quiz)->getResponse($current_question);
      $allow_change_response = $quiz->allowChangeAnswer() || !$this->quizSession->getQuizResult($quiz)->getResponse($current_question);
      $form['question']['answer_form'] = $current_question->getAnsweringForm($current_question_response, $allow_change_response);

      $form['question']['navigation']['actions'] = [
        '#type' => 'actions',
      ];

      if ($this->quizSession->getCurrentQuestionNumber($quiz) > 0 && $quiz->allowBackwardNavigation()) {
        $form['question']['navigation']['actions']['previous'] = [
          '#type' => 'submit',
          '#value' => $this->t('Previous'),
          '#submit' => ['::getPreviousQuestion'],
          '#weight' => 0,
          '#ajax' => [
            'callback' => '::updateQuestionForm',
            'wrapper' => 'question',
            'event' => 'click',
            'progress' => [
              'type' => 'throbber',
              'message' => $this->t('Go to the next question...'),
            ],
          ],
          '#limit_validation_errors' => [],
        ];
      }

      if ($this->quizSession->getCurrentQuestionNumber($quiz) < (count($questions) - 1)) {
        $form['question']['navigation']['actions']['next'] = [
          '#type' => 'submit',
          '#value' => $this->t('Next'),
          '#submit' => ['::getNextQuestion'],
          '#weight' => 1,
          '#ajax' => [
            'callback' => '::updateQuestionForm',
            'wrapper' => 'question',
            'event' => 'click',
            'progress' => [
              'type' => 'throbber',
              'message' => $this->t('Go to the next question...'),
            ],
          ],
        ];
      }

      $form['question']['navigation']['actions']['finish'] = [
        '#type' => 'submit',
        '#value' => $this->t('Finish'),
        '#submit' => ['::submitForm'],
        '#attributes' => [
          'id' => ['quiz-finish'],
        ],
      ];

      if ($this->quizSession->getCurrentQuestionNumber($quiz) != (count($questions)) - 1) {
        $form['question']['navigation']['actions']['finish']['#attributes']['class'] = [
          'hidden',
        ];
      }

    }
    else {
      $form['question']['error'] = [
        '#type' => 'html_tag',
        '#tag' => 'div',
        '#value' => $this->t("Quiz doesn't have th questions."),
      ];

    }

    $form['#cache'] = [
      'contexts' => [
        'session',
        'user',
      ],
    ];

    return $form;
  }

  /**
   * {@inheritDoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state, QuizInterface|null $quiz = NULL): void {
    $quiz = $this->getQuiz();
    if ($quiz instanceof QuizInterface) {
      $current_question = $this->quizSession->getCurrentQuestion($quiz);
      $response_data = $current_question->getResponse($form, $form_state);
      $quiz_result_id = $this->quizSession->getQuizResult($quiz)->id();
      $this->quizSession->finishSession($quiz, $response_data);
      $form_state->setRedirect('entity.quiz_result.canonical', [
        'quiz_result' => $quiz_result_id,
      ]);
    }
  }

  /**
   * {@inheritDoc}
   */
  public function validateForm(array &$form, FormStateInterface $form_state) {
    $quiz = $this->getQuiz();
    if ($quiz instanceof QuizInterface) {
      /** @var \Drupal\quiz_maker\QuestionInterface $current_question */
      $current_question = $this->quizSession->getCurrentQuestion($quiz);
      if (!$quiz->allowSkipping()) {
        $current_question->validateAnsweringForm($form, $form_state);
      }
    }
  }

  /**
   * Get update question form ans save answer.
   *
   * @param array $form
   *   The form array.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   * @param \Symfony\Component\HttpFoundation\Request $request
   *   The request.
   */
  public function updateQuestionForm(array &$form, FormStateInterface $form_state, Request $request): mixed {
    return $form['question'];
  }

  /**
   * Get previous next via ajax.
   *
   * @param array $form
   *   The form array.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   */
  public function getNextQuestion(array &$form, FormStateInterface $form_state): void {
    $quiz = $this->getQuiz();
    if ($quiz instanceof QuizInterface) {
      $current_question = $this->quizSession->getCurrentQuestion($quiz);
      $response_data = $current_question?->getResponse($form, $form_state);
      $question_number = $this->quizSession->incrementQuestionNumber($quiz, $response_data);

      $question_navigation_event = new QuestionNavigationEvent($quiz, $current_question, $this->quizSession->getQuizResult($quiz)->getUser(), $question_number);
      $this->eventDispatcher->dispatch($question_navigation_event, QuizTakeEvents::NEXT_QUESTION);

      $user_input = $form_state->getUserInput();
      $user_input['question_navigation'] = $question_number;
      $form_state->setUserInput($user_input);
      $form_state->setRebuild(TRUE);
    }
  }

  /**
   * Get question.
   *
   * @param array $form
   *   The form.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   *
   * @return array
   *   The form element.
   */
  public function getQuestion(array &$form, FormStateInterface $form_state): array {
    return $form['question'];
  }

  /**
   * Get previous question via ajax.
   *
   * @param array $form
   *   The form array.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   */
  public function getPreviousQuestion(array &$form, FormStateInterface $form_state): void {
    $quiz = $this->getQuiz();
    if ($quiz instanceof QuizInterface) {
      $current_question = $this->quizSession->getCurrentQuestion($quiz);
      $question_number = $this->quizSession->decrementQuestionNumber($quiz);

      $question_navigation_event = new QuestionNavigationEvent($quiz, $current_question, $this->quizSession->getQuizResult($quiz)->getUser(), $question_number);
      $this->eventDispatcher->dispatch($question_navigation_event, QuizTakeEvents::PREVIOUS_QUESTION);

      $user_input = $form_state->getUserInput();
      $user_input['question_navigation'] = $question_number;
      $form_state->setUserInput($user_input);
      $form_state->setRebuild(TRUE);
    }
  }

  /**
   * Add class to radios of question number.
   *
   * @param array $element
   *   The element.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   *
   * @return array
   *   The element array.
   */
  public function getQuestionNumberClass(array $element, FormStateInterface $form_state): array {
    $quiz = $this->getQuiz();
    if ($quiz instanceof QuizInterface) {
      $question_numbers = array_keys($element['#options']);
      $questions = $this->quizSession->getQuestions($quiz);
      foreach ($question_numbers as $question_number) {
        /** @var \Drupal\quiz_maker\QuestionInterface $question */
        $question = $questions[$question_number];
        $question_response = $this->quizSession->getQuizResult($quiz)->getResponse($question);
        if ($question_response) {
          $element[$question_number]['#attributes']['class'][] = $question_response->isCorrect() ? 'correct' : 'in-correct';
        }
      }
    }

    return $element;
  }

  /**
   * Get quiz from request.
   *
   * @return \Drupal\quiz_maker\QuizInterface|null
   *   The quiz.
   */
  private function getQuiz(): ?QuizInterface {
    $quiz = $this->currentRequest->get('quiz');
    if ($quiz instanceof QuizInterface) {
      return $quiz;
    }
    return NULL;
  }

}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc