|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
namespace Coyote; |
|
4
|
|
|
|
|
5
|
|
|
use Carbon\Carbon; |
|
6
|
|
|
use Coyote\Job\Location; |
|
7
|
|
|
use Coyote\Models\Scopes\ForUser; |
|
8
|
|
|
use Coyote\Services\Elasticsearch\CharFilters\JobFilter; |
|
9
|
|
|
use Illuminate\Database\Eloquent\Model; |
|
10
|
|
|
use Illuminate\Database\Eloquent\SoftDeletes; |
|
11
|
|
|
use Illuminate\Queue\SerializesModels; |
|
12
|
|
|
|
|
13
|
|
|
/** |
|
14
|
|
|
* @property int $id |
|
15
|
|
|
* @property int $user_id |
|
16
|
|
|
* @property int $firm_id |
|
17
|
|
|
* @property \Carbon\Carbon $created_at |
|
18
|
|
|
* @property \Carbon\Carbon $updated_at |
|
19
|
|
|
* @property \Carbon\Carbon $deleted_at |
|
20
|
|
|
* @property \Carbon\Carbon $deadline_at |
|
21
|
|
|
* @property int $deadline |
|
22
|
|
|
* @property int $salary_from |
|
23
|
|
|
* @property int $salary_to |
|
24
|
|
|
* @property int $country_id |
|
25
|
|
|
* @property int $currency_id |
|
26
|
|
|
* @property int $is_remote |
|
27
|
|
|
* @property int $enable_apply |
|
28
|
|
|
* @property int $visits |
|
29
|
|
|
* @property int $rate_id |
|
30
|
|
|
* @property int $employment_id |
|
31
|
|
|
* @property int $views |
|
32
|
|
|
* @property float $score |
|
33
|
|
|
* @property float $rank |
|
34
|
|
|
* @property string $slug |
|
35
|
|
|
* @property string $title |
|
36
|
|
|
* @property string $description |
|
37
|
|
|
* @property string $recruitment |
|
38
|
|
|
* @property string $requirements |
|
39
|
|
|
* @property string $email |
|
40
|
|
|
* @property User $user |
|
41
|
|
|
* @property Firm $firm |
|
42
|
|
|
* @property Tag[] $tags |
|
43
|
|
|
* @property Location[] $locations |
|
44
|
|
|
*/ |
|
45
|
|
|
class Job extends Model |
|
46
|
|
|
{ |
|
47
|
|
|
use SoftDeletes, ForUser; |
|
48
|
|
|
use Searchable { |
|
49
|
|
|
getIndexBody as parentGetIndexBody; |
|
50
|
|
|
} |
|
51
|
|
|
|
|
52
|
|
|
const MONTH = 1; |
|
53
|
|
|
const YEAR = 2; |
|
54
|
|
|
const WEEK = 3; |
|
55
|
|
|
const HOUR = 4; |
|
56
|
|
|
|
|
57
|
|
|
/** |
|
58
|
|
|
* Filling each field adds points to job offer score. |
|
59
|
|
|
*/ |
|
60
|
|
|
const SCORE_CONFIG = [ |
|
61
|
|
|
'job' => ['description' => 10, 'salary_from' => 25, 'salary_to' => 25, 'city' => 15], |
|
62
|
|
|
'firm' => ['name' => 15, 'logo' => 5, 'website' => 1, 'description' => 5] |
|
63
|
|
|
]; |
|
64
|
|
|
|
|
65
|
|
|
/** |
|
66
|
|
|
* The attributes that are mass assignable. |
|
67
|
|
|
* |
|
68
|
|
|
* @var array |
|
69
|
|
|
*/ |
|
70
|
|
|
protected $fillable = [ |
|
71
|
|
|
'title', |
|
72
|
|
|
'description', |
|
73
|
|
|
'requirements', |
|
74
|
|
|
'recruitment', |
|
75
|
|
|
'is_remote', |
|
76
|
|
|
'remote_range', |
|
77
|
|
|
'country_id', |
|
78
|
|
|
'salary_from', |
|
79
|
|
|
'salary_to', |
|
80
|
|
|
'currency_id', |
|
81
|
|
|
'rate_id', |
|
82
|
|
|
'employment_id', |
|
83
|
|
|
'deadline_at', |
|
84
|
|
|
'email', |
|
85
|
|
|
'enable_apply' |
|
86
|
|
|
]; |
|
87
|
|
|
|
|
88
|
|
|
/** |
|
89
|
|
|
* Default fields values. |
|
90
|
|
|
* |
|
91
|
|
|
* @var array |
|
92
|
|
|
*/ |
|
93
|
|
|
protected $attributes = [ |
|
94
|
|
|
'enable_apply' => true, |
|
95
|
|
|
'is_remote' => false, |
|
96
|
|
|
'title' => '' |
|
97
|
|
|
]; |
|
98
|
|
|
|
|
99
|
|
|
/** |
|
100
|
|
|
* Cast to when calling toArray() (for example before index in elasticsearch). |
|
101
|
|
|
* |
|
102
|
|
|
* @var array |
|
103
|
|
|
*/ |
|
104
|
|
|
protected $casts = ['is_remote' => 'boolean', 'enable_apply' => 'boolean']; |
|
105
|
|
|
|
|
106
|
|
|
/** |
|
107
|
|
|
* @var string |
|
108
|
|
|
*/ |
|
109
|
|
|
protected $dateFormat = 'Y-m-d H:i:se'; |
|
110
|
|
|
|
|
111
|
|
|
/** |
|
112
|
|
|
* @var array |
|
113
|
|
|
*/ |
|
114
|
|
|
protected $appends = ['deadline']; |
|
115
|
|
|
|
|
116
|
|
|
/** |
|
117
|
|
|
* Elasticsearch type mapping |
|
118
|
|
|
* |
|
119
|
|
|
* @var array |
|
120
|
|
|
*/ |
|
121
|
|
|
protected $mapping = [ |
|
122
|
|
|
"id" => [ |
|
123
|
|
|
"type" => "long" |
|
124
|
|
|
], |
|
125
|
|
|
"locations" => [ |
|
126
|
|
|
"type" => "nested", |
|
127
|
|
|
"properties" => [ |
|
128
|
|
|
"city" => [ |
|
129
|
|
|
"type" => "string", |
|
130
|
|
|
"analyzer" => "keyword_asciifolding_analyzer", |
|
131
|
|
|
"fields" => [ |
|
132
|
|
|
"original" => ["type" => "text", "analyzer" => "keyword_analyzer", "fielddata" => true] |
|
133
|
|
|
] |
|
134
|
|
|
], |
|
135
|
|
|
"coordinates" => [ |
|
136
|
|
|
"type" => "geo_point" |
|
137
|
|
|
] |
|
138
|
|
|
] |
|
139
|
|
|
], |
|
140
|
|
|
"title" => [ |
|
141
|
|
|
"type" => "text", |
|
142
|
|
|
"analyzer" => "default_analyzer" |
|
143
|
|
|
], |
|
144
|
|
|
"description" => [ |
|
145
|
|
|
"type" => "text", |
|
146
|
|
|
"analyzer" => "default_analyzer" |
|
147
|
|
|
], |
|
148
|
|
|
"requirements" => [ |
|
149
|
|
|
"type" => "text", |
|
150
|
|
|
"analyzer" => "default_analyzer" |
|
151
|
|
|
], |
|
152
|
|
|
"is_remote" => [ |
|
153
|
|
|
"type" => "boolean" |
|
154
|
|
|
], |
|
155
|
|
|
"remote_range" => [ |
|
156
|
|
|
"type" => "integer" |
|
157
|
|
|
], |
|
158
|
|
|
"tags" => [ |
|
159
|
|
|
"type" => "text", |
|
160
|
|
|
"fields" => [ |
|
161
|
|
|
"original" => ["type" => "keyword"] |
|
162
|
|
|
] |
|
163
|
|
|
], |
|
164
|
|
|
"firm" => [ |
|
165
|
|
|
"type" => "object", |
|
166
|
|
|
"properties" => [ |
|
167
|
|
|
"name" => [ |
|
168
|
|
|
"type" => "text", |
|
169
|
|
|
"analyzer" => "default_analyzer", |
|
170
|
|
|
"fields" => [ |
|
171
|
|
|
// filtrujemy firmy po tym polu |
|
172
|
|
|
"original" => ["type" => "text", "analyzer" => "keyword_analyzer", "fielddata" => true] |
|
173
|
|
|
] |
|
174
|
|
|
] |
|
175
|
|
|
] |
|
176
|
|
|
], |
|
177
|
|
|
"created_at" => [ |
|
178
|
|
|
"type" => "date", |
|
179
|
|
|
"format" => "yyyy-MM-dd HH:mm:ss" |
|
180
|
|
|
], |
|
181
|
|
|
"updated_at" => [ |
|
182
|
|
|
"type" => "date", |
|
183
|
|
|
"format" => "yyyy-MM-dd HH:mm:ss" |
|
184
|
|
|
], |
|
185
|
|
|
"deadline_at" => [ |
|
186
|
|
|
"type" => "date", |
|
187
|
|
|
"format" => "yyyy-MM-dd HH:mm:ss" |
|
188
|
|
|
], |
|
189
|
|
|
"salary" => [ |
|
190
|
|
|
"type" => "float" |
|
191
|
|
|
], |
|
192
|
|
|
"score" => [ |
|
193
|
|
|
"type" => "long" |
|
194
|
|
|
], |
|
195
|
|
|
"rank" => [ |
|
196
|
|
|
"type" => "float" |
|
197
|
|
|
] |
|
198
|
|
|
]; |
|
199
|
|
|
|
|
200
|
|
|
/** |
|
201
|
|
|
* We need to set firm id to null offer is private |
|
202
|
|
|
*/ |
|
203
|
|
|
public static function boot() |
|
204
|
|
|
{ |
|
205
|
|
|
parent::boot(); |
|
206
|
|
|
|
|
207
|
|
|
static::saving(function (Job $model) { |
|
208
|
|
|
// nullable column |
|
209
|
|
|
foreach (['firm_id', 'salary_from', 'salary_to', 'remote_range'] as $column) { |
|
210
|
|
|
if (empty($model->{$column})) { |
|
211
|
|
|
$model->{$column} = null; |
|
212
|
|
|
} |
|
213
|
|
|
} |
|
214
|
|
|
|
|
215
|
|
|
$model->score = $model->getScore(); |
|
216
|
|
|
$timestamp = $model->created_at ? strtotime($model->created_at) : time(); |
|
217
|
|
|
|
|
218
|
|
|
$seconds = ($timestamp - 1380585600) / 35000; |
|
219
|
|
|
$model->rank = number_format($model->score + $seconds, 6, '.', ''); |
|
220
|
|
|
|
|
221
|
|
|
// field must not be null |
|
222
|
|
|
$model->is_remote = (int) $model->is_remote; |
|
223
|
|
|
}); |
|
224
|
|
|
} |
|
225
|
|
|
|
|
226
|
|
|
/** |
|
227
|
|
|
* @return string[] |
|
228
|
|
|
*/ |
|
229
|
|
|
public static function getRatesList() |
|
230
|
|
|
{ |
|
231
|
|
|
return [self::MONTH => 'miesięcznie', self::YEAR => 'rocznie', self::WEEK => 'tygodniowo', self::HOUR => 'godzinowo']; |
|
232
|
|
|
} |
|
233
|
|
|
|
|
234
|
|
|
/** |
|
235
|
|
|
* @return string[] |
|
236
|
|
|
*/ |
|
237
|
|
|
public static function getEmploymentList() |
|
238
|
|
|
{ |
|
239
|
|
|
return [1 => 'Umowa o pracę', 2 => 'Umowa zlecenie', 3 => 'Umowa o dzieło', 4 => 'Kontrakt']; |
|
240
|
|
|
} |
|
241
|
|
|
|
|
242
|
|
|
/** |
|
243
|
|
|
* @return array |
|
244
|
|
|
*/ |
|
245
|
|
|
public static function getRemoteRangeList() |
|
246
|
|
|
{ |
|
247
|
|
|
$list = []; |
|
248
|
|
|
|
|
249
|
|
|
for ($i = 100; $i >= 0; $i -= 10) { |
|
250
|
|
|
$list[$i] = "$i%"; |
|
251
|
|
|
} |
|
252
|
|
|
|
|
253
|
|
|
return $list; |
|
254
|
|
|
} |
|
255
|
|
|
|
|
256
|
|
|
/** |
|
257
|
|
|
* @return int |
|
258
|
|
|
*/ |
|
259
|
|
|
public function getScore() |
|
260
|
|
|
{ |
|
261
|
|
|
$score = 0; |
|
262
|
|
|
|
|
263
|
|
View Code Duplication |
foreach (self::SCORE_CONFIG['job'] as $column => $point) { |
|
|
|
|
|
|
264
|
|
|
if (!empty($this->{$column})) { |
|
265
|
|
|
$score += $point; |
|
266
|
|
|
} |
|
267
|
|
|
} |
|
268
|
|
|
|
|
269
|
|
|
// 30 points maximum... |
|
270
|
|
|
$score += min(30, (count($this->tags()->get()) * 10)); |
|
271
|
|
|
|
|
272
|
|
|
if ($this->firm_id) { |
|
273
|
|
|
$firm = $this->firm; |
|
274
|
|
|
|
|
275
|
|
View Code Duplication |
foreach (self::SCORE_CONFIG['firm'] as $column => $point) { |
|
|
|
|
|
|
276
|
|
|
if (!empty($firm->{$column})) { |
|
277
|
|
|
$score += $point; |
|
278
|
|
|
} |
|
279
|
|
|
} |
|
280
|
|
|
|
|
281
|
|
|
$score += min(25, $firm->benefits()->count() * 5); |
|
282
|
|
|
$score -= ($firm->is_agency * 15); |
|
283
|
|
|
} else { |
|
284
|
|
|
$score -= 15; |
|
285
|
|
|
} |
|
286
|
|
|
|
|
287
|
|
|
return max(0, $score); // score can't be negative |
|
288
|
|
|
} |
|
289
|
|
|
|
|
290
|
|
|
/** |
|
291
|
|
|
* Scope for currently active job offers |
|
292
|
|
|
* |
|
293
|
|
|
* @param \Illuminate\Database\Query\Builder $query |
|
294
|
|
|
* @return \Illuminate\Database\Query\Builder |
|
295
|
|
|
*/ |
|
296
|
|
|
public function scopePriorDeadline($query) |
|
297
|
|
|
{ |
|
298
|
|
|
return $query->where('deadline_at', '>', date('Y-m-d H:i:s')); |
|
299
|
|
|
} |
|
300
|
|
|
|
|
301
|
|
|
/** |
|
302
|
|
|
* @return \Illuminate\Database\Eloquent\Relations\HasMany |
|
303
|
|
|
*/ |
|
304
|
|
|
public function locations() |
|
305
|
|
|
{ |
|
306
|
|
|
return $this->hasMany('Coyote\Job\Location'); |
|
307
|
|
|
} |
|
308
|
|
|
|
|
309
|
|
|
/** |
|
310
|
|
|
* @return \Illuminate\Database\Eloquent\Relations\MorphOne |
|
311
|
|
|
*/ |
|
312
|
|
|
public function page() |
|
313
|
|
|
{ |
|
314
|
|
|
return $this->morphOne('Coyote\Page', 'content'); |
|
315
|
|
|
} |
|
316
|
|
|
|
|
317
|
|
|
/** |
|
318
|
|
|
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo |
|
319
|
|
|
*/ |
|
320
|
|
|
public function firm() |
|
321
|
|
|
{ |
|
322
|
|
|
return $this->belongsTo('Coyote\Firm'); |
|
323
|
|
|
} |
|
324
|
|
|
|
|
325
|
|
|
/** |
|
326
|
|
|
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo |
|
327
|
|
|
*/ |
|
328
|
|
|
public function currency() |
|
329
|
|
|
{ |
|
330
|
|
|
return $this->belongsTo('Coyote\Currency'); |
|
331
|
|
|
} |
|
332
|
|
|
|
|
333
|
|
|
/** |
|
334
|
|
|
* @return \Illuminate\Database\Eloquent\Relations\HasMany |
|
335
|
|
|
*/ |
|
336
|
|
|
public function referers() |
|
337
|
|
|
{ |
|
338
|
|
|
return $this->hasMany('Coyote\Job\Referer'); |
|
339
|
|
|
} |
|
340
|
|
|
|
|
341
|
|
|
/** |
|
342
|
|
|
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany |
|
343
|
|
|
*/ |
|
344
|
|
|
public function tags() |
|
345
|
|
|
{ |
|
346
|
|
|
return $this->belongsToMany('Coyote\Tag', 'job_tags')->orderBy('order')->withPivot(['priority', 'order']); |
|
347
|
|
|
} |
|
348
|
|
|
|
|
349
|
|
|
/** |
|
350
|
|
|
* @return \Illuminate\Database\Eloquent\Relations\HasMany |
|
351
|
|
|
*/ |
|
352
|
|
|
public function subscribers() |
|
353
|
|
|
{ |
|
354
|
|
|
return $this->hasMany('Coyote\Job\Subscriber'); |
|
355
|
|
|
} |
|
356
|
|
|
|
|
357
|
|
|
/** |
|
358
|
|
|
* @return \Illuminate\Database\Eloquent\Relations\HasMany |
|
359
|
|
|
*/ |
|
360
|
|
|
public function applications() |
|
361
|
|
|
{ |
|
362
|
|
|
return $this->hasMany('Coyote\Job\Application'); |
|
363
|
|
|
} |
|
364
|
|
|
|
|
365
|
|
|
/** |
|
366
|
|
|
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo |
|
367
|
|
|
*/ |
|
368
|
|
|
public function user() |
|
369
|
|
|
{ |
|
370
|
|
|
return $this->belongsTo('Coyote\User'); |
|
371
|
|
|
} |
|
372
|
|
|
|
|
373
|
|
|
/** |
|
374
|
|
|
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo |
|
375
|
|
|
*/ |
|
376
|
|
|
public function country() |
|
377
|
|
|
{ |
|
378
|
|
|
return $this->belongsTo('Coyote\Country'); |
|
379
|
|
|
} |
|
380
|
|
|
|
|
381
|
|
|
/** |
|
382
|
|
|
* @param string $title |
|
383
|
|
|
*/ |
|
384
|
|
|
public function setTitleAttribute($title) |
|
385
|
|
|
{ |
|
386
|
|
|
$title = trim($title); |
|
387
|
|
|
|
|
388
|
|
|
$this->attributes['title'] = $title; |
|
389
|
|
|
$this->attributes['slug'] = str_slug($title, '_'); |
|
390
|
|
|
} |
|
391
|
|
|
|
|
392
|
|
|
/** |
|
393
|
|
|
* @param string $value |
|
394
|
|
|
*/ |
|
395
|
|
|
public function setSalaryFromAttribute($value) |
|
396
|
|
|
{ |
|
397
|
|
|
$this->attributes['salary_from'] = $value === null ? null : (int) trim($value); |
|
398
|
|
|
} |
|
399
|
|
|
|
|
400
|
|
|
/** |
|
401
|
|
|
* @param string $value |
|
402
|
|
|
*/ |
|
403
|
|
|
public function setSalaryToAttribute($value) |
|
404
|
|
|
{ |
|
405
|
|
|
$this->attributes['salary_to'] = $value === null ? null : (int) trim($value); |
|
406
|
|
|
} |
|
407
|
|
|
|
|
408
|
|
|
/** |
|
409
|
|
|
* @param int $value |
|
410
|
|
|
*/ |
|
411
|
|
|
public function setDeadlineAttribute($value) |
|
412
|
|
|
{ |
|
413
|
|
|
$this->attributes['deadline_at'] = Carbon::now()->addDay($value); |
|
414
|
|
|
} |
|
415
|
|
|
|
|
416
|
|
|
/** |
|
417
|
|
|
* @return int |
|
418
|
|
|
*/ |
|
419
|
|
|
public function getDeadlineAttribute() |
|
420
|
|
|
{ |
|
421
|
|
|
return $this->deadline_at ? (new Carbon($this->deadline_at))->diff(Carbon::now())->days : 90; |
|
422
|
|
|
} |
|
423
|
|
|
|
|
424
|
|
|
/** |
|
425
|
|
|
* @return mixed |
|
426
|
|
|
*/ |
|
427
|
|
|
public function getCityAttribute() |
|
428
|
|
|
{ |
|
429
|
|
|
return $this->locations->implode('city', ', '); |
|
|
|
|
|
|
430
|
|
|
} |
|
431
|
|
|
|
|
432
|
|
|
/** |
|
433
|
|
|
* @param int $userId |
|
434
|
|
|
*/ |
|
435
|
|
|
public function setDefaultUserId($userId) |
|
436
|
|
|
{ |
|
437
|
|
|
if (empty($this->user_id)) { |
|
438
|
|
|
$this->user_id = $userId; |
|
439
|
|
|
} |
|
440
|
|
|
} |
|
441
|
|
|
|
|
442
|
|
|
/** |
|
443
|
|
|
* @param string $url |
|
444
|
|
|
*/ |
|
445
|
|
|
public function addReferer($url) |
|
446
|
|
|
{ |
|
447
|
|
|
if ($url && mb_strlen($url) < 200) { |
|
448
|
|
|
$referer = $this->referers()->firstOrNew(['url' => $url]); |
|
449
|
|
|
|
|
450
|
|
|
if (!$referer->id) { |
|
451
|
|
|
$referer->save(); |
|
452
|
|
|
} else { |
|
453
|
|
|
$referer->increment('count'); |
|
454
|
|
|
} |
|
455
|
|
|
} |
|
456
|
|
|
} |
|
457
|
|
|
|
|
458
|
|
|
/** |
|
459
|
|
|
* Check if user has applied for this job offer. |
|
460
|
|
|
* |
|
461
|
|
|
* @param int|null $userId |
|
462
|
|
|
* @param string $sessionId |
|
463
|
|
|
* @return boolean |
|
464
|
|
|
*/ |
|
465
|
|
|
public function hasApplied($userId, $sessionId) |
|
466
|
|
|
{ |
|
467
|
|
|
if ($userId) { |
|
|
|
|
|
|
468
|
|
|
return $this->applications()->forUser($userId)->exists(); |
|
469
|
|
|
} |
|
470
|
|
|
|
|
471
|
|
|
return $this->applications()->where('session_id', $sessionId)->exists(); |
|
472
|
|
|
} |
|
473
|
|
|
|
|
474
|
|
|
/** |
|
475
|
|
|
* @return array |
|
476
|
|
|
*/ |
|
477
|
|
|
protected function getIndexBody() |
|
478
|
|
|
{ |
|
479
|
|
|
$this->setCharFilter(JobFilter::class); |
|
480
|
|
|
$body = $this->parentGetIndexBody(); |
|
481
|
|
|
|
|
482
|
|
|
// maximum offered salary |
|
483
|
|
|
$salary = $this->monthlySalary(max($this->salary_from, $this->salary_to)); |
|
484
|
|
|
$body = array_except($body, ['deleted_at', 'enable_apply']); |
|
485
|
|
|
|
|
486
|
|
|
$locations = []; |
|
487
|
|
|
|
|
488
|
|
|
// We need to transform locations to format acceptable by elasticsearch. |
|
489
|
|
|
// I'm talking here about the coordinates |
|
490
|
|
|
/** @var \Coyote\Job\Location $location */ |
|
491
|
|
|
foreach ($this->locations()->get(['city', 'longitude', 'latitude']) as $location) { |
|
492
|
|
|
$nested = ['city' => $location->city]; |
|
493
|
|
|
|
|
494
|
|
|
if ($location->latitude && $location->longitude) { |
|
495
|
|
|
$nested['coordinates'] = [ |
|
496
|
|
|
'lat' => $location->latitude, |
|
497
|
|
|
'lon' => $location->longitude |
|
498
|
|
|
]; |
|
499
|
|
|
} |
|
500
|
|
|
|
|
501
|
|
|
$locations[] = $nested; |
|
502
|
|
|
} |
|
503
|
|
|
|
|
504
|
|
|
$body['score'] = intval($body['score']); |
|
505
|
|
|
|
|
506
|
|
|
$body = array_merge($body, [ |
|
507
|
|
|
'locations' => $locations, |
|
508
|
|
|
'salary' => $salary, |
|
509
|
|
|
'salary_from' => $this->monthlySalary($this->salary_from), |
|
510
|
|
|
'salary_to' => $this->monthlySalary($this->salary_to), |
|
511
|
|
|
// yes, we index currency name so we don't have to look it up in database during search process |
|
512
|
|
|
'currency_name' => $this->currency()->value('name'), |
|
513
|
|
|
// higher tag's priorities first |
|
514
|
|
|
'tags' => $this->tags()->get(['name', 'priority'])->sortByDesc('pivot.priority')->pluck('name') |
|
515
|
|
|
]); |
|
516
|
|
|
|
|
517
|
|
|
if (!empty($body['firm'])) { |
|
518
|
|
|
// logo is instance of File object. casting to string returns file name. |
|
519
|
|
|
// cast to (array) if firm is empty. |
|
520
|
|
|
$body['firm'] = array_map('strval', (array) array_only($body['firm'], ['name', 'logo'])); |
|
521
|
|
|
} |
|
522
|
|
|
|
|
523
|
|
|
return $body; |
|
524
|
|
|
} |
|
525
|
|
|
|
|
526
|
|
|
/** |
|
527
|
|
|
* @param float|null $salary |
|
528
|
|
|
* @return float|null |
|
529
|
|
|
*/ |
|
530
|
|
|
private function monthlySalary($salary) |
|
531
|
|
|
{ |
|
532
|
|
|
if (empty($salary) || $this->rate_id === self::MONTH) { |
|
533
|
|
|
return $salary; |
|
534
|
|
|
} |
|
535
|
|
|
|
|
536
|
|
|
// we need to calculate monthly salary in order to sorting data by salary |
|
537
|
|
|
if ($this->rate_id == self::YEAR) { |
|
538
|
|
|
$salary = round($salary / 12); |
|
539
|
|
|
} elseif ($this->rate_id == self::WEEK) { |
|
540
|
|
|
$salary = round($salary * 4); |
|
541
|
|
|
} elseif ($this->rate_id == self::HOUR) { |
|
542
|
|
|
$salary = round($salary * 8 * 5 * 4); |
|
543
|
|
|
} |
|
544
|
|
|
|
|
545
|
|
|
return $salary; |
|
546
|
|
|
} |
|
547
|
|
|
} |
|
548
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.