Passed
Push — master ( cd2cd2...6fd81b )
by Tristan
25:03 queued 13:24
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\Request;
10
use App\Http\Controllers\Controller;
11
12
use Carbon\Carbon;
13
14
use App\Mail\JobPosterReviewRequested;
15
16
use App\Models\JobPoster;
17
use App\Models\JobPosterQuestion;
18
19
use App\Services\Validation\JobPosterValidator;
20
use Jenssegers\Date\Date;
21
22
class JobController extends Controller
23
{
24
    /**
25
     * Display a listing of JobPosters.
26
     *
27
     * @return \Illuminate\Http\Response
28
     */
29
    public function index()
30
    {
31
        $now = Carbon::now();
32
33
        // Find published jobs that are currently open for applications.
34
        // Eager load required relationships: Department, Province, JobTerm.
35
        // Eager load the count of submitted applications, to prevent the relationship
36
        // from being actually loaded and firing off events.
37
        $jobs = JobPoster::where('open_date_time', '<=', $now)
38
            ->where('close_date_time', '>=', $now)
39
            ->where('published', true)
40
            ->with([
41
                'department',
42
                'province',
43
                'job_term',
44
            ])
45
            ->withCount([
46
                'submitted_applications',
47
            ])
48
            ->get();
49
        return view('applicant/job_index', [
50
            'job_index' => Lang::get('applicant/job_index'),
51
            'jobs' => $jobs
52
        ]);
53
    }
54
55
    /**
56
     * Display a listing of a manager's JobPosters.
57
     *
58
     * @return \Illuminate\Http\Response
59
     */
60
    public function managerIndex()
61
    {
62
        $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...
63
        $jobs = JobPoster::where('manager_id', $manager->id)
64
            ->withCount('submitted_applications')
65
            ->get();
66
67
        return view('manager/job_index', [
68
            // Localization Strings.
69
            'jobs_l10n' => Lang::get('manager/job_index'),
70
            // Data.
71
            'jobs' => $jobs,
72
        ]);
73 1
    }
74
75 1
    /**
76 1
     * Submit the Job Poster for review.
77 1
     *
78 1
     * @param  \Illuminate\Http\Request $request   Incoming request object.
79
     * @param  \App\Models\JobPoster    $jobPoster Job Poster object.
80 1
     * @return \Illuminate\Http\Response
81
     */
82 1
    public function submitForReview(Request $request, JobPoster $jobPoster)
83
    {
84 1
        // Update review request timestamp.
85
        $jobPoster->review_requested_at = new Date();
86
        $jobPoster->save();
87
88
        // Refresh model instance with updated DB values.
89
        $jobPoster = JobPoster::withCount('submitted_applications')->where('id', $jobPoster->id)->first();
90
91
        // Send email.
92
        $reviewer_email = config('mail.reviewer_email');
93
        if (isset($reviewer_email)) {
94
            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

94
            Mail::to($reviewer_email)->send(new JobPosterReviewRequested($jobPoster, /** @scrutinizer ignore-type */ Auth::user()));
Loading history...
95 1
        } else {
96
            Log::error('The reviewer email environment variable is not set.');
97
        }
98 1
99 1
        return view('manager/job_index/job', [
100
            // Localization Strings.
101
            'jobs_l10n' => Lang::get('manager/job_index'),
102 1
            'job' => $jobPoster
103
        ]);
104
    }
105 1
106 1
    /**
107 1
     * Delete a draft Job Poster.
108
     *
109
     * @param  \Illuminate\Http\Request $request   Incoming request object.
110
     * @param  \App\Models\JobPoster    $jobPoster Job Poster object.
111
     * @return \Illuminate\Http\Response
1 ignored issue
show
Coding Style introduced by
Function return type is not void, but function has no return statement
Loading history...
112 1
     */
113
    public function destroy(Request $request, JobPoster $jobPoster)
114 1
    {
115 1
        $jobPoster->delete();
116
    }
117
118
    /**
119
     * Display the specified job poster.
120
     *
121
     * @param  \Illuminate\Http\Request $request   Incoming request object.
122
     * @param  \App\Models\JobPoster    $jobPoster Job Poster object.
123
     * @return \Illuminate\Http\Response
124
     */
125
    public function show(Request $request, JobPoster $jobPoster)
126
    {
127
        $jobPoster->load([
128
            'department',
129
            'criteria.skill.skill_type',
130
            'manager.team_culture',
131
            'manager.work_environment'
132
        ]);
133
134
        $user = Auth::user();
0 ignored issues
show
Unused Code introduced by
The assignment to $user is dead and can be removed.
Loading history...
135
136
        // TODO: Improve workplace photos, and reference them in template direction from WorkEnvironment model.
137
        $workplacePhotos = [];
138 6
        foreach ($jobPoster->manager->work_environment->workplace_photo_captions as $photoCaption) {
139
            $workplacePhotos[] = [
140 6
                'description' => $photoCaption->description,
141 6
                'url' => '/images/user.png'
142
            ];
143
        }
144
145
        // TODO: replace route('manager.show',manager.id) in templates with link using slug.
146
        $criteria = [
147 6
            'essential' => $jobPoster->criteria->filter(
148
                function ($value, $key) {
149
                    return $value->criteria_type->name == 'essential';
150 6
                }
151 6
            ),
152
            'asset' => $jobPoster->criteria->filter(
153
                function ($value, $key) {
154
                    return $value->criteria_type->name == 'asset';
155
                }
156
            ),
157
        ];
158
159
        $jobLang = Lang::get('applicant/job_post');
160 6
161
        $applyButton = [];
162 4
        if (!$jobPoster->published && $this->authorize('update', $jobPoster)) {
163 6
            $applyButton = [
164
                'href' => route('manager.jobs.edit', $jobPoster->id),
165 6
                'title' => $jobLang['apply']['edit_link_title'],
166
                'text' => $jobLang['apply']['edit_link_label'],
167 4
            ];
168 6
        } elseif (Auth::check() && $jobPoster->isOpen()) {
169
            $applyButton = [
170
                'href' => route('job.application.edit.1', $jobPoster->id),
171
                'title' => $jobLang['apply']['apply_link_title'],
172 6
                'text' => $jobLang['apply']['apply_link_label'],
173
            ];
174 6
        } elseif (Auth::guest() && $jobPoster->isOpen()) {
175 6
            $applyButton = [
176
                'href' => route('job.application.edit.1', $jobPoster->id),
177 4
                'title' => $jobLang['apply']['login_link_title'],
178 4
                'text' => $jobLang['apply']['login_link_label'],
179 4
            ];
180
        } else {
181 2
            $applyButton = [
182
                'href' => null,
183 1
                'title' => null,
184 1
                'text' => $jobLang['apply']['job_closed_label'],
185 1
            ];
186
        }
187 1
188
        $jpb_release_date = strtotime('2019-08-21 16:18:17');
189 1
        $job_created_at = strtotime($jobPoster->created_at);
190 1
191 1
        // If the job poster is created after the release of the JPB.
192
        // Then, render with updated poster template.
193
        // Else, render with old poster template.
194
        if ($job_created_at > $jpb_release_date) {
195
            // Updated job poster (JPB).
196
            return view(
197
                'applicant/jpb_job_post',
198
                [
199
                    'job_post' => $jobLang,
200
                    'skill_template' => Lang::get('common/skills'),
201 6
                    'job' => $jobPoster,
202 6
                    'manager' => $jobPoster->manager,
203
                    'criteria' => $criteria,
204 6
                    'apply_button' => $applyButton,
205 6
                ]
206 6
            );
207 6
        } else {
208 6
            // Old job poster.
209 6
            return view(
210 6
                'applicant/job_post',
211 6
                [
212 6
                    'job_post' => $jobLang,
213 6
                    'manager' => $jobPoster->manager,
214
                    'manager_profile_photo_url' => '/images/user.png', // TODO get real photo.
215
                    'team_culture' => $jobPoster->manager->team_culture,
216
                    'work_environment' => $jobPoster->manager->work_environment,
217
                    'workplace_photos' => $workplacePhotos,
218
                    'job' => $jobPoster,
219
                    'criteria' => $criteria,
220
                    'apply_button' => $applyButton,
221
                    'skill_template' => Lang::get('common/skills'),
222
                ]
223
            );
224 2
        }
225
    }
226 2
227 2
    /**
228
     * Display the form for editing an existing Job Poster
229 2
     * Only allows editing fields that don't appear on the react-built Job Poster Builder.
230
     *
231 2
     * @param  \Illuminate\Http\Request $request   Incoming request object.
232 2
     * @param  \App\Models\JobPoster    $jobPoster Job Poster object.
233 2
     * @return \Illuminate\Http\Response
234
     */
235
    public function edit(Request $request, JobPoster $jobPoster)
236 2
    {
237
        $manager = $jobPoster->manager;
238
239
        if ($jobPoster->job_poster_questions === null || $jobPoster->job_poster_questions->count() === 0) {
240
            $jobPoster->job_poster_questions()->saveMany($this->populateDefaultQuestions());
241
            $jobPoster->refresh();
242
        }
243
244
        return view(
245
            'manager/job_create',
246
            [
247
                // Localization Strings.
248
                'job_l10n' => Lang::get('manager/job_edit'),
249
                // Data.
250
                'manager' => $manager,
251
                'job' => $jobPoster,
252
                'form_action_url' => route('admin.jobs.update', $jobPoster),
253
            ]
254
        );
255
    }
256
257 1
    /**
258
     * Update a resource in storage
259 1
     * NOTE: Only saves fields that are not on the react-built Job Poster Builder
260
     *
261
     * @param  \Illuminate\Http\Request $request   Incoming request object.
262
     * @param  \App\Models\JobPoster    $jobPoster Optional Job Poster object.
263
     * @return \Illuminate\Http\Response
264
     */
265
    public function store(Request $request, JobPoster $jobPoster)
266
    {
267
        // Don't allow edits for published Job Posters
268
        // Also check auth while we're at it.
269 1
        $this->authorize('update', $jobPoster);
270
        JobPosterValidator::validateUnpublished($jobPoster);
271 1
272
        $input = $request->input();
273
274 1
        if ($jobPoster->manager_id == null) {
275
            $jobPoster->manager_id = $request->user()->manager->id;
276
            $jobPoster->save();
277 1
        }
278 1
279 1
        $this->fillAndSaveJobPoster($input, $jobPoster);
280 1
281
        $this->fillAndSaveJobPosterQuestions($input, $jobPoster, true);
282
283
        return redirect(route('manager.jobs.show', $jobPoster->id));
284
    }
285
286
    /**
287
     * Fill Job Poster model's properties and save
288
     * NOTE: only saves properties which don't appear on the Job Poster Builder
289
     *
290
     * @param  mixed[]               $input     Field values.
291 1
     * @param  \App\Models\JobPoster $jobPoster Job Poster object.
292
     * @return void
293 1
     */
294 1
    protected function fillAndSaveJobPoster(array $input, JobPoster $jobPoster) : void
295
    {
296 1
        $jobPoster->fill(
297 1
            [
298 1
                'open_date_time' => ptDayStartToUtcTime($input['open_date']),
299 1
                'close_date_time' => ptDayEndToUtcTime($input['close_date']),
300
                'start_date_time' => ptDayStartToUtcTime($input['start_date']),
301
                'salary_min' => $input['salary_min'],
302 1
                'salary_max' => $input['salary_max'],
303
                'noc' => $input['noc'],
304 1
            ]
305
        );
306 1
        $jobPoster->save();
307
    }
308 1
309 1
    /**
310
     * Fill Job Poster's questions and save
311 1
     *
312 1
     * @param  mixed[]               $input     Field values.
313 1
     * @param  \App\Models\JobPoster $jobPoster Job Poster object.
314 1
     * @param  boolean               $replace   Remove existing relationships.
315
     * @return void
316
     */
317 1
    protected function fillAndSaveJobPosterQuestions(array $input, JobPoster $jobPoster, bool $replace) : void
318
    {
319 1
        if ($replace) {
320
            $jobPoster->job_poster_questions()->delete();
321 1
        }
322
323 1
        if (!array_key_exists('question', $input) || !is_array($input['question'])) {
324 1
            return;
325
        }
326
327
        foreach ($input['question'] as $question) {
328 1
            $jobQuestion = new JobPosterQuestion();
329 1
            $jobQuestion->job_poster_id = $jobPoster->id;
330
            $jobQuestion->fill(
331
                [
332 1
                    'en' => [
333 1
                        'question' => $question['question']['en'],
334
                        'description' => $question['description']['en']
335
                    ],
336
                    'fr' => [
337 1
                        'question' => $question['question']['fr'],
338
                        'description' => $question['description']['fr']
339 1
                    ]
340
                ]
341 1
            );
342
            $jobQuestion->save();
343 1
        }
344 1
    }
345 1
346
    /**
347 1
     * Get the localized default questions and add them to an array.
348
     *
349 1
     * @return mixed[]|void
350 1
     */
351 1
    protected function populateDefaultQuestions()
352
    {
353 1
        $defaultQuestions = [
354 1
            'en' => array_values(Lang::get('manager/job_create', [], 'en')['questions']),
355
            'fr' => array_values(Lang::get('manager/job_create', [], 'fr')['questions']),
356
        ];
357 1
358
        if (count($defaultQuestions['en']) !== count($defaultQuestions['fr'])) {
359 1
            Log::warning('There must be the same number of French and English default questions for a Job Poster.');
360 1
            return;
361 1
        }
362 1
363 1
        $jobQuestions = [];
364 1
365 1
        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...
366 1
            $jobQuestion = new JobPosterQuestion();
367 1
            $jobQuestion->fill(
368 1
                [
369 1
                    'en' => [
370
                        'question' => $defaultQuestions['en'][$i],
371
                    ],
372
                    'fr' => [
373
                        'question' => $defaultQuestions['fr'][$i],
374
                    ]
375
                ]
376
            );
377
            $jobQuestions[] = $jobQuestion;
378
        }
379
380
        return $jobQuestions;
381 6
    }
382
}
383