Passed
Push — master ( 8f0b22...747c28 )
by Francesco
03:57
created

CompilationService::getAnswerText()   B

Complexity

Conditions 7
Paths 7

Size

Total Lines 35
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 4
Bugs 1 Features 0
Metric Value
cc 7
eloc 21
c 4
b 1
f 0
nc 7
nop 2
dl 0
loc 35
rs 8.6506
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
    /**
128
     * Get answer text from ID, if any.
129
     *
130
     * @param string|int $answerId
131
     * @param string $questionId context of the answer to search, in case it belongs to a different table
132
     * @return string
133
     *
134
     * @todo refactor
135
     */
136
    public function getAnswerText($answerId, string $questionId = "") : string
137
    {
138
139
        $text = $answerId;
140
141
        switch ($questionId) {
142
143
            case "stage_location_id":
144
                $text = $this->otherAnswers["__stage_locations__"]->get($answerId)->name;
145
                break;
146
147
            case "stage_ward_id":
148
                $text = $this->otherAnswers["__stage_wards__"]->get($answerId)->name;
149
                break;
150
151
            case "stage_weeks":
152
                $text = $answerId;
153
                break;
154
155
            case "student_gender":
156
                $text = __($answerId);
157
                break;
158
159
            case "student_nationality":
160
                $text = $this->otherAnswers["__student_nationalities__"]->get($answerId);
161
                break;
162
163
            default:
164
                if ($answer = $this->answers->get($answerId)) {
165
                    $text = __($answer->text);
166
                }
167
168
        }
169
170
        return (string)$text;
171
172
    }
173
174
    /**
175
     * Get question's section, if available.
176
     *
177
     * @param int|string $questionId question ID
178
     * @return null|Section
179
     */
180
    public function getQuestionSection($questionId)
181
    {
182
183
        if ($this->otherQuestions->has($questionId) === true) {
184
            return null;
185
        }
186
187
        $questionId = (string)$questionId;
188
189
        $question = $this->questions->get(preg_replace("/^q/", "", $questionId));
190
191
        return $this->sections->get($question->section_id);
192
193
    }
194
195
    /**
196
     * Apply filters to compilation search query.
197
     *
198
     * @param Builder $query
199
     * @param string $parameter
200
     * @param $value
201
     */
202
    public function applyQueryFilters(Builder $query, string $parameter, $value)
203
    {
204
205
        if (strpos($parameter, "stage_") === 0) {
206
            $this->applyQueryStageFilters($query, $parameter, $value);
207
        }
208
209
        if (strpos($parameter, "student_") === 0) {
210
            $parameter = str_replace("student_", "", $parameter);
211
            $this->applyQueryStudentFilters($query, $parameter, $value);
212
        }
213
214
        // Dynamic questions
215
        if (preg_match("/^q\d+$/", $parameter) === 1) {
216
            $parameter = str_replace("q", "", $parameter);
217
            $query->whereHas("items", function ($query) use ($parameter, $value) {
218
                $query->where("question_id", $parameter)
219
                    ->where("answer", $value);
220
            });
221
        }
222
223
    }
224
225
    /**
226
     * Apply stage-related filters to compilation search query.
227
     *
228
     * @param Builder $query
229
     * @param string $parameter
230
     * @param mixed $value
231
     */
232
    private function applyQueryStageFilters(Builder $query, string $parameter, $value)
233
    {
234
235
        switch ($parameter) {
236
            case "stage_location_id":
237
            case "stage_ward_id":
238
            case "stage_academic_year":
239
                $query->where($parameter, $value);
240
                break;
241
            case "stage_weeks":
242
                // @todo check whether this SQL string is compatible with other database engines
243
                $query->whereRaw(
244
                    "round((strftime('%J', stage_end_date) - strftime('%J', stage_start_date) + 1) / 7) = ?",
245
                    [(int)$value]
246
                );
247
                break;
248
            default:
249
250
        }
251
    }
252
253
    /**
254
     * Apply student-related filters to compilation search query.
255
     *
256
     * @param Builder $query
257
     * @param string $parameter
258
     * @param mixed $value
259
     */
260
    private function applyQueryStudentFilters(Builder $query, string $parameter, $value)
261
    {
262
263
        switch ($parameter) {
264
            case "gender":
265
            case "nationality":
266
                $query->whereHas("student", function ($query) use ($parameter, $value) {
267
                    $query->where($parameter, $value);
268
                });
269
                break;
270
            default:
271
272
        }
273
    }
274
275
}
276