aboutsummaryrefslogtreecommitdiffhomepage
path: root/services/app/src/Forms/AnswerNewForm.php
blob: 809b03a140f1a569047101a5d9c5037f847b7de3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
<?php

declare(strict_types=1);

namespace Nsfisis\Albatross\Forms;

use Nsfisis\Albatross\Database\Connection;
use Nsfisis\Albatross\Exceptions\EntityValidationException;
use Nsfisis\Albatross\Form\FormBase;
use Nsfisis\Albatross\Form\FormItem;
use Nsfisis\Albatross\Form\FormState;
use Nsfisis\Albatross\Form\FormSubmissionFailureException;
use Nsfisis\Albatross\Models\Answer;
use Nsfisis\Albatross\Models\Quiz;
use Nsfisis\Albatross\Models\User;
use Nsfisis\Albatross\Repositories\AnswerRepository;
use Nsfisis\Albatross\Repositories\TestcaseExecutionRepository;
use Slim\Interfaces\RouteParserInterface;

final class AnswerNewForm extends FormBase
{
    private ?Answer $answer = null;

    public function __construct(
        ?FormState $state,
        private readonly User $currentUser,
        private readonly Quiz $quiz,
        private readonly RouteParserInterface $routeParser,
        private readonly AnswerRepository $answerRepo,
        private readonly TestcaseExecutionRepository $testcaseExecutionRepo,
        private readonly Connection $conn,
    ) {
        if (!isset($state)) {
            $state = new FormState();
        }
        parent::__construct($state);
    }

    public function pageTitle(): string
    {
        return "問題 #{$this->quiz->quiz_id} - 提出";
    }

    public function redirectUrl(): string
    {
        $answer = $this->answer;
        assert(isset($answer));
        return $this->routeParser->urlFor(
            'answer_view',
            ['qslug' => $this->quiz->slug, 'anum' => "$answer->answer_number"],
        );
    }

    protected function submitLabel(): string
    {
        return '投稿';
    }

    /**
     * @return list<FormItem>
     */
    protected function items(): array
    {
        return [
            new FormItem(
                name: 'code',
                type: 'textarea',
                label: 'コード',
                isRequired: true,
                extra: 'rows=3 cols=80',
            ),
        ];
    }

    /**
     * @return array{quiz: Quiz, is_closed: bool}
     */
    public function getRenderContext(): array
    {
        return [
            'quiz' => $this->quiz,
            'is_closed' => $this->quiz->isClosedToAnswer(),
        ];
    }

    public function submit(): void
    {
        if ($this->quiz->isClosedToAnswer()) {
            $this->state->setErrors(['general' => 'この問題の回答は締め切られました']);
            throw new FormSubmissionFailureException();
        }

        $code = $this->state->get('code') ?? '';

        try {
            $answer_id = $this->conn->transaction(function () use ($code) {
                $answer_id = $this->answerRepo->create(
                    $this->quiz->quiz_id,
                    $this->currentUser->user_id,
                    $code,
                );
                $this->testcaseExecutionRepo->enqueueForSingleAnswer(
                    $answer_id,
                    $this->quiz->quiz_id,
                );
                return $answer_id;
            });
        } catch (EntityValidationException $e) {
            $this->state->setErrors($e->toFormErrors());
            throw new FormSubmissionFailureException(previous: $e);
        }
        $answer = $this->answerRepo->findById($answer_id);
        assert(isset($answer));
        $this->answer = $answer;
    }
}