Passed
Push — task/application-timeline-vali... ( 468de4...69c43c )
by Chris
03:44
created

ApplicationTimelineValidator   A

Complexity

Total Complexity 19

Size/Duplication

Total Lines 308
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 19
eloc 107
c 1
b 0
f 0
dl 0
loc 308
rs 10

14 Methods

Rating   Name   Duplication   Size   Complexity  
A experienceValidator() 0 37 2
A skillsComplete() 0 4 1
A basicInfoSimpleRules() 0 9 1
A fitComplete() 0 4 1
A basicsValidator() 0 24 1
A basicsComplete() 0 4 1
A experienceComplete() 0 4 1
A skillsValidator() 0 38 2
A fitValidator() 0 24 2
A affirmationComplete() 0 3 1
A affirmationValidator() 0 12 1
A addNestedValidatorRules() 0 12 1
A arrayMapKeys() 0 7 2
A questionAnswerRules() 0 19 2
1
<?php
2
3
namespace App\Services\Validation;
4
5
use App\Models\ExperienceSkill;
6
use App\Models\JobApplication;
7
use App\Models\Lookup\CriteriaType;
8
use App\Models\Lookup\LanguageRequirement;
9
use App\Services\Validation\JobApplicationAnswerValidator;
10
use App\Services\Validation\Rules\ContainsArrayWithAttributeRule;
11
use App\Services\Validation\Rules\ContainsObjectWithAttributeRule;
12
use Illuminate\Database\Eloquent\Builder;
13
use Illuminate\Support\Facades\Validator;
14
15
class ApplicationTimelineValidator
16
{
17
18
    /**
19
     * Validation rules for the Basic Info step.
20
     *
21
     * @return mixed[]
22
     */
23
    protected function basicInfoSimpleRules()
24
    {
25
        // Field 'language_test_confirmed' needs to be validated conditionally.
26
        return [
27
            'language_requirement_confirmed' => ['required', 'boolean', 'accepted'],
28
            'citizenship_declaration_id' => ['required', 'exists:citizenship_declarations,id'],
29
            'veteran_status_id' => ['required', 'exists:veteran_statuses,id'],
30
            'preferred_language_id' => ['required', 'exists:preferred_languages,id'],
31
            'education_requirement_confirmed' => ['required', 'boolean', 'accepted'],
32
        ];
33
    }
34
35
    /**
36
     * Validator instance for the Basic Info step.
37
     *
38
     * @param JobApplication $application Job Application object.
39
     *
40
     * @return \Illuminate\Contracts\Validation\Validator
41
     */
42
    public function basicsValidator(JobApplication $application)
43
    {
44
        $application->loadMissing('job_poster');
45
46
        // Check to see if this application is related to a Job Poster with
47
        // a bilingual requirement.
48
        $langRequirement = $application->job_poster->language_requirement_id;
49
        $langTestRequirement = LanguageRequirement::where('name', 'bilingual_intermediate')
50
            ->orWhere('name', 'bilingual_advanced')
51
            ->pluck('id');
52
53
        $validator = Validator::make($application->toArray(), $this->basicInfoSimpleRules());
54
55
        // Conditionally check for 'language_test_confirmed' if the
56
        // closure returns true.
57
        $validator->sometimes(
58
            'language_test_confirmed',
59
            'required|boolean|accepted',
60
            function ($input) use ($langRequirement, $langTestRequirement) {
61
                return in_array($langRequirement, $langTestRequirement->toArray());
62
            }
63
        );
64
65
        return $validator;
66
    }
67
68
    /**
69
     * Helper function to return completeness for the Basic Info step.
70
     *
71
     * @param JobApplication $application Job Application object.
72
     *
73
     * @return boolean
74
     */
75
    public function basicsComplete(JobApplication $application)
76
    {
77
        $validator = $this->basicsValidator($application);
78
        return $validator->passes();
79
    }
80
81
    public function fitValidator(JobApplication $application)
82
    {
83
        // Load application answers so they are included in application->toArray().
84
        $application->load('job_application_answers');
85
86
        // Start with Answer rules, that ensure each answer is complete.
87
        $answerValidator = new JobApplicationAnswerValidator($application);
88
89
        $rules = $this->addNestedValidatorRules(
90
            'job_application_answers.*',
91
            $answerValidator->rules(),
92
            []
93
        );
94
95
        // Validate that each question has been answered.
96
        $jobPosterQuestionRules = [];
97
        foreach ($application->job_poster->job_poster_questions as $question) {
98
            $jobPosterQuestionRules[] = new ContainsObjectWithAttributeRule('job_poster_question_id', $question->id);
99
        }
100
        $rules['job_application_answers'] = $jobPosterQuestionRules;
101
102
        $validator = Validator::make($application->toArray(), $rules);
103
104
        return $validator;
105
    }
106
107
    public function fitComplete(JobApplication $application)
108
    {
109
        $validator = $this->fitValidator($application);
110
        return $validator->passes();
111
    }
112
113
    /**
114
     * Validator instance for the Experience step.
115
     *
116
     * @param JobApplication $application Job Application object.
117
     *
118
     * @return \Illuminate\Contracts\Validation\Validator
119
     */
120
    public function experienceValidator(JobApplication $application)
121
    {
122
        $application->loadMissing('job_poster', 'job_poster.criteria');
123
124
        $essentialCriteriaType = CriteriaType::where('name', 'essential')->first()->id;
125
126
        $jobCriteria = $application->job_poster->criteria;
127
        $requiredCriteria = $jobCriteria->filter(function ($criterion) use ($essentialCriteriaType) {
128
            return $criterion->criteria_type_id === $essentialCriteriaType;
129
        });
130
131
        $experienceSkillRules = array();
132
133
        foreach ($requiredCriteria as $criterion) {
134
            // Validate that every essential skill has a corresponding experience.
135
            $experienceSkillRules[] = [
136
                'skill_id' => new ContainsArrayWithAttributeRule('skill_id', $criterion->skill_id)
137
            ];
138
        }
139
140
        // Get all Experiences belonging to the current application, that are assigned to required Criteria.
141
        $experiences = ExperienceSkill::whereHasMorph(
142
            'experience',
143
            '*',
144
            function (Builder $query) use ($application): void {
145
                $query->where([
146
                    ['experienceable_type', 'application'],
147
                    ['experienceable_id', $application->id]
148
                ]);
149
            }
150
        )
151
        ->whereIn('skill_id', $requiredCriteria->pluck('skill_id')->all())
152
        ->get();
153
154
        $validator = Validator::make($experiences->toArray(), $experienceSkillRules);
155
156
        return $validator;
157
    }
158
159
    /**
160
     * Helper function to return completeness for the Experience step.
161
     *
162
     * @param JobApplication $application Job Application object.
163
     *
164
     * @return boolean
165
     */
166
    public function experienceComplete(JobApplication $application)
167
    {
168
        $validator = $this->experienceValidator($application);
169
        return $validator->passes();
170
    }
171
172
    /**
173
     * Validator instance for the Skills step.
174
     *
175
     * @param JobApplication $application Job Application object.
176
     *
177
     * @return \Illuminate\Contracts\Validation\Validator
178
     */
179
    public function skillsValidator(JobApplication $application)
180
    {
181
        $application->loadMissing('job_poster', 'job_poster.criteria');
182
183
        $essentialCriteriaType = CriteriaType::where('name', 'essential')->first()->id;
184
185
        $jobCriteria = $application->job_poster->criteria;
186
        $requiredCriteria = $jobCriteria->filter(function ($criterion) use ($essentialCriteriaType) {
187
            return $criterion->criteria_type_id === $essentialCriteriaType;
188
        });
189
190
        // Validate that every experience has a justification.
191
        $experienceSkillRules = array('*.justification' => 'required|max:100');
192
193
        foreach ($requiredCriteria as $criterion) {
194
            // Validate that every essential skill has a corresponding experience.
195
            $experienceSkillRules[] = [
196
                'skill_id' => new ContainsArrayWithAttributeRule('skill_id', $criterion->skill_id),
197
            ];
198
        }
199
200
        // Get all Experiences belonging to the current application, that are assigned to required Criteria.
201
        $experiences = ExperienceSkill::whereHasMorph(
202
            'experience',
203
            '*',
204
            function (Builder $query) use ($application): void {
205
                $query->where([
206
                    ['experienceable_type', 'application'],
207
                    ['experienceable_id', $application->id]
208
                ]);
209
            }
210
        )
211
        ->whereIn('skill_id', $requiredCriteria->pluck('skill_id')->all())
212
        ->get();
213
214
        $validator = Validator::make($experiences->toArray(), $experienceSkillRules);
215
216
        return $validator;
217
    }
218
219
    /**
220
     * Helper function to return completeness for the Skills step.
221
     *
222
     * @param JobApplication $application Job Application object.
223
     *
224
     * @return boolean
225
     */
226
    public function skillsComplete(JobApplication $application)
227
    {
228
        $validator = $this->skillsValidator($application);
229
        return $validator->passes();
230
    }
231
232
    /**
233
     * Validator instance for the Final Submit step.
234
     *
235
     * @param JobApplication $application Job Application object.
236
     *
237
     * @return \Illuminate\Contracts\Validation\Validator
238
     */
239
    public function affirmationValidator(JobApplication $application)
240
    {
241
        return Validator::make($application->toArray(), [
242
            'submission_signature' => [
243
                'required',
244
                'string',
245
                'max:191',
246
            ],
247
            'submission_date' => [
248
                'required',
249
                'string',
250
                'max:191',
251
            ]
252
        ]);
253
    }
254
255
    /**
256
     * Helper function to return completeness for the Final Submit step.
257
     *
258
     * @param JobApplication $application Job Application object.
259
     *
260
     * @return boolean
261
     */
262
    public function affirmationComplete(JobApplication $application)
263
    {
264
        return $this->affirmationValidator($application)->passes();
265
    }
266
267
    /**
268
     * Return a copy of $array, with function $fn applied to each key, but values left unchanged.
269
     *
270
     * @param function $fn    Function applied to each key.
271
     * @param array    $array Array to operate on.
272
     * @return array
273
     */
274
    protected function arrayMapKeys($fn, $array): array
275
    {
276
        $newArray = [];
277
        foreach ($array as $key => $value) {
278
            $newArray[$fn($key)] = $value;
279
        }
280
        return $newArray;
281
    }
282
283
    protected function addNestedValidatorRules($nestedAttribute, $validatorRules, $rules = [])
284
    {
285
        // Prepend the attribute name of each validator rule with the nested attribute name.
286
        $newRules = $this->arrayMapKeys(
287
            function ($key) use ($nestedAttribute) {
0 ignored issues
show
Bug introduced by
function(...) { /* ... */ } of type callable is incompatible with the type App\Services\Validation\function expected by parameter $fn of App\Services\Validation\...lidator::arrayMapKeys(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

287
            /** @scrutinizer ignore-type */ function ($key) use ($nestedAttribute) {
Loading history...
288
                return implode('.', [$nestedAttribute, $key]);
289
            },
290
            $validatorRules
291
        );
292
        // Merge new rules with old rules.
293
        $rules = array_merge($rules, $newRules);
294
        return $rules;
295
    }
296
297
    /**
298
     * Helper function to return validation rules for Job Question Answers.
299
     *
300
     * @param JobApplication $application Job Application object.
301
     *
302
     * @return mixed
303
     */
304
    protected function questionAnswerRules(JobApplication $application)
305
    {
306
        // Start with Answer rules, that ensure each answer is complete.
307
        $answerValidator = new JobApplicationAnswerValidator($application);
308
309
        $rules = $this->addNestedValidatorRules(
310
            'job_application_answers.*',
311
            $answerValidator->rules(),
312
            []
313
        );
314
315
        // Validate that each question has been answered.
316
        $jobPosterQuestionRules = [];
317
        foreach ($application->job_poster->job_poster_questions as $question) {
318
            $jobPosterQuestionRules[] = new ContainsObjectWithAttributeRule('job_poster_question_id', $question->id);
319
        }
320
        $rules['job_application_answers'] = $jobPosterQuestionRules;
321
322
        return $rules;
323
    }
324
}
325