Passed
Push — task/experience-skills ( d76f21 )
by Grant
05:20
created

JobApplication::experiences_education()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 2
c 0
b 0
f 0
dl 0
loc 4
rs 10
cc 1
nc 1
nop 0
1
<?php
2
3
/**
4
 * Created by Reliese Model.
5
 * Date: Thu, 12 Jul 2018 22:39:27 +0000.
6
 */
7
8
namespace App\Models;
9
10
use App\Events\ApplicationRetrieved;
11
use App\Events\ApplicationSaved;
12
use App\Models\Applicant;
13
use App\Models\ApplicationReview;
14
use App\Services\Validation\ApplicationValidator;
15
use Illuminate\Notifications\Notifiable;
16
17
/**
18
 * Class JobApplication
19
 *
20
 * @property int $id
21
 * @property int $job_poster_id
22
 * @property int $application_status_id
23
 * @property int $citizenship_declaration_id
24
 * @property int $veteran_status_id
25
 * @property int $preferred_language_id
26
 * @property int $applicant_id
27
 * @property int $applicant_snapshot_id
28
 * @property string $submission_signature
29
 * @property string $submission_date
30
 * @property boolean $experience_saved
31
 * @property boolean $language_requirement_confirmed
32
 * @property string $user_name
33
 * @property string $user_email
34
 * @property int $version_id
35
 * @property \Jenssegers\Date\Date $created_at
36
 * @property \Jenssegers\Date\Date $updated_at
37
 *
38
 * @property \App\Models\Applicant $applicant
39
 * @property \App\Models\Applicant $applicant_snapshot
40
 * @property \App\Models\Lookup\ApplicationStatus $application_status
41
 * @property \App\Models\Lookup\CitizenshipDeclaration $citizenship_declaration
42
 * @property \App\Models\Lookup\VeteranStatus $veteran_status
43
 * @property \App\Models\Lookup\PreferredLanguage $preferred_language
44
 * @property \App\Models\JobPoster $job_poster
45
 * @property \Illuminate\Database\Eloquent\Collection $job_application_answers
46
 * @property \Illuminate\Database\Eloquent\Collection $skill_declarations
47
 * @property \App\Models\ApplicationReview $application_review
48
 * @property \Illuminate\Database\Eloquent\Collection $degrees
49
 * @property \Illuminate\Database\Eloquent\Collection $courses
50
 * @property \Illuminate\Database\Eloquent\Collection $work_experiences
51
 * @property \Illuminate\Database\Eloquent\Collection $references
52
 * @property \Illuminate\Database\Eloquent\Collection $work_samples
53
 * @property \Illuminate\Database\Eloquent\Collection $projects
54
 * @property \App\Models\JobApplicationVersion $job_application_version
55
 *
56
 * Version 2 application models.
57
 * @property \Illuminate\Database\Eloquent\Collection $experiences_work
58
 * @property \Illuminate\Database\Eloquent\Collection $experiences_personal
59
 * @property \Illuminate\Database\Eloquent\Collection $experiences_education
60
 * @property \Illuminate\Database\Eloquent\Collection $experiences_award
61
 * @property \Illuminate\Database\Eloquent\Collection $experiences_community
62
 */
63
class JobApplication extends BaseModel
64
{
65
66
    use Notifiable;
67
68
    protected $dispatchesEvents = [
69
        'retrieved' => ApplicationRetrieved::class,
70
        'saved' => ApplicationSaved::class,
71
    ];
72
73
    protected $casts = [
74
        'job_poster_id' => 'int',
75
        'application_status_id' => 'int',
76
        'citizenship_declaration_id' => 'int',
77
        'veteran_status_id' => 'int',
78
        'preferred_language_id' => 'int',
79
        'applicant_id' => 'int',
80
        'applicant_snapshot_id' => 'int',
81
        'submission_signature' => 'string',
82
        'submission_date' => 'string',
83
        'experience_saved' => 'boolean',
84
        'language_requirement_confirmed' => 'boolean',
85
        'version_id' => 'int',
86
    ];
87
    protected $fillable = [
88
        'citizenship_declaration_id',
89
        'language_requirement_confirmed',
90
        'veteran_status_id',
91
        'preferred_language_id',
92
        'submission_signature',
93
        'submission_date',
94
        'experience_saved',
95
    ];
96
97
    /**
98
     * The accessors to append to the model's array/json form.
99
     *
100
     * @var array
101
     */
102
    protected $appends = ['meets_essential_criteria'];
103
104
    protected function createApplicantSnapshot($applicant_id)
105
    {
106
        $applicant = Applicant::where('id', $applicant_id)->firstOrFail();
107
108
        $snapshot = $applicant->replicate();
0 ignored issues
show
Unused Code introduced by
The assignment to $snapshot is dead and can be removed.
Loading history...
109
    }
110
111
    public function applicant()
112
    {
113
        return $this->belongsTo(\App\Models\Applicant::class);
114
    }
115
116
    public function applicant_snapshot() //phpcs:ignore
117
    {
118
        return $this->belongsTo(\App\Models\Applicant::class, 'applicant_snapshot_id');
119
    }
120
121
    public function application_status() //phpcs:ignore
122
    {
123
        return $this->belongsTo(\App\Models\Lookup\ApplicationStatus::class);
124
    }
125
126
    public function citizenship_declaration() //phpcs:ignore
127
    {
128
        return $this->belongsTo(\App\Models\Lookup\CitizenshipDeclaration::class);
129
    }
130
131
    public function veteran_status() //phpcs:ignore
132
    {
133
        return $this->belongsTo(\App\Models\Lookup\VeteranStatus::class);
134
    }
135
136
    public function preferred_language() //phpcs:ignore
137
    {
138
        return $this->belongsTo(\App\Models\Lookup\PreferredLanguage::class);
139
    }
140
141
    public function job_poster() //phpcs:ignore
142
    {
143
        return $this->belongsTo(\App\Models\JobPoster::class);
144
    }
145
146
    public function job_application_answers() //phpcs:ignore
147
    {
148
        return $this->hasMany(\App\Models\JobApplicationAnswer::class);
149
    }
150
151
    public function skill_declarations() //phpcs:ignore
152
    {
153
        return $this->morphMany(\App\Models\SkillDeclaration::class, 'skillable');
154
    }
155
156
    public function application_review() //phpcs:ignore
157
    {
158
        return $this->hasOne(ApplicationReview::class);
159
    }
160
161
    public function degrees()
162
    {
163
        return $this->morphMany(\App\Models\Degree::class, 'degreeable')->orderBy('end_date', 'desc');
164
    }
165
166
    public function courses()
167
    {
168
        return $this->morphMany(\App\Models\Course::class, 'courseable')->orderBy('end_date', 'desc');
169
    }
170
171
    public function work_experiences() //phpcs:ignore
172
    {
173
        return $this->morphMany(\App\Models\WorkExperience::class, 'experienceable')->orderBy('end_date', 'desc');
174
    }
175
176
    public function references()
177
    {
178
        return $this->morphMany(\App\Models\Reference::class, 'referenceable');
179
    }
180
181
    public function projects()
182
    {
183
        return $this->morphMany(\App\Models\Project::class, 'projectable');
184
    }
185
186
    public function work_samples() //phpcs:ignore
187
    {
188
        return $this->morphMany(\App\Models\WorkSample::class, 'work_sampleable');
189
    }
190
191
    public function job_application_version() //phpcs:ignore
192
    {
193
        return $this->hasOne(\App\Models\JobApplicationVersion::class);
194
    }
195
196
    // Version 2 application models.
197
    public function experiences_work() //phpcs:ignore
198
    {
199
        return $this->morphMany(\App\Models\ExperienceWork::class, 'experienceable')
200
            ->orderBy('end_date', 'desc');
201
    }
202
203
    public function experiences_personal() //phpcs:ignore
204
    {
205
        return $this->morphMany(\App\Models\ExperiencePersonal::class, 'experienceable')
206
            ->orderBy('end_date', 'desc');
207
    }
208
209
    public function experiences_education() //phpcs:ignore
210
    {
211
        return $this->morphMany(\App\Models\ExperienceEducation::class, 'experienceable')
212
            ->orderBy('end_date', 'desc');
213
    }
214
215
    public function experiences_award() //phpcs:ignore
216
    {
217
        return $this->morphMany(\App\Models\ExperienceAward::class, 'experienceable');
218
    }
219
220
    public function experiences_community() //phpcs:ignore
221
    {
222
        return $this->morphMany(\App\Models\ExperienceCommunity::class, 'experienceable')
223
            ->orderBy('end_date', 'desc');
224
    }
225
226
    /**
227
     * Return either 'complete', 'incomplete' or 'error', depending on the
228
     * status of the requested section.
229
     *
230
     * @param  string $section Should be one of:
231
     *                              'basics'
232
     *                              'experience'
233
     *                              'essential_skills'
234
     *                              'asset_skills'
235
     *                              'preview'
236
     *
237
     * @return string $status   'complete', 'incomplete' or 'error'
238
     */
239
    public function getSectionStatus(string $section)
240
    {
241
        // TODO: determine whether sections are complete or invalid
242
        $validator = new ApplicationValidator();
243
        $status = 'incomplete';
244
        switch ($section) {
245
            case 'basics':
246
                if ($validator->basicsComplete($this)) {
247
                    $status = 'complete';
248
                }
249
                break;
250
            case 'experience':
251
                if ($validator->experienceComplete($this)) {
252
                    $status = 'complete';
253
                }
254
                break;
255
            case 'essential_skills':
256
                if ($validator->essentialSkillsComplete($this)) {
257
                    $status = 'complete';
258
                }
259
                break;
260
            case 'asset_skills':
261
                if ($validator->assetSkillsComplete($this)) {
262
                    $status = 'complete';
263
                }
264
                break;
265
            case 'preview':
266
                if (
267
                    $validator->basicsComplete($this) &&
268
                    $validator->experienceComplete($this) &&
269
                    $validator->essentialSkillsComplete($this) &&
270
                    $validator->assetSkillsComplete($this)
271
                ) {
272
                    $status = 'complete';
273
                }
274
                break;
275
            case 'confirm':
276
                if ($validator->affirmationComplete($this)) {
277
                    $status = 'complete';
278
                }
279
                break;
280
            default:
281
                $status = 'error';
282
                break;
283
        }
284
        return $status;
285
    }
286
287
    /**
288
     * Check if the status of the application is 'draft'
289
     *
290
     * @return boolean
291
     */
292
    public function isDraft(): bool
293
    {
294
        return $this->application_status->name === 'draft';
295
    }
296
297
    /**
298
     * Returns true if this meets all the essential criteria.
299
     * That means it has attached an SkillDeclaration for each essential criterion,
300
     * with a level at least as high as the required level.
301
     * NOTE: If this application is in draft status, it will use
302
     *  SkillDeclarations from the the applicants profile for this check.
303
     *
304
     * @return boolean
305
     */
306
    public function meetsEssentialCriteria(): bool
307
    {
308
        $essentialCriteria = $this->job_poster->criteria->filter(
309
            function ($value, $key) {
0 ignored issues
show
Unused Code introduced by
The parameter $key is not used and could be removed. ( Ignorable by Annotation )

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

309
            function ($value, /** @scrutinizer ignore-unused */ $key) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
310
                return $value->criteria_type->name == 'essential';
311
            }
312
        );
313
        $source = $this->isDraft() ? $this->applicant : $this;
314
        foreach ($essentialCriteria as $criterion) {
315
            $skillDeclaration = $source->skill_declarations->where('skill_id', $criterion->skill_id)->first();
316
            if (
317
                $skillDeclaration === null ||
318
                $skillDeclaration->skill_level_id < $criterion->skill_level_id
319
            ) {
320
                return false;
321
            }
322
        }
323
        return true;
324
    }
325
326
    /**
327
     * Accessor for meetsEssentialCriteria function, which
328
     * allows this value to be automatically appended to array/json representation.
329
     *
330
     * @return boolean
331
     */
332
    public function getMeetsEssentialCriteriaAttribute(): bool
333
    {
334
        return $this->meetsEssentialCriteria();
335
    }
336
337
    /**
338
     * Save copies of all relevant profile data to this application.
339
     *
340
     *
341
     * @return void
342
     */
343
    public function saveProfileSnapshot(): void
344
    {
345
        $applicant = $this->applicant->fresh();
346
347
        $this->user_name = $applicant->user->full_name;
348
        $this->user_email = $applicant->user->email;
349
        $this->save();
350
351
        // Delete previous snapshot.
352
        $this->degrees()->delete();
353
        $this->courses()->delete();
354
        $this->work_experiences()->delete();
355
        $this->projects()->delete();
356
        $this->references()->delete();
357
        $this->work_samples()->delete();
358
        $this->skill_declarations()->delete();
359
360
        $this->degrees()->saveMany($applicant->degrees->map->replicate());
361
        $this->courses()->saveMany($applicant->courses->map->replicate());
362
        $this->work_experiences()->saveMany($applicant->work_experiences->map->replicate());
363
364
        $copyWithHistory = function ($model) {
365
            return [
366
                'old' => $model,
367
                'new' => $model->replicate()
368
            ];
369
        };
370
371
372
        $projectMap = $applicant->projects->map($copyWithHistory);
373
        $referenceMap = $applicant->references->map($copyWithHistory);
374
        $workSampleMap = $applicant->work_samples->map($copyWithHistory);
375
        $skillDeclarationMap = $applicant->skill_declarations->map($copyWithHistory);
376
377
        // First link new projects, references, work samples and skill declarations to this application.
378
        $this->projects()->saveMany($projectMap->pluck('new'));
379
        $this->references()->saveMany($referenceMap->pluck('new'));
380
        $this->work_samples()->saveMany($workSampleMap->pluck('new'));
381
        $this->skill_declarations()->saveMany($skillDeclarationMap->pluck('new'));
382
383
        $findNewFromOld = function ($mapping, $old) {
384
            $matchingItem = $mapping->first(function ($value) use ($old) {
385
                return $value['old']->id === $old->id;
386
            });
387
            return $matchingItem['new'];
388
        };
389
390
        // Replicate copies shallow attributes, but not relationships. We have to copy those ourselves.
391
        $findNewReferenceFromOld = function ($old) use ($findNewFromOld, $referenceMap) {
392
            return $findNewFromOld($referenceMap, $old);
393
        };
394
395
        $findNewSkillDeclarationFromOld = function ($old) use ($findNewFromOld, $skillDeclarationMap) {
396
            return $findNewFromOld($skillDeclarationMap, $old);
397
        };
398
399
        // Link projects and references.
400
        foreach ($projectMap as $item) {
401
            $old = $item['old'];
402
            $newProj = $item['new'];
403
            $newReferences = $old->references->map($findNewReferenceFromOld);
404
            $newProj->references()->sync($newReferences);
405
        }
406
407
        // Link references and skills.
408
        foreach ($referenceMap as $item) {
409
            $old = $item['old'];
410
            $newRef = $item['new'];
411
            $newSkillDecs = $old->skill_declarations->map($findNewSkillDeclarationFromOld);
412
            $newRef->skill_declarations()->sync($newSkillDecs);
413
        }
414
415
        // Link work samples and skills.
416
        foreach ($workSampleMap as $item) {
417
            $old = $item['old'];
418
            $newSample = $item['new'];
419
            $newSkillDecs = $old->skill_declarations->map($findNewSkillDeclarationFromOld);
420
            $newSample->skill_declarations()->sync($newSkillDecs);
421
        }
422
    }
423
}
424