Issues (18)

src/Models/User.php (6 issues)

1
<?php
2
3
namespace Sfneal\Users\Models;
4
5
use Illuminate\Database\Eloquent\Builder;
6
use Illuminate\Database\Eloquent\Factories\HasFactory;
7
use Illuminate\Database\Eloquent\Relations\BelongsTo;
8
use Illuminate\Database\Eloquent\Relations\HasMany;
9
use Illuminate\Database\Eloquent\Relations\HasOne;
10
use Illuminate\Database\Eloquent\Relations\MorphOne;
11
use Sfneal\Address\Models\Address;
12
use Sfneal\Address\Models\Traits\AddressAccessors;
13
use Sfneal\Casts\NewlineCast;
14
use Sfneal\Currency\Currency;
15
use Sfneal\Helpers\Strings\StringHelpers;
16
use Sfneal\Models\AuthModel;
17
use Sfneal\Scopes\OrderScope;
18
use Sfneal\Users\Builders\UserBuilder;
19
use Sfneal\Users\Factories\UserFactory;
20
use Sfneal\Users\Scopes\UserActiveScope;
21
use Sfneal\Users\Services\OrganizationService;
22
use Vkovic\LaravelCustomCasts\HasCustomCasts;
23
24
class User extends AuthModel
25
{
26
    // todo: refactor status to use Status model?
27
    use AddressAccessors;
0 ignored issues
show
The trait Sfneal\Address\Models\Traits\AddressAccessors requires some properties which are not provided by Sfneal\Users\Models\User: $city, $zip, $state, $address_2, $address, $address_1, $city_state
Loading history...
28
    use HasCustomCasts;
29
    use HasFactory;
30
31
    /**
32
     * The "booting" method of the model.
33
     *
34
     * @return void
35
     */
36
    protected static function boot()
37
    {
38
        parent::boot();
39
40
        // Global scopes
41
        static::addGlobalScope(new UserActiveScope());
42
        static::addGlobalScope(new OrderScope('last_name', 'asc'));
43
    }
44
45
    protected $dates = ['deleted_at'];
46
    protected $table = 'user';
47
    protected $primaryKey = 'id';
48
49
    /**
50
     * The attributes that are mass assignable.
51
     *
52
     * @var array
53
     */
54
    protected $fillable = [
55
        'role_id',
56
        'first_name',
57
        'middle_name',
58
        'last_name',
59
        'nickname',
60
        'nickname_preferred',
61
        'title',
62
        'suffix',
63
        'email',
64
        'phone_work',
65
        'phone_mobile',
66
        'fax',
67
        'website',
68
        'bio',
69
        'username',
70
        'password',
71
        'status',
72
        'rate',
73
    ];
74
75
    /**
76
     * The attributes that should be hidden from arrays.
77
     *
78
     * @var array
79
     */
80
    protected $hidden = [
81
        'password',
82
        'remember_token',
83
    ];
84
85
    /**
86
     * The attributes that should type cast.
87
     *
88
     * @var array
89
     */
90
    protected $casts = [
91
        'role_id' => 'int',
92
        'nickname_preferred' => 'int',
93
        'bio' => NewlineCast::class,
94
        'status' => 'int',
95
        'rate' => 'int',
96
    ];
97
98
    /**
99
     * @var array Attributes that should be appended to collections
100
     */
101
    protected $appends = [
102
        'name',
103
        'name_full',
104
    ];
105
106
    /**
107
     * Create a new factory instance for the model.
108
     *
109
     * @return UserFactory
110
     */
111
    protected static function newFactory(): UserFactory
112
    {
113
        return new UserFactory();
114
    }
115
116
    /**
117
     * Query Builder.
118
     *
119
     * @param  $query
120
     * @return UserBuilder
121
     */
122
    public function newEloquentBuilder($query)
123
    {
124
        return new UserBuilder($query);
125
    }
126
127
    /**
128
     * Custom User query Builder.
129
     *
130
     * @return UserBuilder|Builder
131
     */
132
    public static function query(): UserBuilder
133
    {
134
        return parent::query();
135
    }
136
137
    /**
138
     * User's 'role' relationship.
139
     *
140
     * @return BelongsTo
141
     */
142
    public function role()
143
    {
144
        return $this->belongsTo(Role::class, 'role_id', 'role_id');
145
    }
146
147
    /**
148
     * User's Notification Subscriptions.
149
     *
150
     * @return HasMany
151
     */
152
    public function notificationSubscriptions()
153
    {
154
        return $this->hasMany(UserNotification::class, 'user_id', 'id');
155
    }
156
157
    /**
158
     * User's 'team' relationship - indicates user is a member of the public team.
159
     *
160
     * @return HasOne
161
     */
162
    public function team()
163
    {
164
        return $this->hasOne(Team::class, 'user_id', 'id');
165
    }
166
167
    /**
168
     * User's address.
169
     *
170
     * @return MorphOne
171
     */
172
    public function address()
173
    {
174
        return $this->morphOne(Address::class, 'addressable');
175
    }
176
177
    /**
178
     * Determine if a User has a particular 'role_id'.
179
     *
180
     * @param  int  $role_id
181
     * @return bool
182
     */
183
    public function isRoleId(int $role_id): bool
184
    {
185
        return $this->role_id == $role_id;
0 ignored issues
show
The property role_id does not exist on Sfneal\Users\Models\User. Did you mean role?
Loading history...
186
    }
187
188
    /**
189
     * Determine if a User has a particular 'role name'.
190
     *
191
     * @param  string  $role
192
     * @return bool
193
     */
194
    public function isRole(string $role): bool
195
    {
196
        return strtolower($this->role->name) == strtolower($role);
0 ignored issues
show
The property name does not seem to exist on Sfneal\Users\Models\Role. Are you sure there is no database migration missing?

Checks if undeclared accessed properties appear in database migrations and if the creating migration is correct.

Loading history...
197
    }
198
199
    /**
200
     * Determine if a User has an 'admin' role.
201
     *
202
     * @return bool
203
     */
204
    public function isAdmin(): bool
205
    {
206
        // User is considered an 'admin' if user is a 'web developer'
207
        return $this->isRoleId(3) || $this->isRoleId(4);
208
    }
209
210
    /**
211
     * Determine if a User has a 'web developer' role.
212
     *
213
     * @return bool
214
     */
215
    public function isWebDeveloper(): bool
216
    {
217
        return $this->isRoleId(4);
218
    }
219
220
    /**
221
     * Determine if a User has an 'employee' role.
222
     *
223
     * @return bool
224
     */
225
    public function isEmployee(): bool
226
    {
227
        return $this->isRoleId(1);
228
    }
229
230
    /**
231
     * Determine if a User has an 'contractor' role.
232
     *
233
     * @return bool
234
     */
235
    public function isContractor(): bool
236
    {
237
        return $this->isRoleId(2);
238
    }
239
240
    /**
241
     * Determine if a User is 'active'.
242
     *
243
     * @return bool
244
     */
245
    public function isActive(): bool
246
    {
247
        return $this->attributes['status'] == 1;
248
    }
249
250
    /**
251
     * Determine if a User's $nickname is preferred over their $first_name.
252
     *
253
     * @return bool
254
     */
255
    public function isNicknamePreferred(): bool
256
    {
257
        return $this->attributes['nickname_preferred'] == 1;
258
    }
259
260
    /**
261
     * Retrieve a User's initials.
262
     *
263
     * @return string
264
     */
265
    public function getInitialsAttribute()
266
    {
267
        return StringHelpers::implodeFiltered('', collect([
0 ignored issues
show
array($this->attributes[...ttributes['last_name']) of type array is incompatible with the type Illuminate\Contracts\Support\Arrayable expected by parameter $value of collect(). ( Ignorable by Annotation )

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

267
        return StringHelpers::implodeFiltered('', collect(/** @scrutinizer ignore-type */ [
Loading history...
268
            $this->attributes['first_name'],
269
            $this->attributes['middle_name'],
270
            $this->attributes['last_name'],
271
        ])->map(function ($name) {
272
            return substr($name, 0, 1);
273
        })->toArray());
274
    }
275
276
    /**
277
     * Get the AWS S3 file upload directory for an Inquiry model by retrieving the table name and primary key.
278
     *
279
     * @param  string  $base_dir
280
     * @return string
281
     */
282
    public function getUploadDirectory($base_dir = 'images'): string
283
    {
284
        return $base_dir.'/'.str_replace('_', '-', $this->getTable()).'/'.$this->getKey();
285
    }
286
287
    /**
288
     * Mutate the 'middle_name' attribute.
289
     *
290
     * @param  string|null  $value
291
     */
292
    public function setMiddleNameAttribute(string $value = null)
293
    {
294
        if (! is_null($value)) {
295
            // Remove leading & trailing whitespace
296
            $middle_name = trim($value);
297
298
            // Append '.' to the middle name if's a single letter
299
            $this->attributes['middle_name'] = strlen($middle_name) == 1 ? "{$middle_name}." : $middle_name;
300
        }
301
    }
302
303
    /**
304
     * Mutate the 'first_name' attribute.
305
     *
306
     * @param  string|null  $value
307
     */
308
    public function setFirstNameAttribute(string $value = null)
309
    {
310
        if (! is_null($value)) {
311
            $this->attributes['first_name'] = trim($value);
312
        }
313
    }
314
315
    /**
316
     * Mutate the 'last_name' attribute.
317
     *
318
     * @param  string|null  $value
319
     */
320
    public function setLastNameAttribute(string $value = null)
321
    {
322
        if (! is_null($value)) {
323
            $this->attributes['last_name'] = trim($value);
324
        }
325
    }
326
327
    /**
328
     * Access the 'first_name' attribute.
329
     *
330
     * @param  string|null  $value
331
     * @return string
332
     */
333
    public function getFirstNameAttribute(string $value = null): string
334
    {
335
        return trim($value);
0 ignored issues
show
It seems like $value can also be of type null; however, parameter $string of trim() does only seem to accept string, 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

335
        return trim(/** @scrutinizer ignore-type */ $value);
Loading history...
336
    }
337
338
    /**
339
     * Access the 'last_name' attribute.
340
     *
341
     * @param  string|null  $value
342
     * @return string
343
     */
344
    public function getLastNameAttribute(string $value = null): string
345
    {
346
        return trim($value);
0 ignored issues
show
It seems like $value can also be of type null; however, parameter $string of trim() does only seem to accept string, 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

346
        return trim(/** @scrutinizer ignore-type */ $value);
Loading history...
347
    }
348
349
    /**
350
     * Retrieve the User's name (first & last).
351
     *
352
     * @return string
353
     */
354
    public function getNameAttribute(): string
355
    {
356
        if ($this->nameAttributesAreNotAccessible()) {
357
            return '';
358
        }
359
360
        // Use nickname instead of first if it is set & preferred
361
        $first = (isset($this->attributes['nickname']) && $this->isNicknamePreferred())
362
            ? $this->attributes['nickname']
363
            : $this->attributes['first_name'];
364
365
        // Return concatenated name
366
        return "{$first} {$this->attributes['last_name']}";
367
    }
368
369
    /**
370
     * Retrieve the User's full name with middle initial.
371
     *
372
     * @return string
373
     */
374
    public function getNameFullAttribute(): string
375
    {
376
        if ($this->nameAttributesAreNotAccessible()) {
377
            return '';
378
        }
379
380
        $name = $this->attributes['first_name'];
381
        if (array_key_exists('middle_name', $this->attributes) && $this->attributes['middle_name']) {
382
            $name .= ' '.$this->attributes['middle_name'];
383
        }
384
385
        return "{$name} {$this->attributes['last_name']}";
386
    }
387
388
    private function nameAttributesAreNotAccessible(): bool
389
    {
390
        return ! isset($this->attributes['first_name']) || ! isset($this->attributes['last_name']);
391
    }
392
393
    /**
394
     * Retrieve the User's name with their suffix.
395
     *
396
     * @return string
397
     */
398
    public function getNameSuffixAttribute(): string
399
    {
400
        return $this->getNameFullAttribute().($this->attributes['suffix'] ? ", {$this->attributes['suffix']}" : '');
401
    }
402
403
    /**
404
     * Get the 'list_name' attribute.
405
     *
406
     * @return string
407
     */
408
    public function getListNameAttribute()
409
    {
410
        return implode(', ', array_reverse(explode(' ', $this->getNameAttribute())));
411
    }
412
413
    /**
414
     * Get the 'name_link' attribute that returns a url to the User's team.show page.
415
     *
416
     * @return string
417
     */
418
    public function getNameLinkAttribute()
419
    {
420
        return '<a href="'.route('user.show', ['user' => $this->getKey()]).'">'.$this->getNameAttribute().'</a>';
421
    }
422
423
    /**
424
     * Retrieve an email link.
425
     *
426
     * @return string
427
     */
428
    public function getEmailLinkAttribute(): string
429
    {
430
        return $this->attributes['email'] ? ('mailto:'.$this->attributes['email']) : '#!';
431
    }
432
433
    /**
434
     * Retrieve a work phone link.
435
     *
436
     * @return string
437
     */
438
    public function getPhoneWorkLinkAttribute(): string
439
    {
440
        return $this->attributes['phone_work'] ? ('tel:'.$this->attributes['phone_work']) : '#!';
441
    }
442
443
    /**
444
     * Retrieve a mobile phone link.
445
     *
446
     * @return string
447
     */
448
    public function getPhoneMobileLinkAttribute(): string
449
    {
450
        return $this->attributes['phone_mobile'] ? ('tel:'.$this->attributes['phone_mobile']) : '#!';
451
    }
452
453
    /**
454
     * Retrieve the User's rate formatted as dollars.
455
     *
456
     * @return string
457
     */
458
    public function getRateFormattedAttribute(): string
459
    {
460
        return (! empty($this->attributes['rate'])) ? Currency::dollars($this->attributes['rate']) : '-';
461
    }
462
463
    /**
464
     * Retrieve the raw 'text' attribute with newline chars.
465
     *
466
     * @return mixed
467
     */
468
    public function getTextareaAttribute()
469
    {
470
        return $this->attributes['bio'];
471
    }
472
473
    /**
474
     * Retrieve a User's custom email footer.
475
     *
476
     * @return string
477
     */
478
    public function getEmailFooterAttribute(): string
479
    {
480
        $footer = "{$this->attributes['name']}";
481
        $footer .= $this->attributes['title'] ? "\n{$this->attributes['title']}" : '';
482
        $footer .= "\n".OrganizationService::name() ?? '';
483
        $footer .= "\n".($this->attributes['phone_work'] ?? OrganizationService::phone()) ?? '';
484
        $footer .= $this->attributes['email'] ? "\n{$this->attributes['email']}" : '';
485
486
        return $footer;
487
    }
488
}
489