Passed
Push — task/comment-model ( 01686c )
by Yonathan
13:23
created

JobPoster::comments()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

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