Completed
Push — master ( 280aac...64cd8c )
by Francesco
03:10
created

CompilationService::isFreeTextQuestion()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 19
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 8
nc 3
nop 1
dl 0
loc 19
rs 10
c 0
b 0
f 0
1
<?php
2
declare(strict_types = 1);
3
4
namespace App\Services;
5
6
use App\Models\Answer;
7
use App\Models\Location;
8
use App\Models\Question;
9
use App\Models\Section;
10
use App\Models\Ward;
11
use Illuminate\Database\Eloquent\Builder;
12
use Illuminate\Database\Eloquent\Collection as EloquentCollection;
13
use Illuminate\Support\Collection;
14
15
class CompilationService
16
{
17
18
    /**
19
     * @var EloquentCollection all questionnaire sections, also deleted
20
     */
21
    private $sections = null;
22
23
    /**
24
     * @var EloquentCollection all questionnaire questions, also deleted
25
     */
26
    private $questions = null;
27
28
    /**
29
     * @var EloquentCollection all questionnaire dynamic answers (i.e. based on database/seeds/*.json files), also deleted
30
     */
31
    private $answers = null;
32
33
    /**
34
     * @var Collection all questionnaire fixed questions (see __construct() method for details), also deleted
35
     */
36
    private $otherQuestions = null;
37
38
    /**
39
     * @var array all questionnaire "other" answers (see __construct() method for details), also deleted
40
     */
41
    private $otherAnswers = [];
42
43
    public function __construct(CountryService $countryService)
44
    {
45
        $this->sections = Section::withTrashed()->get()->keyBy("id");
46
        $this->questions = Question::withTrashed()->get()->keyBy("id");
47
        $this->answers = Answer::withTrashed()->get()->keyBy("id");
48
49
        // Questions whose answers are located on other tables
50
        // and that are not derived from database/seeds/*.json files
51
        $this->otherQuestions = new Collection([
52
            "stage_location_id" => __("Location"),
53
            "stage_ward_id" => __("Ward"),
54
            "stage_start_date" => __("Start date"),
55
            "stage_end_date" => __("End date"),
56
            "stage_academic_year" => __("Academic year"),
57
            "stage_weeks" => __("Weeks"),
58
            "student_gender" => __("Gender"),
59
            "student_nationality" => __("Nationality"),
60
        ]);
61
62
        // Answers located on other tables
63
        $this->otherAnswers["__stage_locations__"] = Location::withTrashed()->get()->keyBy("id");
64
        $this->otherAnswers["__stage_wards__"] = Ward::withTrashed()->get()->keyBy("id");
65
66
        // Answers not located on tables
67
        // @todo handles case of deleted country
68
        $this->otherAnswers["__student_nationalities__"] = new Collection($countryService->getCountries());
69
    }
70
71
    /**
72
     * Detect whether all environment requirements
73
     * to create a compilation are met
74
     *
75
     * @return bool
76
     */
77
    public function isCompilationCreatable() : bool
78
    {
79
        return
80
            Location::count() > 0 &&
81
            Ward::count() > 0;
82
    }
83
84
    /**
85
     * Get section text from ID.
86
     *
87
     * @param int|string $sectionId section ID
88
     * @return string
89
     */
90
    public function getSectionText($sectionId) : string
91
    {
92
        $section = $this->sections->get($sectionId);
93
94
        if ($section) {
95
            return $section->text;
96
        }
97
98
        return (string)$sectionId;
99
    }
100
101
    /**
102
     * Get question text from ID (e.g. "23" or "q23").
103
     *
104
     * @param int|string $questionId question ID
105
     * @return string
106
     */
107
    public function getQuestionText($questionId) : string
108
    {
109
110
        $otherQuestion = $this->otherQuestions->get($questionId);
111
112
        if ($otherQuestion) {
113
            return $otherQuestion;
114
        }
115
116
        $questionId = (string)$questionId;
117
118
        $question = $this->questions->get(preg_replace("/^q/", "", $questionId));
119
120
        if ($question) {
121
            return $question->text;
122
        }
123
124
        return $questionId;
125
    }
126
    
127
    public function isFreeTextQuestion($questionId) : bool
128
    {
129
130
        $otherQuestion = $this->otherQuestions->get($questionId);
131
132
        // All other questions are not free text.
133
        if ($otherQuestion) {
134
            return false;
135
        }
136
137
        $questionId = (string)$questionId;
138
139
        $question = $this->questions->get(preg_replace("/^q/", "", $questionId));
140
141
        if ($question) {
142
            return $question->type === "text";
143
        }
144
145
        return false;
146
    }
147
148
    /**
149
     * Get answer text from ID, if any.
150
     *
151
     * @param string|int $answerId
152
     * @param string $questionId context of the answer to search, in case it belongs to a different table
153
     * @return string
154
     *
155
     * @todo refactor
156
     */
157
    public function getAnswerText($answerId, string $questionId = "") : string
158
    {
159
160
        $text = $answerId;
161
162
        switch ($questionId) {
163
164
            case "stage_location_id":
165
            case "stage_ward_id":
166
                $text = $this->otherAnswers["__" . str_replace("_id", "s", $questionId) . "__"]->get($answerId)->name;
167
                break;
168
169
            case "stage_weeks":
170
                $text = $answerId;
171
                break;
172
173
            case "student_gender":
174
                $text = __($answerId);
175
                break;
176
177
            case "student_nationality":
178
                $text = $this->otherAnswers["__student_nationalities__"]->get($answerId);
179
                break;
180
181
            default:
182
                if ($answer = $this->answers->get($answerId)) {
183
                    $text = __($answer->text);
184
                }
185
186
        }
187
188
        return (string)$text;
189
190
    }
191
192
    /**
193
     * Get question's section, if available.
194
     *
195
     * @param int|string $questionId question ID
196
     * @return null|Section
197
     */
198
    public function getQuestionSection($questionId)
199
    {
200
201
        if ($this->otherQuestions->has($questionId) === true) {
202
            return null;
203
        }
204
205
        $questionId = (string)$questionId;
206
207
        $question = $this->questions->get(preg_replace("/^q/", "", $questionId));
208
209
        return $this->sections->get($question->section_id);
210
211
    }
212
213
    /**
214
     * Apply filters to compilation search query.
215
     *
216
     * @param Builder $query
217
     * @param string $parameter
218
     * @param $value
219
     */
220
    public function applyQueryFilters(Builder $query, string $parameter, $value)
221
    {
222
223
        if (strpos($parameter, "stage_") === 0) {
224
            $this->applyQueryStageFilters($query, $parameter, $value);
225
        }
226
227
        if (strpos($parameter, "student_") === 0) {
228
            $parameter = str_replace("student_", "", $parameter);
229
            $this->applyQueryStudentFilters($query, $parameter, $value);
230
        }
231
232
        // Dynamic questions
233
        if (preg_match("/^q\d+$/", $parameter) === 1) {
234
            $parameter = str_replace("q", "", $parameter);
235
            $query->whereHas("items", function ($query) use ($parameter, $value) {
236
                $query->where("question_id", $parameter)
237
                    ->where("answer", $value);
238
            });
239
        }
240
241
    }
242
243
    /**
244
     * Apply stage-related filters to compilation search query.
245
     *
246
     * @param Builder $query
247
     * @param string $parameter
248
     * @param mixed $value
249
     */
250
    private function applyQueryStageFilters(Builder $query, string $parameter, $value)
251
    {
252
253
        switch ($parameter) {
254
            case "stage_location_id":
255
            case "stage_ward_id":
256
            case "stage_academic_year":
257
                $query->where($parameter, $value);
258
                break;
259
            case "stage_weeks":
260
                // @todo check whether this SQL string is compatible with other database engines
261
                $query->whereRaw(
262
                    "round((strftime('%J', stage_end_date) - strftime('%J', stage_start_date) + 1) / 7) = ?",
263
                    [(int)$value]
264
                );
265
                break;
266
            default:
267
268
        }
269
    }
270
271
    /**
272
     * Apply student-related filters to compilation search query.
273
     *
274
     * @param Builder $query
275
     * @param string $parameter
276
     * @param mixed $value
277
     */
278
    private function applyQueryStudentFilters(Builder $query, string $parameter, $value)
279
    {
280
281
        switch ($parameter) {
282
            case "gender":
283
            case "nationality":
284
                $query->whereHas("student", function ($query) use ($parameter, $value) {
285
                    $query->where($parameter, $value);
286
                });
287
                break;
288
            default:
289
290
        }
291
    }
292
293
}
294