Passed
Push — task/common-translation-packag... ( 398328...094bed )
by Grant
07:16
created

JobPoster::province()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 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 Spatie\Translatable\HasTranslations;
11
use Backpack\CRUD\app\Models\Traits\CrudTrait;
12
use Jenssegers\Date\Date;
13
use Illuminate\Notifications\Notifiable;
14
use Illuminate\Support\Facades\App;
15
use Illuminate\Support\Facades\Lang;
16
17
use App\Events\JobSaved;
18
19
/**
20
 * Class JobPoster
21
 *
22
 * @property int $id
23
 * @property int $job_term_id
24
 * @property string $chosen_lang
25
 * @property int $term_qty
26
 * @property \Jenssegers\Date\Date $open_date_time
27
 * @property \Jenssegers\Date\Date $close_date_time
28
 * @property \Jenssegers\Date\Date $start_date_time
29
 * @property \Jenssegers\Date\Date $review_requested_at
30
 * @property \Jenssegers\Date\Date $published_at
31
 * @property int $department_id
32
 * @property int $province_id
33
 * @property int $salary_min
34
 * @property int $salary_max
35
 * @property int $noc
36
 * @property int $classification_id
37
 * @property int $classification_level
38
 * @property int $security_clearance_id
39
 * @property int $language_requirement_id
40
 * @property boolean $remote_work_allowed
41
 * @property int $manager_id
42
 * @property boolean $published
43
 * @property int $team_size
44
 * @property array $work_env_features This should be an array of boolean flags for features, ie json of shape {[feature: string]: boolean}
45
 * @property int $fast_vs_steady
46
 * @property int $horizontal_vs_vertical
47
 * @property int $experimental_vs_ongoing
48
 * @property int $citizen_facing_vs_back_office
49
 * @property int $collaborative_vs_independent
50
 * @property int $telework_allowed_frequency_id
51
 * @property int $flexible_hours_frequency_id
52
 * @property int $travel_requirement_id
53
 * @property int $overtime_requirement_id
54
 * @property int $process_number
55
 * @property int $priority_clearance_number
56
 * @property \Jenssegers\Date\Date $loo_issuance_date
57
 * @property \Jenssegers\Date\Date $created_at
58
 * @property \Jenssegers\Date\Date $updated_at
59
 *
60
 * @property int $submitted_applications_count
61
 *
62
 * @property \App\Models\Lookup\Department $department
63
 * @property \App\Models\Lookup\JobTerm $job_term
64
 * @property \App\Models\Lookup\LanguageRequirement $language_requirement
65
 * @property \App\Models\Manager $manager
66
 * @property \App\Models\Lookup\Province $province
67
 * @property \App\Models\Lookup\SecurityClearance $security_clearance
68
 * @property \Illuminate\Database\Eloquent\Collection $criteria
69
 * @property \Illuminate\Database\Eloquent\Collection $job_applications
70
 * @property \Illuminate\Database\Eloquent\Collection $job_poster_key_tasks
71
 * @property \Illuminate\Database\Eloquent\Collection $job_poster_questions
72
 * @property \Illuminate\Database\Eloquent\Collection $job_poster_translations
73
 * @property \Illuminate\Database\Eloquent\Collection $submitted_applications
74
 * @property \App\Models\Lookup\Frequency $telework_allowed_frequency
75
 * @property \App\Models\Lookup\Frequency $flexible_hours_frequency
76
 *
77
 * Localized Properties:
78
 * @property string $city
79
 * @property string $title
80
 * @property string $dept_impact
81
 * @property string $team_impact
82
 * @property string $hire_impact
83
 * @property string $division
84
 * @property string $education
85
 * @property string $work_env_description
86
 * @property string $culture_summary
87
 * @property string $culture_special
88
 *
89
 * Methods
90
 * @method boolean isOpen()
91
 * @method string timeRemaining()
92
 * @method mixed[] toApiArray()
93
 *
94
 * Computed Properties
95
 * @property string|null $classification_code
96
 * @property string|null $classification_message
97
 */
98
class JobPoster extends BaseModel
99
{
100
    use CrudTrait;
0 ignored issues
show
introduced by
The trait Backpack\CRUD\app\Models\Traits\CrudTrait requires some properties which are not provided by App\Models\JobPoster: $Type, $fakeColumns
Loading history...
101
    use HasTranslations;
102
    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\JobPoster.
Loading history...
103
104
    const DATE_FORMAT = [
105
        'en' => 'M jS, Y',
106
        'fr' => 'd M Y',
107
    ];
108
    const TIME_FORMAT = [
109
        'en' => 'h:i A T',
110
        'fr' => 'H \h i T',
111
    ];
112
    const TIMEZONE = 'America/Toronto';
113
114
    /**
115
     * @var string[] $translatedAttributes
116
     */
117
    public $translatable = [
118
        'city',
119
        'title',
120
        'dept_impact',
121
        'team_impact',
122
        'hire_impact',
123
        'division',
124
        'education',
125
        'work_env_description',
126
        'culture_summary',
127
        'culture_special',
128
    ];
129
130
    /**
131
     * @var string[] $casts
132
     */
133
    protected $casts = [
134
        'job_term_id' => 'int',
135
        'department_id' => 'int',
136
        'province_id' => 'int',
137
        'salary_min' => 'int',
138
        'salary_max' => 'int',
139
        'noc' => 'int',
140
        'classification_id' => 'int',
141
        'classification_level' => 'int',
142
        'security_clearance_id' => 'int',
143
        'language_requirement_id' => 'int',
144
        'remote_work_allowed' => 'boolean',
145
        'manager_id' => 'int',
146
        'published' => 'boolean',
147
        'team_size' => 'int',
148
        'work_env_features' => 'array',
149
        'fast_vs_steady' => 'int',
150
        'horizontal_vs_vertical' => 'int',
151
        'experimental_vs_ongoing' => 'int',
152
        'citizen_facing_vs_back_office' => 'int',
153
        'collaborative_vs_independent' => 'int',
154
        'telework_allowed_frequency_id' => 'int',
155
        'flexible_hours_frequency_id' => 'int',
156
        'travel_requirement_id' => 'int',
157
        'overtime_requirement_id' => 'int',
158
    ];
159
160
    /**
161
     * @var string[] $dates
162
     */
163
    protected $dates = [
164
        'open_date_time',
165
        'close_date_time',
166
        'start_date_time',
167
        'review_requested_at',
168
        'published_at',
169
        'loo_issuance_date',
170
    ];
171
172
    /**
173
     * @var string[] $fillable
174
     */
175
    protected $fillable = [
176
        'job_term_id',
177
        'chosen_lang',
178
        'term_qty',
179
        'open_date_time',
180
        'close_date_time',
181
        'start_date_time',
182
        'department_id',
183
        'province_id',
184
        'salary_min',
185
        'salary_max',
186
        'noc',
187
        'security_clearance_id',
188
        'language_requirement_id',
189
        'remote_work_allowed',
190
        'published',
191
        'team_size',
192
        'work_env_features',
193
        'fast_vs_steady',
194
        'horizontal_vs_vertical',
195
        'experimental_vs_ongoing',
196
        'citizen_facing_vs_back_office',
197
        'collaborative_vs_independent',
198
        'telework_allowed_frequency_id',
199
        'flexible_hours_frequency_id',
200
        'travel_requirement_id',
201
        'overtime_requirement_id',
202
        'process_number',
203
        'priority_clearance_number',
204
        'loo_issuance_date',
205
        'classification_id',
206
        'classification_level',
207
    ];
208
209
    /**
210
     * The attributes that should be visible in arrays.
211
     * In this case, it blocks loaded relations from appearing.
212
     *
213
     * @var array
214
     */
215
    protected $visible = [
216
        'id',
217
        'manager_id',
218
        'chosen_lang',
219
        'term_qty',
220
        'open_date_time',
221
        'close_date_time',
222
        'start_date_time',
223
        'department_id',
224
        'province_id',
225
        'salary_min',
226
        'salary_max',
227
        'noc',
228
        'security_clearance_id',
229
        'language_requirement_id',
230
        'remote_work_allowed',
231
        'published_at',
232
        'published',
233
        'review_requested_at',
234
        'team_size',
235
        'work_env_features',
236
        'fast_vs_steady',
237
        'horizontal_vs_vertical',
238
        'experimental_vs_ongoing',
239
        'citizen_facing_vs_back_office',
240
        'collaborative_vs_independent',
241
        'telework_allowed_frequency_id',
242
        'flexible_hours_frequency_id',
243
        'travel_requirement_id',
244
        'overtime_requirement_id',
245
        'process_number',
246
        'priority_clearance_number',
247
        'loo_issuance_date',
248
        'classification_id',
249
        'classification_level'
250
    ];
251
252
    /**
253
     * The accessors to append to the model's array form.
254
     *
255
     * @var string[] $appends
256
     */
257
    protected $appends = [
258
        'classification_code',
259
        'classification_message'
260
    ];
261
262
    /**
263
     * @var mixed[] $dispatchesEvents
264
     */
265
    protected $dispatchesEvents = [
266
        'saved' => JobSaved::class,
267
    ];
268
269
    // @codeCoverageIgnoreStart
270
    public function department() // phpcs:ignore
271
    {
272
        return $this->belongsTo(\App\Models\Lookup\Department::class);
273
    }
274
275
    public function job_term() // phpcs:ignore
276
    {
277
        return $this->belongsTo(\App\Models\Lookup\JobTerm::class);
278
    }
279
280
    public function language_requirement() // phpcs:ignore
281
    {
282
        return $this->belongsTo(\App\Models\Lookup\LanguageRequirement::class);
283
    }
284
285
    public function manager() // phpcs:ignore
286
    {
287
        return $this->belongsTo(\App\Models\Manager::class);
288
    }
289
290
    public function province() // phpcs:ignore
291
    {
292
        return $this->belongsTo(\App\Models\Lookup\Province::class);
293
    }
294
295
    public function security_clearance() // phpcs:ignore
296
    {
297
        return $this->belongsTo(\App\Models\Lookup\SecurityClearance::class);
298
    }
299
300
    public function criteria() // phpcs:ignore
301
    {
302
        return $this->hasMany(\App\Models\Criteria::class);
303
    }
304
305
    public function job_applications() // phpcs:ignore
306
    {
307
        return $this->hasMany(\App\Models\JobApplication::class);
308
    }
309
310
    public function job_poster_key_tasks() // phpcs:ignore
311
    {
312
        return $this->hasMany(\App\Models\JobPosterKeyTask::class);
313
    }
314
315
    public function job_poster_questions() // phpcs:ignore
316
    {
317
        return $this->hasMany(\App\Models\JobPosterQuestion::class);
318
    }
319
320
    public function telework_allowed_frequency() // phpcs:ignore
321
    {
322
        return $this->belongsTo(\App\Models\Lookup\Frequency::class);
323
    }
324
325
    public function flexible_hours_frequency() // phpcs:ignore
326
    {
327
        return $this->belongsTo(\App\Models\Lookup\Frequency::class);
328
    }
329
330
    public function travel_requirement() // phpcs:ignore
331
    {
332
        return $this->belongsTo(\App\Models\Lookup\TravelRequirement::class);
333
    }
334
335
    public function overtime_requirement() // phpcs:ignore
336
    {
337
        return $this->belongsTo(\App\Models\Lookup\OvertimeRequirement::class);
338
    }
339
340
    public function classification() // phpcs:ignore
341
    {
342
        return $this->belongsTo(\App\Models\Classification::class);
343
    }
344
345
    // @codeCoverageIgnoreEnd
346
    /* Artificial Relations */
347
348
    /**
349
     * Get all of the Job Applications submitted to this
350
     * Job Poster.
351
     *
352
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
353
     */
354
    public function submitted_applications() // phpcs:ignore
355
    {
356
        return $this->hasMany(\App\Models\JobApplication::class)->whereDoesntHave('application_status', function ($query): void {
357
            $query->where('name', 'draft');
358
        });
359
    }
360
361
    /* Overrides */
362
363
    /**
364
     * Retrieve the model for a bound value.
365
     * Seems to be a useful workaround for providing submitted_applications_count
366
     * to any bound routes that receive a jobPoster instance without using the
367
     * withCount property on the model itself.
368
     * See https://github.com/laravel/framework/issues/23957 for more info.
369
     *
370
     * @param mixed $value Value used to retrieve the model instance.
371
     *
372
     * @return \Illuminate\Database\Eloquent\Model|null
373
     */
374
    public function resolveRouteBinding($value) // phpcs:ignore
375
    {
376
        return $this->withCount('submitted_applications')->where('id', $value)->first() ?? abort(404);
377
    }
378
379
    /**
380
     * Intercept setting the "published" attribute, and set the
381
     * "published_at" timestamp if true.
382
     *
383
     * @param mixed $value Incoming value for the 'published' attribute.
384
     *
385
     * @return void
1 ignored issue
show
introduced by
Method \App\Models\JobPoster::setPublishedAttribute() has useless @return annotation.
Loading history...
386
     */
387
    public function setPublishedAttribute($value): void
388
    {
389
        if ($value) {
390
            $this->attributes['published_at'] = new Date();
391
        } else {
392
            $this->attributes['published_at'] = null;
393
        }
394
        if ($value === null) {
395
            $value = false;
396
        }
397
        $this->attributes['published'] = $value;
398
    }
399
400
    /* Methods */
401
402
    public function submitted_applications_count() //phpcs:ignore
403
    {
404
        return $this->submitted_applications()->count();
405
    }
406
407
    /**
408
     * Formatted and localized date and time the Job Poster closes.
409
     *
410
     * @return string[]
411
     */
412
    public function applyBy(): array
413
    {
414
        $localCloseDate = new Date($this->close_date_time); // This initializes the date object in UTC time.
415
        $localCloseDate->setTimezone(new \DateTimeZone(self::TIMEZONE)); // Then set the time zone for display.
416
        $displayDate = [
417
            'date' => $localCloseDate->format(self::DATE_FORMAT[App::getLocale()]),
418
            'time' => $localCloseDate->format(self::TIME_FORMAT[App::getLocale()])
419
        ];
420
421
        if (App::isLocale('fr')) {
422
            $displayDate['time'] = str_replace(['EST', 'EDT'], ['HNE', 'HAE'], $displayDate['time']);
423
        }
424
425
        return $displayDate;
426
    }
427
428
    /**
429
     * Return whether the Job is Open or Closed.
430
     * Used by the Admin Portal JobPosterCrudController.
431
     *
432
     * @return string
1 ignored issue
show
introduced by
Method \App\Models\JobPoster::displayStatus() has useless @return annotation.
Loading history...
433
     */
434
    public function displayStatus(): string
435
    {
436
        return $this->isOpen() ? 'Open' : 'Closed';
437
    }
438
439
    /**
440
     * Check if a Job Poster is open for applications.
441
     *
442
     * @return boolean
1 ignored issue
show
introduced by
Method \App\Models\JobPoster::isOpen() has useless @return annotation.
Loading history...
443
     */
444
    public function isOpen(): bool
445
    {
446
        return $this->published
447
            && $this->open_date_time !== null
448
            && $this->close_date_time !== null
449
            && $this->open_date_time->isPast()
450
            && $this->close_date_time->isFuture();
451
    }
452
453
    /**
454
     * Check if a Job Poster is closed for applications.
455
     *
456
     * @return boolean
1 ignored issue
show
introduced by
Method \App\Models\JobPoster::isClosed() has useless @return annotation.
Loading history...
457
     */
458
    public function isClosed(): bool
459
    {
460
        return $this->published
461
            && $this->open_date_time !== null
462
            && $this->close_date_time !== null
463
            && $this->open_date_time->isPast()
464
            && $this->close_date_time->isPast();
465
    }
466
467
    /**
468
     * Calculate the remaining time a Job Poster is open.
469
     *
470
     * @return string
1 ignored issue
show
introduced by
Method \App\Models\JobPoster::timeRemaining() has useless @return annotation.
Loading history...
471
     */
472
    public function timeRemaining(): string
473
    {
474
        $interval = $this->close_date_time->diff(Date::now());
475
476
        $d = $interval->d;
477
        $h = $interval->h;
478
        $m = $interval->i;
479
        $s = $interval->s;
480
481
        if ($d > 0) {
482
            $unit = 'day';
483
            $count = $d;
484
        } elseif ($h > 0) {
485
            $unit = 'hour';
486
            $count = $h;
487
        } elseif ($m > 0) {
488
            $unit = 'minute';
489
            $count = $m;
490
        } else {
491
            $unit = 'second';
492
            $count = $s;
493
        }
494
495
        $key = "common/time.$unit";
496
497
        return Lang::choice($key, $count);
498
    }
499
500
    /**
501
     * Return the current status for the Job Poster.
502
     * Possible values are "draft", "submitted", "published" and "closed".
503
     *
504
     * @return string
1 ignored issue
show
introduced by
Method \App\Models\JobPoster::status() has useless @return annotation.
Loading history...
505
     */
506
    public function status(): string
507
    {
508
        $status = 'draft';
509
        if ($this->isOpen()) {
510
            $status = 'published';
511
        } elseif ($this->isClosed()) {
512
            $status = 'closed';
513
        } elseif ($this->review_requested_at !== null) {
514
            $status = 'submitted';
515
        } else {
516
            $status = 'draft';
517
        }
518
519
        return $status;
520
    }
521
522
    /**
523
     * The database model stores a foreign id to the classification table,
524
     * but to simplify the API, this model simply returns the key as classification_code.
525
     *
526
     * @return string|null
527
     */
528
    public function getClassificationCodeAttribute()
0 ignored issues
show
introduced by
Method \App\Models\JobPoster::getClassificationCodeAttribute() does not have native return type hint for its return value but it should be possible to add it based on @return annotation "string|null".
Loading history...
529
    {
530
        if ($this->classification_id !== null) {
531
            return $this->classification->key;
532
        }
533
        return null;
534
    }
535
536
    /**
537
     *
538
     * Get the full government classification message.
539
     *
540
     * @return string|null
541
     */
542
    public function getClassificationMessageAttribute()
0 ignored issues
show
introduced by
Method \App\Models\JobPoster::getClassificationMessageAttribute() does not have native return type hint for its return value but it should be possible to add it based on @return annotation "string|null".
Loading history...
543
    {
544
        if ($this->classification_id !== null && $this->classification_level !== null) {
545
            return $this->classification->key . '-0' . $this->classification_level;
546
        }
547
        return null;
548
    }
549
550
    /**
551
     * Return the array of values used to represent this object in an api response.
552
     * This array should contain no nested objects (besides translations).
553
     *
554
     * @return mixed[]
555
     */
556
    public function toApiArray(): array
557
    {
558
        $jobWithTranslations = array_merge($this->toArray(), $this->getTranslationsArray());
0 ignored issues
show
Bug introduced by
It seems like $this->getTranslationsArray() can also be of type Illuminate\Database\Eloquent\Builder; however, parameter $array2 of array_merge() does only seem to accept array|null, 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

558
        $jobWithTranslations = array_merge($this->toArray(), /** @scrutinizer ignore-type */ $this->getTranslationsArray());
Loading history...
559
        return $jobWithTranslations;
560
    }
561
}
562