Passed
Push — task/refactor-criteria-type ( ab8bbb )
by Tristan
16:05 queued 06:31
created

JobController::deleteCriteria()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 6
dl 0
loc 11
ccs 7
cts 7
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
crap 1
1
<?php
2
3
namespace App\Http\Controllers;
4
5
use Illuminate\Support\Facades\Lang;
6
use Illuminate\Support\Facades\Auth;
7
use Illuminate\Support\Facades\Log;
8
use Illuminate\Support\Facades\Mail;
9
use Illuminate\Http\RedirectResponse;
10
use Illuminate\Http\Request;
11
use Illuminate\Http\Response;
12
use Illuminate\View\View;
13
use App\Http\Controllers\Controller;
14
15
use Carbon\Carbon;
16
17
use App\Mail\JobPosterReviewRequested;
18
19
use App\Models\Criteria;
20
use App\Models\JobPoster;
21
use App\Models\JobPosterKeyTask;
22
use App\Models\JobPosterQuestion;
23
use App\Models\Lookup\JobTerm;
24
use App\Models\Lookup\Province;
25
use App\Models\Lookup\SecurityClearance;
26
use App\Models\Lookup\LanguageRequirement;
27
use App\Models\Lookup\Department;
28
use App\Models\Lookup\SkillLevel;
29
use App\Models\Lookup\CriteriaType;
30
use App\Models\Skill;
31
use App\Models\Manager;
32
33
use App\Services\Validation\JobPosterValidator;
34
use Jenssegers\Date\Date;
35
use App\Models\AssessmentPlanNotification;
36
use App\Models\Assessment;
37
38
class JobController extends Controller
39
{
40
41
    /**
42
     * Get array representation of specified job poster
43
     *
44
     * @param \Illuminate\Http\Request $request   Incoming request object.
45
     * @param \App\Models\JobPoster    $jobPoster Job Poster object.
46
     *
47
     * @return mixed[]
48
     */
49
    public function get(Request $request, JobPoster $jobPoster)
0 ignored issues
show
introduced by
Method \App\Http\Controllers\JobController::get() does not have return type hint for its return value but it should be possible to add it based on @return annotation "mixed[]".
Loading history...
50
    {
51
        $jobWithTranslations = array_merge($jobPoster->toArray(), $jobPoster->getTranslationsArray());
52
        $criteria = Criteria::where('job_poster_id', $jobPoster->id)->get();
53
        $criteriaTranslated = [];
54
        foreach ($criteria as $criterion) {
55
            // TODO: getTranslationsArray probably makes DB calls every loop. Find a way to profile & optimize.
56
            $criteriaTranslated[] = array_merge($criterion->toArray(), $criterion->getTranslationsArray());
57
        }
58
        return array_merge($jobWithTranslations, ['criteria' => $criteriaTranslated]);
59
    }
60
61
    /**
62
 * Display a listing of JobPosters.
63
     *
64
     * @return \Illuminate\View\View|\Illuminate\Contracts\View\Factory
65
     */
66
    public function index()
67
    {
68
        $now = Carbon::now();
69
70
        // Find published jobs that are currently open for applications.
71
        // Eager load required relationships: Department, Province, JobTerm.
72
        // Eager load the count of submitted applications, to prevent the relationship
73
        // from being actually loaded and firing off events.
74
        $jobs = JobPoster::where('open_date_time', '<=', $now)
75
            ->where('close_date_time', '>=', $now)
76
            ->where('published', true)
77
            ->with([
78
                'department',
79
                'province',
80
                'job_term',
81
            ])
82
            ->withCount([
83
                'submitted_applications',
84
            ])
85
            ->get();
86
        return view('applicant/job_index', [
87
            'job_index' => Lang::get('applicant/job_index'),
88
            'jobs' => $jobs
89
        ]);
90
    }
91
92
    /**
93
     * Display a listing of a manager's JobPosters.
94
     *
95
     * @return \Illuminate\View\View|\Illuminate\Contracts\View\Factory
96
     */
97 1
    public function managerIndex()
98
    {
99 1
        $manager = Auth::user()->manager;
0 ignored issues
show
Bug introduced by
Accessing manager on the interface Illuminate\Contracts\Auth\Authenticatable suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
100 1
        $jobs = JobPoster::where('manager_id', $manager->id)
101 1
            ->withCount('submitted_applications')
102 1
            ->get();
103
104 1
        return view('manager/job_index', [
105
            /*Localization Strings*/
106 1
            'jobs_l10n' => Lang::get('manager/job_index'),
107
108
            /* Data */
109 1
            'jobs' => $jobs,
110
        ]);
111
    }
112
113
    /**
114
     * Submit the Job Poster for review.
115
     *
116
     * @param \Illuminate\Http\Request $request   Incoming request object.
117
     * @param \App\Models\JobPoster    $jobPoster Job Poster object.
118
     *
119
     * @return \Illuminate\View\View|\Illuminate\Contracts\View\Factory
120
     */
121 1
    public function submitForReview(Request $request, JobPoster $jobPoster)
122
    {
123
        // Update review request timestamp
124 1
        $jobPoster->review_requested_at = new Date();
125 1
        $jobPoster->save();
126
127
        // Refresh model instance with updated DB values.
128 1
        $jobPoster = JobPoster::withCount('submitted_applications')->where('id', $jobPoster->id)->first();
129
130
        // Send email
131 1
        $reviewer_email = config('mail.reviewer_email');
132 1
        if (isset($reviewer_email)) {
133 1
            Mail::to($reviewer_email)->send(new JobPosterReviewRequested($jobPoster, Auth::user()));
0 ignored issues
show
Bug introduced by
It seems like Illuminate\Support\Facades\Auth::user() can also be of type null; however, parameter $manager of App\Mail\JobPosterReviewRequested::__construct() does only seem to accept App\Models\User, maybe add an additional type check? ( Ignorable by Annotation )

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

133
            Mail::to($reviewer_email)->send(new JobPosterReviewRequested($jobPoster, /** @scrutinizer ignore-type */ Auth::user()));
Loading history...
134
        } else {
135
            Log::error('The reviewer email environment variable is not set.');
136
        }
137
138 1
        return view('manager/job_index/job', [
139
            /*Localization Strings*/
140 1
            'jobs_l10n' => Lang::get('manager/job_index'),
141 1
            'job' => $jobPoster
142
        ]);
143
    }
144
145
    /**
146
     * Delete a draft Job Poster.
147
     *
148
     * @param \Illuminate\Http\Request $request   Incoming request object.
149
     * @param \App\Models\JobPoster    $jobPoster Job Poster object.
150
     *
151
     * @return void
152
     */
153
    public function destroy(Request $request, JobPoster $jobPoster) : void
154
    {
155
        $jobPoster->delete();
156
    }
157
158
    /**
159
     * Display the specified job poster.
160
     *
161
     * @param \Illuminate\Http\Request $request   Incoming request object.
162
     * @param \App\Models\JobPoster    $jobPoster Job Poster object.
163
     *
164
     * @return \Illuminate\View\View|\Illuminate\Contracts\View\Factory
165
     */
166 6
    public function show(Request $request, JobPoster $jobPoster)
167
    {
168 6
        $jobPoster->load([
169 6
            'department',
170
            'criteria.skill.skill_type',
171
            'manager.team_culture',
172
            'manager.work_environment'
173
        ]);
174
175 6
        $user = Auth::user();
0 ignored issues
show
Unused Code introduced by
The assignment to $user is dead and can be removed.
Loading history...
176
177
        //TODO: Improve workplace photos, and reference them in template direction from WorkEnvironment model
178 6
        $workplacePhotos = [];
179 6
        foreach ($jobPoster->manager->work_environment->workplace_photo_captions as $photoCaption) {
180
            $workplacePhotos[] = [
181
                'description' => $photoCaption->description,
182
                'url' => '/images/user.png'
183
            ];
184
        }
185
186
        //TODO: replace route('manager.show',manager.id) in templates with link using slug
187
188
        $criteria = [
189 6
            'essential' => $jobPoster->criteria->filter(
190
                function ($value, $key) {
191 2
                    return $value->criteria_type->name == 'essential';
192 6
                }
193
            ),
194 6
            'asset' => $jobPoster->criteria->filter(
195
                function ($value, $key) {
196 2
                    return $value->criteria_type->name == 'asset';
197 6
                }
198
            ),
199
        ];
200
201 6
        $jobLang = Lang::get('applicant/job_post');
202
203 6
        $applyButton = [];
204 6
        if (!$jobPoster->published && $this->authorize('update', $jobPoster)) {
205
            $applyButton = [
206 4
                'href' => route('manager.jobs.edit', $jobPoster->id),
207 4
                'title' => $jobLang['apply']['edit_link_title'],
208 4
                'text' => $jobLang['apply']['edit_link_label'],
209
            ];
210 2
        } elseif (Auth::check() && $jobPoster->isOpen()) {
211
            $applyButton = [
212 1
                'href' => route('job.application.edit.1', $jobPoster->id),
213 1
                'title' => $jobLang['apply']['apply_link_title'],
214 1
                'text' => $jobLang['apply']['apply_link_label'],
215
            ];
216 1
        } elseif (Auth::guest() && $jobPoster->isOpen()) {
217
            $applyButton = [
218 1
                'href' => route('job.application.edit.1', $jobPoster->id),
219 1
                'title' => $jobLang['apply']['login_link_title'],
220 1
                'text' => $jobLang['apply']['login_link_label'],
221
            ];
222
        } else {
223
            $applyButton = [
224
                'href' => null,
225
                'title' => null,
226
                'text' => $jobLang['apply']['job_closed_label'],
227
            ];
228
        }
229
230 6
        return view(
231 6
            'applicant/job_post',
232
            [
233 6
                'job_post' => $jobLang,
234 6
                'manager' => $jobPoster->manager,
235 6
                'manager_profile_photo_url' => '/images/user.png', //TODO get real photo
236 6
                'team_culture' => $jobPoster->manager->team_culture,
237 6
                'work_environment' => $jobPoster->manager->work_environment,
238 6
                'workplace_photos' => $workplacePhotos,
239 6
                'job' => $jobPoster,
240 6
                'criteria' => $criteria,
241 6
                'apply_button' => $applyButton,
242 6
                'skill_template' => Lang::get('common/skills'),
243
            ]
244
        );
245
    }
246
247
    /**
248
     * Create a blank job poster for the specified manager
249
     *
250
     * @param Manager $manager
1 ignored issue
show
Coding Style Documentation introduced by
Missing parameter comment
Loading history...
251
     * @return \Illuminate\View\View|\Illuminate\Contracts\View\Factory Job Create view
252
     */
253 2
    public function createAsManager(Manager $manager)
254
    {
255 2
        $jobPoster = new JobPoster();
256 2
        $jobPoster->manager_id = $manager->id;
257 2
        $managerEn = $manager->translate('en');
258 2
        $managerFr = $manager->translate('fr');
259
260 2
        $jobPoster->fill([
261 2
            'department_id' => $manager->department_id,
262
            'en' => [
263 2
                'branch' => $managerEn->branch,
264 2
                'division' => $managerEn->division,
265
            ],
266
            'fr' => [
267 2
                'branch' => $managerFr->branch,
268 2
                'division' => $managerFr->division,
269
            ]
270
        ]);
271 2
        $jobPoster->save();
272
273 2
        $defaultQuestions = $this->populateDefaultQuestions();
274 2
        if (!empty($defaultQuestions)) {
275 2
            $jobPoster->job_poster_questions()->saveMany($defaultQuestions);
276
        }
277
278 2
        return redirect()->route('manager.jobs.edit', $jobPoster->id);
1 ignored issue
show
Bug Best Practice introduced by
The expression return redirect()->route....edit', $jobPoster->id) returns the type Illuminate\Http\RedirectResponse which is incompatible with the documented return type Illuminate\Contracts\Vie...ry|Illuminate\View\View.
Loading history...
279
    }
280
281
    /**
282
     * Display the form for creating a new Job Poster
283
     *
284
     * @param \Illuminate\Http\Request $request Incoming request object.
285
     *
286
     * @return \Illuminate\View\View|\Illuminate\Contracts\View\Factory Job Create view
287
     */
288
    public function create(Request $request)
289
    {
290
        return $this->populateCreateView($request);
291
    }
292
293
    /**
294
     * Display the form for editing an existing Job Poster
295
     *
296
     * @param \Illuminate\Http\Request $request   Incoming request object.
297
     * @param \App\Models\JobPoster    $jobPoster Job Poster object.
298
     *
299
     * @return \Illuminate\View\View|\Illuminate\Contracts\View\Factory Job Create view
300
     */
301 1
    public function edit(Request $request, JobPoster $jobPoster)
302
    {
303 1
        return $this->populateCreateView($request, $jobPoster);
304
    }
305
306
    /**
307
     * Get the manager from the request object and check if creating or editing
308
     *
309
     * @param \Illuminate\Http\Request $request   Incoming request object.
310
     * @param \App\Models\JobPoster    $jobPoster Optional Job Poster object.
311
     *
312
     * @return \Illuminate\View\View|\Illuminate\Contracts\View\Factory Job Create view
313
     */
314 1
    public function populateCreateView(Request $request, JobPoster $jobPoster = null)
315
    {
316 1
        if ($jobPoster == null || $jobPoster->manager == null) {
317
            $manager = $request->user() ? $request->user()->manager : null;
318
        } else {
319 1
            $manager = $jobPoster->manager;
320
        }
321
322 1
        if (isset($jobPoster)) {
323 1
            $job = $jobPoster;
324 1
            $route = ['manager.jobs.update', $jobPoster];
325 1
            $jobHeading = 'manager/job_edit';
326
        } else {
327
            $job = [];
328
            $defaultQuestions = $this->populateDefaultQuestions();
329
            if (!empty($defaultQuestions)) {
330
                $job['job_poster_questions'] = $defaultQuestions;
331
            }
332
            $route = ['manager.jobs.store'];
333
            $jobHeading = 'manager/job_create';
334
        }
335
336 1
        $skillLangs = Lang::get('common/skills');
337
338 1
        $softSkills = Skill::whereHas(
339 1
            'skill_type',
340
            function ($query) : void {
341 1
                $query->where('name', '=', 'soft');
342 1
            }
343 1
        )->get()
344 1
            ->mapWithKeys(
345
                function ($skill) {
346
                    return [
347 1
                        $skill->id => $skill->name
348
                    ];
349 1
                }
350
            )
351 1
            ->all();
352
353 1
        $hardSkills = Skill::whereHas(
354 1
            'skill_type',
355
            function ($query) : void {
356 1
                $query->where('name', '=', 'hard');
357 1
            }
358 1
        )->get()
359 1
            ->mapWithKeys(
360
                function ($skill) {
361
                    return [
362 1
                        $skill->id => $skill->name
363
                    ];
364 1
                }
365
            )
366 1
            ->all();
367
368 1
        asort($softSkills, SORT_LOCALE_STRING);
369 1
        asort($hardSkills, SORT_LOCALE_STRING);
370
371
        $skills = [
372
            'essential' => [
373 1
                'hard' => $hardSkills,
374 1
                'soft' => $softSkills
375
            ],
376
            'asset' => [
377 1
                'hard' => $hardSkills,
378 1
                'soft' => $softSkills
379
            ]
380
        ];
381
382 1
        $skillLevelCollection = SkillLevel::all();
383
384 1
        $skillLevels = array();
385
386 1
        $skillLevels['hard'] = $skillLevelCollection->mapWithKeys(
387
            function ($skillLevel) use ($skillLangs) {
388 1
                return [$skillLevel->id => $skillLangs['skill_levels']['hard'][$skillLevel->name]];
389 1
            }
390 1
        )->all();
391
392 1
        $skillLevels['soft'] = $skillLevelCollection->mapWithKeys(
393
            function ($skillLevel) use ($skillLangs) {
394 1
                return [$skillLevel->id => $skillLangs['skill_levels']['soft'][$skillLevel->name]];
395 1
            }
396 1
        )->all();
397
398 1
        return view(
399 1
            'manager/job_create',
400
            [
401
                /*Localization Strings*/
402 1
                'job_l10n' => Lang::get('manager/job_create'),
403
404
                /* Data */
405 1
                'job' => Lang::get($jobHeading),
406 1
                'manager' => $manager,
407 1
                'provinces' => Province::all(),
408 1
                'departments' => Department::all(),
409 1
                'language_requirments' => LanguageRequirement::all(),
410 1
                'security_clearances' => SecurityClearance::all(),
411 1
                'job' => $job,
412 1
                'form_action_url' => route(/** @scrutinizer ignore-type */ ...$route), // phpcs:ignore
0 ignored issues
show
Bug introduced by
$route is expanded, but the parameter $name of route() does not expect variable arguments. ( Ignorable by Annotation )

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

412
                'form_action_url' => route(/** @scrutinizer ignore-type */ /** @scrutinizer ignore-type */ ...$route), // phpcs:ignore
Loading history...
413 1
                'skills' => $skills,
414 1
                'skill_levels' => $skillLevels,
415 1
                'skill_template' => $skillLangs,
416
            ]
417
        );
418
    }
419
420
    /**
421
     * Create a new resource in storage
422
     *
423
     * @param \Illuminate\Http\Request $request   Incoming request object.
424
     * @param \App\Models\JobPoster    $jobPoster Optional Job Poster object.
425
     *
426
     * @return \Illuminate\Routing\Redirector|\Illuminate\Http\RedirectResponse A redirect to the Job Index
427
     */
428 6
    public function store(Request $request, JobPoster $jobPoster = null)
429
    {
430
        // Don't allow edits for published Job Posters
431
        // Also check auth while we're at it
432 6
        if (isset($jobPoster)) {
433 6
            $this->authorize('update', $jobPoster);
434 6
            JobPosterValidator::validateUnpublished($jobPoster);
435
        } else {
436
            $this->authorize('create', JobPoster::class);
437
        }
438
439 6
        $input = $request->input();
440
441 6
        $job = (isset($jobPoster) ? $jobPoster : new JobPoster());
442
443 6
        if ($job->manager_id == null) {
444
            $job->manager_id = $request->user()->manager->id;
445
            $job->save();
446
        }
447
448 6
        $this->fillAndSaveJobPoster($input, $job);
449
450 6
        $this->fillAndSaveJobPosterTasks($input, $job, isset($jobPoster));
451
452 6
        $this->fillAndSaveJobPosterQuestions($input, $job, isset($jobPoster));
453
454 6
        $this->fillAndSaveJobPosterCriteria($input, $job, isset($jobPoster));
0 ignored issues
show
Unused Code introduced by
The call to App\Http\Controllers\Job...SaveJobPosterCriteria() has too many arguments starting with IssetNode. ( Ignorable by Annotation )

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

454
        $this->/** @scrutinizer ignore-call */ 
455
               fillAndSaveJobPosterCriteria($input, $job, isset($jobPoster));

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
455
456 6
        return redirect(route('manager.jobs.show', $job->id));
457
    }
458
459
    /**
460
     * Fill Job Poster model's properties and save
461
     *
462
     * @param mixed[]               $input     Field values.
463
     * @param \App\Models\JobPoster $jobPoster Job Poster object.
464
     *
465
     * @return void
466
     */
467 6
    protected function fillAndSaveJobPoster(array $input, JobPoster $jobPoster) : void
468
    {
469 6
        $jobPoster->fill(
470
            [
471 6
                'job_term_id' => JobTerm::where('name', 'month')->firstOrFail()->id,
472 6
                'term_qty' => $input['term_qty'],
473 6
                'open_date_time' => ptDayStartToUtcTime($input['open_date']),
474 6
                'close_date_time' => ptDayEndToUtcTime($input['close_date']),
475 6
                'start_date_time' => ptDayStartToUtcTime($input['start_date']),
476 6
                'department_id' => $input['department'],
477 6
                'province_id' => $input['province'],
478 6
                'salary_min' => $input['salary_min'],
479 6
                'salary_max' => $input['salary_max'],
480 6
                'noc' => $input['noc'],
481 6
                'classification' => $input['classification'],
482 6
                'security_clearance_id' => $input['security_clearance'],
483 6
                'language_requirement_id' => $input['language_requirement'],
484 6
                'remote_work_allowed' => (isset($input['remote_work_allowed']) ? $input['remote_work_allowed'] : false),
485
                'en' => [
486 6
                    'city' => $input['city'],
487 6
                    'title' => $input['title']['en'],
488 6
                    'impact' => $input['impact']['en'],
489 6
                    'branch' => $input['branch']['en'],
490 6
                    'division' => $input['division']['en'],
491 6
                    'education' => $input['education']['en'],
492
                ],
493
                'fr' => [
494 6
                    'city' => $input['city'],
495 6
                    'title' => $input['title']['fr'],
496 6
                    'impact' => $input['impact']['fr'],
497 6
                    'branch' => $input['branch']['fr'],
498 6
                    'division' => $input['division']['fr'],
499 6
                    'education' => $input['education']['fr'],
500
                ],
501
            ]
502
        );
503 6
        $jobPoster->save();
504 6
    }
505
506
    /**
507
     * Fill Job Poster's tasks and save
508
     *
509
     * @param mixed[]               $input     Field values.
510
     * @param \App\Models\JobPoster $jobPoster Job Poster object.
511
     * @param boolean               $replace   Remove existing relationships.
512
     *
513
     * @return void
514
     */
515 6
    protected function fillAndSaveJobPosterTasks(array $input, JobPoster $jobPoster, bool $replace) : void
516
    {
517 6
        if ($replace) {
518 6
            $jobPoster->job_poster_key_tasks()->delete();
519
        }
520
521 6
        if (!array_key_exists('task', $input) || !is_array($input['task'])) {
522 6
            return;
523
        }
524
525
        foreach ($input['task'] as $task) {
526
            $jobPosterTask = new JobPosterKeyTask();
527
            $jobPosterTask->job_poster_id = $jobPoster->id;
528
            $jobPosterTask->fill(
529
                [
530
                    'en' => [
531
                        'description' => $task['en']
532
                    ],
533
                    'fr' => [
534
                        'description' => $task['fr']
535
                    ]
536
                ]
537
            );
538
            $jobPosterTask->save();
539
        }
540
    }
541
542
    /**
543
     * Fill Job Poster's questions and save
544
     *
545
     * @param mixed[]               $input     Field values.
546
     * @param \App\Models\JobPoster $jobPoster Job Poster object.
547
     * @param boolean               $replace   Remove existing relationships.
548
     *
549
     * @return void
550
     */
551 6
    protected function fillAndSaveJobPosterQuestions(array $input, JobPoster $jobPoster, bool $replace) : void
552
    {
553 6
        if ($replace) {
554 6
            $jobPoster->job_poster_questions()->delete();
555
        }
556
557 6
        if (!array_key_exists('question', $input) || !is_array($input['question'])) {
558 6
            return;
559
        }
560
561
        foreach ($input['question'] as $question) {
562
            $jobQuestion = new JobPosterQuestion();
563
            $jobQuestion->job_poster_id = $jobPoster->id;
564
            $jobQuestion->fill(
565
                [
566
                    'en' => [
567
                        'question' => $question['question']['en'],
568
                        'description' => $question['description']['en']
569
                    ],
570
                    'fr' => [
571
                        'question' => $question['question']['fr'],
572
                        'description' => $question['description']['fr']
573
                    ]
574
                ]
575
            );
576
            $jobQuestion->save();
577
        }
578
    }
579
580
    /**
581
     * Fill Job Poster's criteria and save
582
     *
583
     * @param mixed[]               $input     Field values.
584
     * @param \App\Models\JobPoster $jobPoster Job Poster object.
585
     *
586
     * @return void
587
     */
588 6
    protected function fillAndSaveJobPosterCriteria(array $input, JobPoster $jobPoster) : void
589
    {
590 6
        $affectedCriteriaIds = [];
591
592 6
        if (array_key_exists('criteria', $input) && !is_array($input['criteria'])) {
593
            $criteria = $input['criteria'];
594
595
            // Old criteria must be updated, using the criteriaId that comes from the form element names.
596
            if (!empty($criteria['old'])) {
597
                foreach ($criteria['old'] as $criteriaType => $criteriaTypeInput) {
598
                    foreach ($criteriaTypeInput as $skillTypeInput) {
599
                        foreach ($skillTypeInput as $criteriaId => $criteriaInput) {
600
                            $updatedCriteria = $this->processCriteriaForm($jobPoster, $criteriaType, $criteriaInput, $criteriaId);
601
                            $affectedCriteriaIds[] = $updatedCriteria->id;
602
                        }
603
                    }
604
                }
605
            }
606
            // New criteria must be created from scratch, and the id in the form element name can be disregarded.
607
            if (!empty($criteria['new'])) {
608
                foreach ($criteria['new'] as $criteriaType => $criteriaTypeInput) {
609
                    foreach ($criteriaTypeInput as $skillTypeInput) {
610
                        foreach ($skillTypeInput as $criteriaInput) {
611
                            $newCriteria = $this->processCriteriaForm($jobPoster, $criteriaType, $criteriaInput, null);
612
                            $affectedCriteriaIds[] = $newCriteria->id;
613
                        }
614
                    }
615
                }
616
            }
617
        }
618
619
        // Existing criteria which were not resubmitted must be deleted.
620 6
        $deleteCriteria = $jobPoster->criteria()->whereNotIn('id', $affectedCriteriaIds)->get();
621 6
        foreach ($deleteCriteria as $criteria) {
622 6
            $this->deleteCriteria($criteria);
623
        }
624 6
    }
625
626
    /**
627
     * Process intput representing a single criteria from Job Poster form.
628
     *
629
     * @param JobPoster    $jobPoster
1 ignored issue
show
Coding Style Documentation introduced by
Missing parameter comment
Loading history...
630
     * @param string       $criteriaType
1 ignored issue
show
Coding Style Documentation introduced by
Missing parameter comment
Loading history...
631
     * @param array        $criteriaInput
1 ignored issue
show
introduced by
@param annotation of method \App\Http\Controllers\JobController::processCriteriaForm() does not specify type hint for items of its traversable parameter $criteriaInput.
Loading history...
Coding Style Documentation introduced by
Missing parameter comment
Loading history...
632
     * @param integer|null $criteriaId
1 ignored issue
show
Coding Style Documentation introduced by
Missing parameter comment
Loading history...
633
     * @return Criteria
634
     */
635
    protected function processCriteriaForm(JobPoster $jobPoster, string $criteriaType, array $criteriaInput, ?int $criteriaId): Criteria
636
    {
637
        $skillId = $criteriaInput['skill_id'];
638
639
        //If no description was provided, use the default skill description
640
        $descriptionEn = $criteriaInput['description']['en'] ?
641
            $criteriaInput['description']['en'] : Skill::find($skillId)->getTranslation('description', 'en');
642
        $descriptionFr = $criteriaInput['description']['fr'] ?
643
            $criteriaInput['description']['fr'] : Skill::find($criteriaInput['skill_id'])->getTranslation('description', 'fr');
644
        $data = [
645
            'skill_id' => $criteriaInput['skill_id'],
646
            'criteria_type_id' => CriteriaType::where('name', $criteriaType)->firstOrFail()->id,
647
            'skill_level_id' => $criteriaInput['skill_level_id'],
648
            'en' => [
649
                'description' => $descriptionEn,
650
            ],
651
            'fr' => [
652
                'description' => $descriptionFr,
653
            ],
654
        ];
655
656
        if ($criteriaId) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $criteriaId of type integer|null is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
657
            $existingCriteria = Criteria::find($criteriaId);
658
            $this->updateCriteria($existingCriteria, $data);
659
            return $existingCriteria;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $existingCriteria could return the type null which is incompatible with the type-hinted return App\Models\Criteria. Consider adding an additional type-check to rule them out.
Loading history...
660
        } else {
661
            $newCriteria = $this->createCriteria($jobPoster, $skillId, $data);
662
            return $newCriteria;
663
        }
664
    }
665
666
    /**
667
     * Create a Job Criteria
668
     *
669
     * @param JobPoster $jobPoster
1 ignored issue
show
Coding Style Documentation introduced by
Missing parameter comment
Loading history...
670
     * @param integer   $skillId
1 ignored issue
show
Coding Style Documentation introduced by
Missing parameter comment
Loading history...
671
     * @param array     $data
1 ignored issue
show
introduced by
@param annotation of method \App\Http\Controllers\JobController::createCriteria() does not specify type hint for items of its traversable parameter $data.
Loading history...
Coding Style Documentation introduced by
Missing parameter comment
Loading history...
672
     * @return Criteria
673
     */
674
    protected function createCriteria(JobPoster $jobPoster, int $skillId, array $data): Criteria
675
    {
676
        $criteria = new Criteria();
677
        $criteria->job_poster_id = $jobPoster->id;
678
        $criteria->skill_id = $skillId;
679
        $criteria->fill($data);
680
        $criteria->save();
681
682
        $notification = $this->makeAssessmentPlanNotification(
683
            'CREATE',
684
            $criteria
685
        );
686
        $notification->save();
687
688
        return $criteria;
689
    }
690
691
    /**
692
     * Update an existing Job Criteria
693
     *
694
     * @param Criteria $criteria
1 ignored issue
show
Coding Style Documentation introduced by
Missing parameter comment
Loading history...
695
     * @param array    $data
1 ignored issue
show
introduced by
@param annotation of method \App\Http\Controllers\JobController::updateCriteria() does not specify type hint for items of its traversable parameter $data.
Loading history...
Coding Style Documentation introduced by
Missing parameter comment
Loading history...
696
     * @return void
697
     */
698
    protected function updateCriteria(Criteria $criteria, array $data): void
699
    {
700
        if ($criteria->skill_level_id != $data['skill_level_id'] ||
701
        $criteria->skill_id != $data['skill_id']) {
702
            $notification = $this->makeAssessmentPlanNotification(
703
                'UPDATE',
704
                $criteria,
705
                $data['skill_id'],
706
                $data['skill_level_id']
707
            );
708
            $notification->save();
709
        }
710
        $criteria->fill($data);
711
        $criteria->save();
712
    }
713
714
    /**
715
     * Delete existing Job Criteria
716
     *
717
     * @param Criteria $criteria
1 ignored issue
show
Coding Style Documentation introduced by
Missing parameter comment
Loading history...
718
     * @return void
719
     */
720 6
    protected function deleteCriteria(Criteria $criteria): void
721
    {
722 6
        $notification = $notification = $this->makeAssessmentPlanNotification(
0 ignored issues
show
Unused Code introduced by
The assignment to $notification is dead and can be removed.
Loading history...
723 6
            'DELETE',
724 6
            $criteria
725
        );
726 6
        $notification->save();
727
728
        // Delete assessments related to this criteria
729 6
        Assessment::where("criterion_id", $criteria->id)->delete();
730 6
        $criteria->delete();
731 6
    }
732
733
    /**
734
     * Create a new AssessmentPlanNotification for a modification to a Criteria
735
     *
736
     * @param string       $type            Can be CREATE, UPDATE or DELETE.
737
     * @param Criteria     $criteria
1 ignored issue
show
Coding Style Documentation introduced by
Missing parameter comment
Loading history...
738
     * @param integer|null $newSkillId      Only used for UPDATE type notifications.
739
     * @param integer|null $newSkillLevelId Only used for UPDATE type notifications.
740
     * @return AssessmentPlanNotification
741
     */
742 6
    protected function makeAssessmentPlanNotification(string $type, Criteria $criteria, $newSkillId = null, $newSkillLevelId = null): AssessmentPlanNotification
0 ignored issues
show
introduced by
Method \App\Http\Controllers\JobController::makeAssessmentPlanNotification() does not have parameter type hint for its parameter $newSkillId but it should be possible to add it based on @param annotation "integer|null".
Loading history...
introduced by
Method \App\Http\Controllers\JobController::makeAssessmentPlanNotification() does not have parameter type hint for its parameter $newSkillLevelId but it should be possible to add it based on @param annotation "integer|null".
Loading history...
743
    {
744 6
        $notification = new AssessmentPlanNotification();
745 6
        $notification->job_poster_id = $criteria->job_poster_id;
746 6
        $notification->type = $type;
747 6
        $notification->criteria_id = $criteria->id;
748 6
        $notification->skill_id = $criteria->skill_id;
749 6
        $notification->criteria_type_id = $criteria->criteria_type_id;
750 6
        $notification->skill_level_id = $criteria->skill_level_id;
751 6
        $notification->skill_id_new = $newSkillId;
752 6
        $notification->skill_level_id_new = $newSkillLevelId;
753 6
        $notification->acknowledged = false;
754 6
        return $notification;
755
    }
756
757
    /**
758
     * Get the localized default questions and add them to an array.
759
     *
760
     * @return mixed[]|void
761
     */
762 2
    protected function populateDefaultQuestions()
763
    {
764
        $defaultQuestions = [
765 2
            'en' => array_values(Lang::get('manager/job_create', [], 'en')['questions']),
766 2
            'fr' => array_values(Lang::get('manager/job_create', [], 'fr')['questions']),
767
        ];
768
769 2
        if (count($defaultQuestions['en']) !== count($defaultQuestions['fr'])) {
770
            Log::warning('There must be the same number of French and English default questions for a Job Poster.');
771
            return;
772
        }
773
774 2
        $jobQuestions = [];
775
776 2
        for ($i = 0; $i < count($defaultQuestions['en']); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
777 2
            $jobQuestion = new JobPosterQuestion();
778 2
            $jobQuestion->fill(
779
                [
780
                    'en' => [
781 2
                        'question' => $defaultQuestions['en'][$i],
782
                    ],
783
                    'fr' => [
784 2
                        'question' => $defaultQuestions['fr'][$i],
785
                    ]
786
                ]
787
            );
788 2
            $jobQuestions[] = $jobQuestion;
789
        }
790
791 2
        return $jobQuestions;
792
    }
793
}
794