Passed
Push — master ( 1a29ab...0cfdc0 )
by Chris
08:19 queued 01:55
created

JobController::downloadApplicants()   A

Complexity

Conditions 5
Paths 8

Size

Total Lines 51
Code Lines 32

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 20
CRAP Score 5.3073

Importance

Changes 3
Bugs 0 Features 0
Metric Value
eloc 32
c 3
b 0
f 0
dl 0
loc 51
ccs 20
cts 26
cp 0.7692
rs 9.0968
cc 5
nc 8
nop 1
crap 5.3073

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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\Http\Request;
9
use App\Http\Controllers\Controller;
10
use App\Models\JobApplication;
11
use Carbon\Carbon;
12
use App\Models\JobPoster;
13
use App\Models\JobPosterQuestion;
14
use App\Models\Lookup\ApplicationStatus;
15
use App\Models\Lookup\CitizenshipDeclaration;
16
use App\Models\Lookup\VeteranStatus;
17
use App\Models\Manager;
18
use Mcamara\LaravelLocalization\Facades\LaravelLocalization;
19
use App\Services\Validation\JobPosterValidator;
20
use Facades\App\Services\WhichPortal;
21
use Illuminate\Support\Facades\Response;
22
use Illuminate\Support\Facades\Validator;
23
24
class JobController extends Controller
25
{
26
    /**
27
     * Display a listing of JobPosters.
28
     *
29
     * @return \Illuminate\Http\Response
30
     */
31
    public function index()
32
    {
33
        $now = Carbon::now();
34
35
        // Find published jobs that are currently open for applications.
36
        // Eager load required relationships: Department, Province, JobTerm.
37
        // Eager load the count of submitted applications, to prevent the relationship
38
        // from being actually loaded and firing off events.
39
        $jobs = JobPoster::where('open_date_time', '<=', $now)
40
            ->where('close_date_time', '>=', $now)
41
            ->where('internal_only', false)
42
            ->where('published', true)
43
            ->with([
44
                'department',
45
                'province',
46
                'job_term',
47
            ])
48
            ->withCount([
49
                'submitted_applications',
50
            ])
51
            ->get();
52
        return view('applicant/job_index', [
0 ignored issues
show
Bug introduced by
The function view was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

52
        return /** @scrutinizer ignore-call */ view('applicant/job_index', [
Loading history...
53
            'job_index' => Lang::get('applicant/job_index'),
54
            'jobs' => $jobs
55
        ]);
56
    }
57
58
    /**
59
     * Display a listing of a manager's JobPosters.
60
     *
61
     * @return \Illuminate\Http\Response
62
     */
63
    public function managerIndex()
64
    {
65
        $manager = Auth::user()->manager;
66
67
        $jobs = JobPoster::where('manager_id', $manager->id)
68
            ->with('classification')
69
            ->withCount('submitted_applications')
70
            ->get();
71
72
        foreach ($jobs as &$job) {
73 1
            $chosen_lang = $job->chosen_lang;
74
75 1
            // Show chosen lang title if current title is empty.
76 1
            if (empty($job->title)) {
77 1
                $job->title = $job->getTranslation('title', $chosen_lang);
78 1
                $job->trans_required = true;
79
            }
80 1
81
            // Always preview and edit in the chosen language.
82 1
            $job->preview_link = LaravelLocalization::getLocalizedURL($chosen_lang, route('manager.jobs.show', $job));
0 ignored issues
show
Bug introduced by
The function route was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

82
            $job->preview_link = LaravelLocalization::getLocalizedURL($chosen_lang, /** @scrutinizer ignore-call */ route('manager.jobs.show', $job));
Loading history...
83
            $job->edit_link = LaravelLocalization::getLocalizedURL($chosen_lang, route('manager.jobs.edit', $job));
84 1
        }
85
86
87
        return view('manager/job_index', [
0 ignored issues
show
Bug introduced by
The function view was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

87
        return /** @scrutinizer ignore-call */ view('manager/job_index', [
Loading history...
88
            // Localization Strings.
89
            'jobs_l10n' => Lang::get('manager/job_index'),
90
            // Data.
91
            'jobs' => $jobs,
92
        ]);
93
    }
94
95 1
    /**
96
     * Display a listing of a hr advisor's JobPosters.
97
     *
98 1
     * @return \Illuminate\Http\Response
99 1
     */
100
    public function hrIndex(Request $request)
101
    {
102 1
        $hrAdvisor = $request->user()->hr_advisor;
103
        return view('hr_advisor/job_index', [
0 ignored issues
show
Bug introduced by
The function view was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

103
        return /** @scrutinizer ignore-call */ view('hr_advisor/job_index', [
Loading history...
104
            'title' => Lang::get('hr_advisor/job_index.title'),
105 1
            'hr_advisor_id' => $hrAdvisor->id
106 1
        ]);
107 1
    }
108
109
110
111
112 1
    /**
113
     * Delete a draft Job Poster.
114 1
     *
115 1
     * @param  \Illuminate\Http\Request $request   Incoming request object.
116
     * @param  \App\Models\JobPoster    $jobPoster Job Poster object.
117
     * @return \Illuminate\Http\Response
118
     */
119
    public function destroy(Request $request, JobPoster $jobPoster)
120
    {
121
        $jobPoster->delete();
122
    }
123
124
    /**
125
     * Display the specified job poster.
126
     *
127
     * @param  \Illuminate\Http\Request $request   Incoming request object.
128
     * @param  \App\Models\JobPoster    $jobPoster Job Poster object.
129
     * @return \Illuminate\Http\Response
130
     */
131
    public function show(Request $request, JobPoster $jobPoster)
132
    {
133
        $jobPoster->load([
134
            'department',
135
            'criteria.skill.skill_type',
136
            'manager.team_culture',
137
            'manager.work_environment'
138 6
        ]);
139
140 6
        $user = Auth::user();
141 6
142
        // TODO: Improve workplace photos, and reference them in template direction from WorkEnvironment model.
143
        $workplacePhotos = [];
144
        foreach ($jobPoster->manager->work_environment->workplace_photo_captions as $photoCaption) {
145
            $workplacePhotos[] = [
146
                'description' => $photoCaption->description,
147 6
                'url' => '/images/user.png'
148
            ];
149
        }
150 6
151 6
        // TODO: replace route('manager.show',manager.id) in templates with link using slug.
152
        $criteria = [
153
            'essential' => $jobPoster->criteria->filter(
154
                function ($value, $key) {
155
                    return $value->criteria_type->name == 'essential';
156
                }
157
            ),
158
            'asset' => $jobPoster->criteria->filter(
159
                function ($value, $key) {
160 6
                    return $value->criteria_type->name == 'asset';
161
                }
162 4
            ),
163 6
        ];
164
165 6
        $jobLang = Lang::get('applicant/job_post');
166
167 4
        $applyButton = [];
168 6
        if (WhichPortal::isManagerPortal()) {
169
            $applyButton = [
170
                'href' => route('manager.jobs.edit', $jobPoster->id),
0 ignored issues
show
Bug introduced by
The function route was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

170
                'href' => /** @scrutinizer ignore-call */ route('manager.jobs.edit', $jobPoster->id),
Loading history...
171
                'title' => $jobLang['apply']['edit_link_title'],
172 6
                'text' => $jobLang['apply']['edit_link_label'],
173
            ];
174 6
        } elseif (WhichPortal::isHrPortal()) {
175 6
            if ($jobPoster->hr_advisors->contains('user_id', $user->id)) {
176
                $applyButton = [
177 4
                    'href' => route('hr_advisor.jobs.summary', $jobPoster->id),
178 4
                    'title' => null,
179 4
                    'text' => Lang::get('hr_advisor/job_summary.summary_title'),
180
                ];
181 2
            } else {
182
                $applyButton = [
183 1
                    'href' => route('hr_advisor.jobs.index'),
184 1
                    'title' => null,
185 1
                    'text' => Lang::get('hr_advisor/job_index.title'),
186
                ];
187 1
            }
188
        } elseif (Auth::check() && $jobPoster->isOpen()) {
189 1
            $application = JobApplication::where('applicant_id', Auth::user()->applicant->id)
190 1
            ->where('job_poster_id', $jobPoster->id)->first();
191 1
            // If applicants job application is not draft anymore then link to application preview page.
192
            if ($application != null && $application->application_status->name != 'draft') {
193
                $applyButton = [
194
                    'href' => route('applications.show', $application->id),
195
                    'title' => $jobLang['apply']['view_link_title'],
196
                    'text' => $jobLang['apply']['view_link_label'],
197
                ];
198
            } else {
199
                $applyButton = [
200
                    'href' => route('job.application.edit.1', $jobPoster->id),
201 6
                    'title' => $jobLang['apply']['apply_link_title'],
202 6
                    'text' => $jobLang['apply']['apply_link_label'],
203
                ];
204 6
            }
205 6
        } elseif (Auth::guest() && $jobPoster->isOpen()) {
206 6
            $applyButton = [
207 6
                'href' => route('job.application.edit.1', $jobPoster->id),
208 6
                'title' => $jobLang['apply']['login_link_title'],
209 6
                'text' => $jobLang['apply']['login_link_label'],
210 6
            ];
211 6
        } else {
212 6
            $applyButton = [
213 6
                'href' => null,
214
                'title' => null,
215
                'text' => $jobLang['apply']['job_closed_label'],
216
            ];
217
        }
218
219
        $jpb_release_date = strtotime('2019-08-21 16:18:17');
220
        $job_created_at = strtotime($jobPoster->created_at);
221
222
        // If the job poster is created after the release of the JPB.
223
        // Then, render with updated poster template.
224 2
        // Else, render with old poster template.
225
        if ($job_created_at > $jpb_release_date) {
226 2
            // Updated job poster (JPB).
227 2
            return view(
0 ignored issues
show
Bug introduced by
The function view was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

227
            return /** @scrutinizer ignore-call */ view(
Loading history...
228
                'applicant/jpb_job_post',
229 2
                [
230
                    'job_post' => $jobLang,
231 2
                    'frequencies' => Lang::get('common/lookup/frequency'),
232 2
                    'skill_template' => Lang::get('common/skills'),
233 2
                    'job' => $jobPoster,
234
                    'manager' => $jobPoster->manager,
235
                    'criteria' => $criteria,
236 2
                    'apply_button' => $applyButton,
237
                ]
238
            );
239
        } else {
240
            // Old job poster.
241
            return view(
242
                'applicant/job_post',
243
                [
244
                    'job_post' => $jobLang,
245
                    'frequencies' => Lang::get('common/lookup/frequency'),
246
                    'manager' => $jobPoster->manager,
247
                    'manager_profile_photo_url' => '/images/user.png', // TODO get real photo.
248
                    'team_culture' => $jobPoster->manager->team_culture,
249
                    'work_environment' => $jobPoster->manager->work_environment,
250
                    'workplace_photos' => $workplacePhotos,
251
                    'job' => $jobPoster,
252
                    'criteria' => $criteria,
253
                    'apply_button' => $applyButton,
254
                    'skill_template' => Lang::get('common/skills'),
255
                ]
256
            );
257 1
        }
258
    }
259 1
260
    /**
261
     * Display the form for editing an existing Job Poster
262
     * Only allows editing fields that don't appear on the react-built Job Poster Builder.
263
     *
264
     * @param  \Illuminate\Http\Request $request   Incoming request object.
265
     * @param  \App\Models\JobPoster    $jobPoster Job Poster object.
266
     * @return \Illuminate\Http\Response
267
     */
268
    public function edit(Request $request, JobPoster $jobPoster)
269 1
    {
270
        $manager = $jobPoster->manager;
271 1
272
        if ($jobPoster->job_poster_questions === null || $jobPoster->job_poster_questions->count() === 0) {
273
            $jobPoster->job_poster_questions()->saveMany($this->populateDefaultQuestions());
274 1
            $jobPoster->refresh();
275
        }
276
277 1
        return view(
0 ignored issues
show
Bug introduced by
The function view was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

277
        return /** @scrutinizer ignore-call */ view(
Loading history...
278 1
            'manager/job_create',
279 1
            [
280 1
                // Localization Strings.
281
                'job_l10n' => Lang::get('manager/job_edit'),
282
                // Data.
283
                'manager' => $manager,
284
                'job' => $jobPoster,
285
                'form_action_url' => route('admin.jobs.update', $jobPoster),
0 ignored issues
show
Bug introduced by
The function route was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

285
                'form_action_url' => /** @scrutinizer ignore-call */ route('admin.jobs.update', $jobPoster),
Loading history...
286
            ]
287
        );
288
    }
289
290
    /**
291 1
     * Create a blank job poster for the specified manager
292
     *
293 1
     * @param  \App\Models\Manager $manager Incoming Manager object.
294 1
     * @return \Illuminate\Http\Response Job Create view
295
     */
296 1
    public function createAsManager(Manager $manager)
297 1
    {
298 1
        $jobPoster = new JobPoster();
299 1
        $jobPoster->manager_id = $manager->id;
300
301
        // Save manager-specific info to the job poster - equivalent to the intro step of the JPB
302 1
        $divisionEn = $manager->getTranslation('division', 'en');
303
        $divisionFr = $manager->getTranslation('division', 'fr');
304 1
        $jobPoster->fill([
305
            'department_id' => $manager->department_id,
306 1
            'division' => ['en' => $divisionEn],
307
            'division' => ['fr' => $divisionFr],
308 1
        ]);
309 1
310
        $jobPoster->save();
311 1
312 1
        return redirect()->route('manager.jobs.edit', $jobPoster->id);
0 ignored issues
show
Bug introduced by
The function redirect was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

312
        return /** @scrutinizer ignore-call */ redirect()->route('manager.jobs.edit', $jobPoster->id);
Loading history...
313 1
    }
314 1
315
    /**
316
     * Update a resource in storage
317 1
     * NOTE: Only saves fields that are not on the react-built Job Poster Builder
318
     *
319 1
     * @param  \Illuminate\Http\Request $request   Incoming request object.
320
     * @param  \App\Models\JobPoster    $jobPoster Optional Job Poster object.
321 1
     * @return \Illuminate\Http\Response
322
     */
323 1
    public function store(Request $request, JobPoster $jobPoster)
324 1
    {
325
        // Don't allow edits for published Job Posters
326
        // Also check auth while we're at it.
327
        $this->authorize('update', $jobPoster);
328 1
        JobPosterValidator::validateUnpublished($jobPoster);
329 1
330
        $input = $request->input();
331
332 1
        if ($jobPoster->manager_id == null) {
333 1
            $jobPoster->manager_id = $request->user()->manager->id;
334
            $jobPoster->save();
335
        }
336
337 1
        if ($request->input('question')) {
338
            $validator = Validator::make($request->input('question'), [
339 1
                '*.question.*' => 'required|string',
340
            ], [
341 1
                'required' => Lang::get('validation.custom.job_poster_question.required'),
342
                'string' => Lang::get('validation.custom.job_poster_question.string')
343 1
            ]);
344 1
345 1
            if ($validator->fails()) {
346
                $request->session()->flash('errors', $validator->errors());
347 1
                return redirect(route('admin.jobs.edit', $jobPoster->id));
0 ignored issues
show
Bug introduced by
The function route was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

347
                return redirect(/** @scrutinizer ignore-call */ route('admin.jobs.edit', $jobPoster->id));
Loading history...
Bug introduced by
The function redirect was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

347
                return /** @scrutinizer ignore-call */ redirect(route('admin.jobs.edit', $jobPoster->id));
Loading history...
348
            }
349 1
        }
350 1
351 1
        $this->fillAndSaveJobPosterQuestions($input, $jobPoster, true);
352
353 1
        return redirect(route('manager.jobs.show', $jobPoster->id));
354 1
    }
355
356
    /**
357 1
     * Fill Job Poster's questions and save
358
     *
359 1
     * @param  mixed[]               $input     Field values.
360 1
     * @param  \App\Models\JobPoster $jobPoster Job Poster object.
361 1
     * @param  boolean               $replace   Remove existing relationships.
362 1
     * @return void
363 1
     */
364 1
    protected function fillAndSaveJobPosterQuestions(array $input, JobPoster $jobPoster, bool $replace) : void
365 1
    {
366 1
        if ($replace) {
367 1
            $jobPoster->job_poster_questions()->delete();
368 1
        }
369 1
370
        if (!array_key_exists('question', $input) || !is_array($input['question'])) {
371
            return;
372
        }
373
374
        foreach ($input['question'] as $question) {
375
            $jobQuestion = new JobPosterQuestion();
376
            $jobQuestion->job_poster_id = $jobPoster->id;
377
            $jobQuestion->fill(
378
                [
379
                    'question' => [
380
                        'en' => $question['question']['en'],
381 6
                        'fr' => $question['question']['fr']
382
383
                    ],
384
                    'description' => [
385 6
                        'en' => $question['description']['en'],
386 6
                        'fr' => $question['description']['fr']
387 6
                    ]
388
                ]
389
            );
390
            $jobPoster->save();
391
            $jobQuestion->save();
392 6
        }
393
    }
394 6
395
    /**
396 6
     * Get the localized default questions and add them to an array.
397
     *
398
     * @return mixed[]|void
399
     */
400
    protected function populateDefaultQuestions()
401 6
    {
402
        $defaultQuestions = [
403 6
            'en' => array_values(Lang::get('manager/job_create', [], 'en')['questions']),
404
            'fr' => array_values(Lang::get('manager/job_create', [], 'fr')['questions']),
405 6
        ];
406
407 6
        if (count($defaultQuestions['en']) !== count($defaultQuestions['fr'])) {
408
            Log::warning('There must be the same number of French and English default questions for a Job Poster.');
409 6
            return;
410
        }
411
412
        $jobQuestions = [];
413
414
        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...
415
            $jobQuestion = new JobPosterQuestion();
416
            $jobQuestion->fill(
417
                [
418
                    'question' => [
419 6
                        'en' => $defaultQuestions['en'][$i],
420
                        'fr' => $defaultQuestions['fr'][$i],
421 6
                    ]
422
                ]
423 6
            );
424 6
            $jobQuestions[] = $jobQuestion;
425 6
        }
426 6
427 6
        return $jobQuestions;
428 6
    }
429 6
430 6
    /**
431 6
     * Downloads a CSV file with the applicants who have applied to the job poster.
432 6
     *
433 6
     * @param  \App\Models\JobPoster $jobPoster Job Poster object.
434 6
     * @return \Symfony\Component\HttpFoundation\BinaryFileResponse
435 6
     */
436 6
    protected function downloadApplicants(JobPoster $jobPoster)
437
    {
438 6
        $tables = [];
439 6
        // The first row in the array represents the names of the columns in the spreadsheet.
440 6
        $tables[0] = ['Status', 'Applicant Name', 'Email', 'Language'];
441 6
442 6
        $application_status_id = ApplicationStatus::where('name', 'submitted')->first()->id;
443 6
        $applications = JobApplication::where('job_poster_id', $jobPoster->id)
444
            ->where('application_status_id', $application_status_id)
445
            ->get();
446 6
447 6
        $index = 1;
448 6
        foreach ($applications as $application) {
449 6
            $status = '';
450 6
            $username = $application->user_name;
451 6
            $user_email = $application->user_email;
452
            $language = strtoupper($application->preferred_language->name);
453
            // If the applicants veteran status name is NOT 'none' then set status to veteran.
454
            $non_veteran = VeteranStatus::where('name', 'none')->first()->id;
455 6
            if ($application->veteran_status_id != $non_veteran) {
456 6
                $status = 'Veteran';
457
            } else {
458
                // Check if the applicant is a canadian citizen.
459
                $canadian_citizen = CitizenshipDeclaration::where('name', 'citizen')->first()->id;
460
                if ($application->citizenship_declaration->id == $canadian_citizen) {
461
                    $status = 'Citizen';
462
                } else {
463
                    $status = 'Non-citizen';
464
                }
465
            }
466 6
            $tables[$index] = [$status, $username, $user_email, $language];
467
            $index++;
468 6
        }
469 6
470
        $filename = $jobPoster->id . '-' . 'applicants-data.csv';
471
472 6
        // Open file.
473 6
        $file = fopen($filename, 'w');
474
        // Iterate through tables and add each line to csv file.
475
        foreach ($tables as $line) {
476
            fputcsv($file, $line);
0 ignored issues
show
Bug introduced by
It seems like $file can also be of type false; however, parameter $handle of fputcsv() does only seem to accept resource, 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

476
            fputcsv(/** @scrutinizer ignore-type */ $file, $line);
Loading history...
477
        }
478
        // Close open file.
479
        fclose($file);
0 ignored issues
show
Bug introduced by
It seems like $file can also be of type false; however, parameter $handle of fclose() does only seem to accept resource, 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

479
        fclose(/** @scrutinizer ignore-type */ $file);
Loading history...
480
481
        $headers = [
482
            'Content-Type' => 'text/csv',
483
            'Content-Disposition' => 'attachment; filename=' . $filename,
484
        ];
485
486
        return Response::download($filename, $filename, $headers);
487
    }
488
}
489