Passed
Push — task/laravel-breadcrumbs ( 3beccb...a96280 )
by Yonathan
10:46 queued 10s
created

JobApplication   B

Complexity

Total Complexity 44

Size/Duplication

Total Lines 322
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 44
eloc 141
dl 0
loc 322
rs 8.8798
c 0
b 0
f 0

22 Methods

Rating   Name   Duplication   Size   Complexity  
A work_experiences() 0 3 1
A projects() 0 3 1
A references() 0 3 1
A preferred_language() 0 3 1
A courses() 0 3 1
A applicant_snapshot() 0 3 1
A job_poster() 0 3 1
A application_review() 0 3 1
A citizenship_declaration() 0 3 1
A degrees() 0 3 1
A veteran_status() 0 3 1
A applicant() 0 3 1
A application_status() 0 3 1
A createApplicantSnapshot() 0 5 1
A work_samples() 0 3 1
A skill_declarations() 0 3 1
A job_application_answers() 0 3 1
A saveProfileSnapshot() 0 78 4
A meetsEssentialCriteria() 0 18 5
A isDraft() 0 3 1
A getMeetsEssentialCriteriaAttribute() 0 3 1
C getSectionStatus() 0 46 16

How to fix   Complexity   

Complex Class

Complex classes like JobApplication often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use JobApplication, and based on these observations, apply Extract Interface, too.

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\Models\Applicant;
11
use App\Models\ApplicationReview;
12
use Illuminate\Notifications\Notifiable;
13
use App\Events\ApplicationSaved;
14
use App\Events\ApplicationRetrieved;
15
use App\Services\Validation\ApplicationValidator;
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 \Jenssegers\Date\Date $created_at
35
 * @property \Jenssegers\Date\Date $updated_at
36
 *
37
 * @property \App\Models\Applicant $applicant
38
 * @property \App\Models\Applicant $applicant_snapshot
39
 * @property \App\Models\Lookup\ApplicationStatus $application_status
40
 * @property \App\Models\Lookup\CitizenshipDeclaration $citizenship_declaration
41
 * @property \App\Models\Lookup\VeteranStatus $veteran_status
42
 * @property \App\Models\Lookup\PreferredLanguage $preferred_language
43
 * @property \App\Models\JobPoster $job_poster
44
 * @property \Illuminate\Database\Eloquent\Collection $job_application_answers
45
 * @property \Illuminate\Database\Eloquent\Collection $skill_declarations
46
 * @property \App\Models\ApplicationReview $application_review
47
 * @property \Illuminate\Database\Eloquent\Collection $degrees
48
 * @property \Illuminate\Database\Eloquent\Collection $courses
49
 * @property \Illuminate\Database\Eloquent\Collection $work_experiences
50
 * @property \Illuminate\Database\Eloquent\Collection $references
51
 * @property \Illuminate\Database\Eloquent\Collection $work_samples
52
 * @property \Illuminate\Database\Eloquent\Collection $projects
53
 */
54
class JobApplication extends BaseModel
55
{
56
57
    use Notifiable;
0 ignored issues
show
Bug introduced by
The trait Illuminate\Notifications\Notifiable requires the property $email which is not provided by App\Models\JobApplication.
Loading history...
58
59
    protected $dispatchesEvents = [
1 ignored issue
show
Coding Style Documentation introduced by
Missing member variable doc comment
Loading history...
60
        'retrieved' => ApplicationRetrieved::class,
61
        'saved' => ApplicationSaved::class,
62
    ];
63
64
    protected $casts = [
1 ignored issue
show
Coding Style Documentation introduced by
Missing member variable doc comment
Loading history...
65
        'job_poster_id' => 'int',
66
        'application_status_id' => 'int',
67
        'citizenship_declaration_id' => 'int',
68
        'veteran_status_id' => 'int',
69
        'preferred_language_id' => 'int',
70
        'applicant_id' => 'int',
71
        'applicant_snapshot_id' => 'int',
72
        'submission_signature' => 'string',
73
        'submission_date' => 'string',
74
        'experience_saved' => 'boolean',
75
        'language_requirement_confirmed' => 'boolean'
76
    ];
77
    protected $fillable = [
1 ignored issue
show
Coding Style Documentation introduced by
Missing member variable doc comment
Loading history...
78
        'citizenship_declaration_id',
79
        'language_requirement_confirmed',
80
        'veteran_status_id',
81
        'preferred_language_id',
82
        'submission_signature',
83
        'submission_date',
84
        'experience_saved',
85
    ];
86
87
    /**
88
     * The accessors to append to the model's array/json form.
89
     *
90
     * @var array
91
     */
92
    protected $appends = ['meets_essential_criteria'];
93
94
    protected function createApplicantSnapshot($applicant_id)
1 ignored issue
show
introduced by
Method \App\Models\JobApplication::createApplicantSnapshot() does not have parameter type hint nor @param annotation for its parameter $applicant_id.
Loading history...
introduced by
Method \App\Models\JobApplication::createApplicantSnapshot() does not have void return type hint.
Loading history...
Coding Style Documentation introduced by
Missing doc comment for function createApplicantSnapshot()
Loading history...
95
    {
96
        $applicant = Applicant::where('id', $applicant_id)->firstOrFail();
97
98
        $snapshot = $applicant->replicate();
0 ignored issues
show
Unused Code introduced by
The assignment to $snapshot is dead and can be removed.
Loading history...
99
    }
100
101
    public function applicant()
2 ignored issues
show
introduced by
Method \App\Models\JobApplication::applicant() does not have return type hint nor @return annotation for its return value.
Loading history...
Coding Style Documentation introduced by
Missing doc comment for function applicant()
Loading history...
102
    {
103
        return $this->belongsTo(\App\Models\Applicant::class);
104
    }
105
106
    public function applicant_snapshot()
2 ignored issues
show
introduced by
Method \App\Models\JobApplication::applicant_snapshot() does not have return type hint nor @return annotation for its return value.
Loading history...
Coding Style Documentation introduced by
Missing doc comment for function applicant_snapshot()
Loading history...
Coding Style introduced by
Method name "JobApplication::applicant_snapshot" is not in camel caps format
Loading history...
107
    {
108
        return $this->belongsTo(\App\Models\Applicant::class, 'applicant_snapshot_id');
109
    }
110
111
    public function application_status()
2 ignored issues
show
introduced by
Method \App\Models\JobApplication::application_status() does not have return type hint nor @return annotation for its return value.
Loading history...
Coding Style Documentation introduced by
Missing doc comment for function application_status()
Loading history...
Coding Style introduced by
Method name "JobApplication::application_status" is not in camel caps format
Loading history...
112
    {
113
        return $this->belongsTo(\App\Models\Lookup\ApplicationStatus::class);
114
    }
115
116
    public function citizenship_declaration()
2 ignored issues
show
introduced by
Method \App\Models\JobApplication::citizenship_declaration() does not have return type hint nor @return annotation for its return value.
Loading history...
Coding Style Documentation introduced by
Missing doc comment for function citizenship_declaration()
Loading history...
Coding Style introduced by
Method name "JobApplication::citizenship_declaration" is not in camel caps format
Loading history...
117
    {
118
        return $this->belongsTo(\App\Models\Lookup\CitizenshipDeclaration::class);
119
    }
120
121
    public function veteran_status()
2 ignored issues
show
introduced by
Method \App\Models\JobApplication::veteran_status() does not have return type hint nor @return annotation for its return value.
Loading history...
Coding Style Documentation introduced by
Missing doc comment for function veteran_status()
Loading history...
Coding Style introduced by
Method name "JobApplication::veteran_status" is not in camel caps format
Loading history...
122
    {
123
        return $this->belongsTo(\App\Models\Lookup\VeteranStatus::class);
124
    }
125
126
    public function preferred_language()
2 ignored issues
show
introduced by
Method \App\Models\JobApplication::preferred_language() does not have return type hint nor @return annotation for its return value.
Loading history...
Coding Style Documentation introduced by
Missing doc comment for function preferred_language()
Loading history...
Coding Style introduced by
Method name "JobApplication::preferred_language" is not in camel caps format
Loading history...
127
    {
128
        return $this->belongsTo(\App\Models\Lookup\PreferredLanguage::class);
129
    }
130
131
    public function job_poster()
2 ignored issues
show
introduced by
Method \App\Models\JobApplication::job_poster() does not have return type hint nor @return annotation for its return value.
Loading history...
Coding Style Documentation introduced by
Missing doc comment for function job_poster()
Loading history...
Coding Style introduced by
Method name "JobApplication::job_poster" is not in camel caps format
Loading history...
132
    {
133
        return $this->belongsTo(\App\Models\JobPoster::class);
134
    }
135
136
    public function job_application_answers()
2 ignored issues
show
introduced by
Method \App\Models\JobApplication::job_application_answers() does not have return type hint nor @return annotation for its return value.
Loading history...
Coding Style Documentation introduced by
Missing doc comment for function job_application_answers()
Loading history...
Coding Style introduced by
Method name "JobApplication::job_application_answers" is not in camel caps format
Loading history...
137
    {
138
        return $this->hasMany(\App\Models\JobApplicationAnswer::class);
139
    }
140
141
    public function skill_declarations()
2 ignored issues
show
introduced by
Method \App\Models\JobApplication::skill_declarations() does not have return type hint nor @return annotation for its return value.
Loading history...
Coding Style Documentation introduced by
Missing doc comment for function skill_declarations()
Loading history...
Coding Style introduced by
Method name "JobApplication::skill_declarations" is not in camel caps format
Loading history...
142
    {
143
        return $this->morphMany(\App\Models\SkillDeclaration::class, 'skillable');
144
    }
145
146
    public function application_review()
2 ignored issues
show
introduced by
Method \App\Models\JobApplication::application_review() does not have return type hint nor @return annotation for its return value.
Loading history...
Coding Style Documentation introduced by
Missing doc comment for function application_review()
Loading history...
Coding Style introduced by
Method name "JobApplication::application_review" is not in camel caps format
Loading history...
147
    {
148
        return $this->hasOne(ApplicationReview::class);
149
    }
150
151
    public function degrees()
2 ignored issues
show
introduced by
Method \App\Models\JobApplication::degrees() does not have return type hint nor @return annotation for its return value.
Loading history...
Coding Style Documentation introduced by
Missing doc comment for function degrees()
Loading history...
152
    {
153
        return $this->morphMany(\App\Models\Degree::class, 'degreeable')->orderBy('end_date', 'desc');
154
    }
155
156
    public function courses()
2 ignored issues
show
introduced by
Method \App\Models\JobApplication::courses() does not have return type hint nor @return annotation for its return value.
Loading history...
Coding Style Documentation introduced by
Missing doc comment for function courses()
Loading history...
157
    {
158
        return $this->morphMany(\App\Models\Course::class, 'courseable')->orderBy('end_date', 'desc');
159
    }
160
161
    public function work_experiences()
2 ignored issues
show
introduced by
Method \App\Models\JobApplication::work_experiences() does not have return type hint nor @return annotation for its return value.
Loading history...
Coding Style introduced by
Method name "JobApplication::work_experiences" is not in camel caps format
Loading history...
Coding Style Documentation introduced by
Missing doc comment for function work_experiences()
Loading history...
162
    {
163
        return $this->morphMany(\App\Models\WorkExperience::class, 'experienceable')->orderBy('end_date', 'desc');
164
    }
165
166
    public function references()
2 ignored issues
show
introduced by
Method \App\Models\JobApplication::references() does not have return type hint nor @return annotation for its return value.
Loading history...
Coding Style Documentation introduced by
Missing doc comment for function references()
Loading history...
167
    {
168
        return $this->morphMany(\App\Models\Reference::class, 'referenceable');
169
    }
170
171
    public function projects()
2 ignored issues
show
introduced by
Method \App\Models\JobApplication::projects() does not have return type hint nor @return annotation for its return value.
Loading history...
Coding Style Documentation introduced by
Missing doc comment for function projects()
Loading history...
172
    {
173
        return $this->morphMany(\App\Models\Project::class, 'projectable');
174
    }
175
176
    public function work_samples()
2 ignored issues
show
introduced by
Method \App\Models\JobApplication::work_samples() does not have return type hint nor @return annotation for its return value.
Loading history...
Coding Style Documentation introduced by
Missing doc comment for function work_samples()
Loading history...
Coding Style introduced by
Method name "JobApplication::work_samples" is not in camel caps format
Loading history...
177
    {
178
        return $this->morphMany(\App\Models\WorkSample::class, 'work_sampleable');
179
    }
180
181
    /**
182
     * Return either 'complete', 'incomplete' or 'error', depending on the
183
     * status of the requested section.
184
     *
185
     * @param  string $section Should be one of:
1 ignored issue
show
Coding Style Documentation introduced by
Parameter comment must end with a full stop
Loading history...
186
     *                              'basics'
187
     *                              'experience'
188
     *                              'essential_skills'
189
     *                              'asset_skills'
190
     *                              'preview'
191
     *
192
     * @return string $status   'complete', 'incomplete' or 'error'
193
     */
194
    public function getSectionStatus(string $section)
0 ignored issues
show
introduced by
Method \App\Models\JobApplication::getSectionStatus() does not have native return type hint for its return value but it should be possible to add it based on @return annotation "string".
Loading history...
195
    {
196
        // TODO: determine whether sections are complete or invalid
197
        $validator = new ApplicationValidator();
198
        $status = 'incomplete';
199
        switch ($section) {
200
            case 'basics':
201
                if ($validator->basicsComplete($this)) {
202
                    $status = 'complete';
203
                }
204
                break;
205
            case 'experience':
206
                if ($validator->experienceComplete($this)) {
207
                    $status = 'complete';
208
                }
209
                break;
210
            case 'essential_skills':
211
                if ($validator->essentialSkillsComplete($this)) {
212
                    $status = 'complete';
213
                }
214
                break;
215
            case 'asset_skills':
216
                if ($validator->assetSkillsComplete($this)) {
217
                    $status = 'complete';
218
                }
219
                break;
220
            case 'preview':
221
                if (
222
                    $validator->basicsComplete($this) &&
223
                    $validator->experienceComplete($this) &&
224
                    $validator->essentialSkillsComplete($this) &&
225
                    $validator->assetSkillsComplete($this)
226
                ) {
227
                    $status = 'complete';
228
                }
229
                break;
230
            case 'confirm':
231
                if ($validator->affirmationComplete($this)) {
232
                    $status = 'complete';
233
                }
234
                break;
235
            default:
236
                $status = 'error';
237
                break;
238
        }
239
        return $status;
240
    }
241
242
    /**
243
     * Check if the status of the application is 'draft'
244
     *
245
     * @return boolean
1 ignored issue
show
introduced by
Method \App\Models\JobApplication::isDraft() has useless @return annotation.
Loading history...
246
     */
247
    public function isDraft(): bool
248
    {
249
        return $this->application_status->name === 'draft';
250
    }
251
252
    /**
253
     * Returns true if this meets all the essential criteria.
254
     * That means it has attached an SkillDeclaration for each essential criterion,
255
     * with a level at least as high as the required level.
256
     * NOTE: If this application is in draft status, it will use
257
     *  SkillDeclarations from the the applicants profile for this check.
258
     *
259
     * @return boolean
1 ignored issue
show
introduced by
Method \App\Models\JobApplication::meetsEssentialCriteria() has useless @return annotation.
Loading history...
260
     */
261
    public function meetsEssentialCriteria(): bool
262
    {
263
        $essentialCriteria = $this->job_poster->criteria->filter(
264
            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

264
            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...
265
                return $value->criteria_type->name == 'essential';
266
            }
267
        );
268
        $source = $this->isDraft() ? $this->applicant : $this;
269
        foreach ($essentialCriteria as $criterion) {
270
            $skillDeclaration = $source->skill_declarations->where('skill_id', $criterion->skill_id)->first();
271
            if (
272
                $skillDeclaration === null ||
273
                $skillDeclaration->skill_level_id < $criterion->skill_level_id
274
            ) {
275
                return false;
276
            }
277
        }
278
        return true;
279
    }
280
281
    /**
282
     * Accessor for meetsEssentialCriteria function, which
283
     * allows this value to be automtacially appended to array/json representation.
284
     *
285
     * @return boolean
1 ignored issue
show
introduced by
Method \App\Models\JobApplication::getMeetsEssentialCriteriaAttribute() has useless @return annotation.
Loading history...
286
     */
287
    public function getMeetsEssentialCriteriaAttribute(): bool
288
    {
289
        return $this->meetsEssentialCriteria();
290
    }
291
292
    /**
293
     * Save copies of all relevant profile data to this application.
294
     *
295
     *
296
     * @return void
1 ignored issue
show
introduced by
Method \App\Models\JobApplication::saveProfileSnapshot() has useless @return annotation.
Loading history...
297
     */
298
    public function saveProfileSnapshot(): void
299
    {
300
        $applicant = $this->applicant->fresh();
301
302
        $this->user_name = $applicant->user->full_name;
303
        $this->user_email = $applicant->user->email;
304
        $this->save();
305
306
        // Delete previous snapshot.
307
        $this->degrees()->delete();
308
        $this->courses()->delete();
309
        $this->work_experiences()->delete();
310
        $this->projects()->delete();
311
        $this->references()->delete();
312
        $this->work_samples()->delete();
313
        $this->skill_declarations()->delete();
314
315
        $this->degrees()->saveMany($applicant->degrees->map->replicate());
316
        $this->courses()->saveMany($applicant->courses->map->replicate());
317
        $this->work_experiences()->saveMany($applicant->work_experiences->map->replicate());
318
319
        $copyWithHistory = function ($model) {
320
            return [
321
                'old' => $model,
322
                'new' => $model->replicate()
323
            ];
324
        };
325
326
327
        $projectMap = $applicant->projects->map($copyWithHistory);
328
        $referenceMap = $applicant->references->map($copyWithHistory);
329
        $workSampleMap = $applicant->work_samples->map($copyWithHistory);
330
        $skillDeclarationMap = $applicant->skill_declarations->map($copyWithHistory);
331
332
        // First link new projects, references, work samples and skill declarations to this application.
333
        $this->projects()->saveMany($projectMap->pluck('new'));
334
        $this->references()->saveMany($referenceMap->pluck('new'));
335
        $this->work_samples()->saveMany($workSampleMap->pluck('new'));
336
        $this->skill_declarations()->saveMany($skillDeclarationMap->pluck('new'));
337
338
        $findNewFromOld = function ($mapping, $old) {
339
            $matchingItem = $mapping->first(function ($value) use ($old) {
340
                return $value['old']->id === $old->id;
341
            });
342
            return $matchingItem['new'];
343
        };
344
345
        // Replicate copies shallow attributes, but not relationships. We have to copy those ourselves.
346
        $findNewReferenceFromOld = function ($old) use ($findNewFromOld, $referenceMap) {
347
            return $findNewFromOld($referenceMap, $old);
348
        };
349
350
        $findNewSkillDeclarationFromOld = function ($old) use ($findNewFromOld, $skillDeclarationMap) {
351
            return $findNewFromOld($skillDeclarationMap, $old);
352
        };
353
354
        // Link projects and references.
355
        foreach ($projectMap as $item) {
356
            $old = $item['old'];
357
            $newProj = $item['new'];
358
            $newReferences = $old->references->map($findNewReferenceFromOld);
359
            $newProj->references()->sync($newReferences);
360
        }
361
362
        // Link references and skills.
363
        foreach ($referenceMap as $item) {
364
            $old = $item['old'];
365
            $newRef = $item['new'];
366
            $newSkillDecs = $old->skill_declarations->map($findNewSkillDeclarationFromOld);
367
            $newRef->skill_declarations()->sync($newSkillDecs);
368
        }
369
370
        // Link work samples and skills.
371
        foreach ($workSampleMap as $item) {
372
            $old = $item['old'];
373
            $newSample = $item['new'];
374
            $newSkillDecs = $old->skill_declarations->map($findNewSkillDeclarationFromOld);
375
            $newSample->skill_declarations()->sync($newSkillDecs);
376
        }
377
    }
378
}
379