Completed
Push — develop ( 5abd4c...dae2aa )
by Abdelrahman
09:07
created

User::setAbilitiesAttribute()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 11
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 11
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 7
nc 1
nop 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Cortex\Auth\Models;
6
7
use Rinvex\Country\Country;
8
use Rinvex\Language\Language;
9
use Illuminate\Auth\Authenticatable;
10
use Illuminate\Support\Facades\Hash;
11
use Rinvex\Auth\Traits\HasHashables;
12
use Rinvex\Tenants\Traits\Tenantable;
13
use Rinvex\Auth\Traits\CanVerifyEmail;
14
use Rinvex\Auth\Traits\CanVerifyPhone;
15
use Cortex\Foundation\Traits\Auditable;
16
use Illuminate\Database\Eloquent\Model;
17
use Rinvex\Cacheable\CacheableEloquent;
18
use Illuminate\Notifications\Notifiable;
19
use Rinvex\Auth\Traits\CanResetPassword;
20
use Rinvex\Attributes\Traits\Attributable;
21
use Rinvex\Support\Traits\ValidatingTrait;
22
use Spatie\Activitylog\Traits\HasActivity;
23
use Spatie\MediaLibrary\HasMedia\HasMedia;
24
use Spatie\MediaLibrary\HasMedia\HasMediaTrait;
25
use Rinvex\Auth\Traits\AuthenticatableTwoFactor;
26
use Rinvex\Auth\Contracts\CanVerifyEmailContract;
27
use Rinvex\Auth\Contracts\CanVerifyPhoneContract;
28
use Silber\Bouncer\Database\HasRolesAndAbilities;
29
use Illuminate\Foundation\Auth\Access\Authorizable;
30
use Rinvex\Auth\Contracts\CanResetPasswordContract;
31
use Illuminate\Database\Eloquent\Relations\MorphMany;
32
use Rinvex\Auth\Contracts\AuthenticatableTwoFactorContract;
33
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
34
use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract;
35
36
abstract class User extends Model implements AuthenticatableContract, AuthenticatableTwoFactorContract, AuthorizableContract, CanResetPasswordContract, CanVerifyEmailContract, CanVerifyPhoneContract, HasMedia
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 208 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
37
{
38
    // @TODO: Strangely, this issue happens only here!!!
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
39
    // Duplicate trait usage to fire attached events for cache
40
    // flush before other events in other traits specially HasActivity,
41
    // otherwise old cached queries used and no changelog recorded on update.
42
    use CacheableEloquent;
43
    use Auditable;
44
    use Tenantable;
45
    use Notifiable;
46
    use HasActivity;
47
    use Attributable;
48
    use Authorizable;
49
    use HasHashables;
50
    use HasMediaTrait;
51
    use CanVerifyEmail;
52
    use CanVerifyPhone;
53
    use Authenticatable;
54
    use ValidatingTrait;
55
    use CanResetPassword;
56
    use HasRolesAndAbilities;
57
    use AuthenticatableTwoFactor;
58
59
    /**
60
     * {@inheritdoc}
61
     */
62
    protected $fillable = [
63
        'username',
64
        'password',
65
        'two_factor',
66
        'email',
67
        'email_verified',
68
        'email_verified_at',
69
        'phone',
70
        'phone_verified',
71
        'phone_verified_at',
72
        'name_prefix',
73
        'first_name',
74
        'middle_name',
75
        'last_name',
76
        'name_suffix',
77
        'title',
78
        'country_code',
79
        'language_code',
80
        'birthday',
81
        'gender',
82
        'is_active',
83
        'last_activity',
84
        'abilities',
85
        'roles',
86
    ];
87
88
    /**
89
     * {@inheritdoc}
90
     */
91
    protected $casts = [
92
        'username' => 'string',
93
        'password' => 'string',
94
        'two_factor' => 'json',
95
        'email' => 'string',
96
        'email_verified' => 'boolean',
97
        'email_verified_at' => 'datetime',
98
        'phone' => 'string',
99
        'phone_verified' => 'boolean',
100
        'phone_verified_at' => 'datetime',
101
        'name_prefix' => 'string',
102
        'first_name' => 'string',
103
        'middle_name' => 'string',
104
        'last_name' => 'string',
105
        'name_suffix' => 'string',
106
        'title' => 'string',
107
        'country_code' => 'string',
108
        'language_code' => 'string',
109
        'birthday' => 'string',
110
        'gender' => 'string',
111
        'is_active' => 'boolean',
112
        'last_activity' => 'datetime',
113
        'deleted_at' => 'datetime',
114
    ];
115
116
    /**
117
     * {@inheritdoc}
118
     */
119
    protected $hidden = [
120
        'password',
121
        'two_factor',
122
        'remember_token',
123
    ];
124
125
    /**
126
     * {@inheritdoc}
127
     */
128
    protected $observables = [
129
        'validating',
130
        'validated',
131
    ];
132
133
    /**
134
     * The attributes to be encrypted before saving.
135
     *
136
     * @var array
137
     */
138
    protected $hashables = [
139
        'password',
140
    ];
141
142
    /**
143
     * The default rules that the model will validate against.
144
     *
145
     * @var array
146
     */
147
    protected $rules = [];
148
149
    /**
150
     * Whether the model should throw a
151
     * ValidationException if it fails validation.
152
     *
153
     * @var bool
154
     */
155
    protected $throwValidationExceptions = true;
156
157
    /**
158
     * Indicates whether to log only dirty attributes or all.
159
     *
160
     * @var bool
161
     */
162
    protected static $logOnlyDirty = true;
163
164
    /**
165
     * The attributes that are logged on change.
166
     *
167
     * @var array
168
     */
169
    protected static $logFillable = true;
170
171
    /**
172
     * The attributes that are ignored on change.
173
     *
174
     * @var array
175
     */
176
    protected static $ignoreChangedAttributes = [
177
        'password',
178
        'two_factor',
179
        'email_verified_at',
180
        'phone_verified_at',
181
        'last_activity',
182
        'created_at',
183
        'updated_at',
184
        'deleted_at',
185
    ];
186
187
    /**
188
     * Get the route key for the model.
189
     *
190
     * @return string
191
     */
192
    public function getRouteKeyName(): string
193
    {
194
        return 'username';
195
    }
196
197
    /**
198
     * Register media collections.
199
     *
200
     * @return void
201
     */
202
    public function registerMediaCollections(): void
203
    {
204
        $this->addMediaCollection('profile_picture')->singleFile();
205
        $this->addMediaCollection('cover_photo')->singleFile();
206
    }
207
208
    /**
209
     * Attach the given abilities to the model.
210
     *
211
     * @param mixed $abilities
212
     *
213
     * @return void
214
     */
215
    public function setAbilitiesAttribute($abilities): void
216
    {
217
        static::saved(function (self $model) use ($abilities) {
218
            activity()
219
                ->performedOn($model)
220
                ->withProperties(['attributes' => ['abilities' => $abilities], 'old' => ['abilities' => $model->abilities->pluck('id')->toArray()]])
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 148 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
221
                ->log('updated');
222
223
            $model->abilities()->sync($abilities, true);
224
        });
225
    }
226
227
    /**
228
     * Attach the given roles to the model.
229
     *
230
     * @param mixed $roles
231
     *
232
     * @return void
233
     */
234
    public function setRolesAttribute($roles): void
235
    {
236
        static::saved(function (self $model) use ($roles) {
237
            activity()
238
                ->performedOn($model)
239
                ->withProperties(['attributes' => ['roles' => $roles], 'old' => ['roles' => $model->roles->pluck('id')->toArray()]])
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 132 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
240
                ->log('updated');
241
242
            $model->roles()->sync($roles, true);
243
        });
244
    }
245
246
    /**
247
     * {@inheritdoc}
248
     */
249
    protected static function boot()
250
    {
251
        parent::boot();
252
253
        static::saving(function (self $user) {
254
            foreach (array_intersect($user->getHashables(), array_keys($user->getAttributes())) as $hashable) {
255
                if ($user->isDirty($hashable) && Hash::needsRehash($user->$hashable)) {
256
                    $user->$hashable = Hash::make($user->$hashable);
257
                }
258
            }
259
        });
260
    }
261
262
    /**
263
     * The user may have many sessions.
264
     *
265
     * @return \Illuminate\Database\Eloquent\Relations\MorphMany
266
     */
267
    public function sessions(): MorphMany
268
    {
269
        return $this->morphMany(config('cortex.auth.models.session'), 'user');
270
    }
271
272
    /**
273
     * The user may have many socialites.
274
     *
275
     * @return \Illuminate\Database\Eloquent\Relations\MorphMany
276
     */
277
    public function socialites(): MorphMany
278
    {
279
        return $this->morphMany(config('cortex.auth.models.socialite'), 'user');
280
    }
281
282
    /**
283
     * Get name attribute.
284
     *
285
     * @return string
286
     */
287
    public function getNameAttribute(): string
288
    {
289
        $name = trim(implode(' ', [$this->name_prefix, $this->first_name, $this->middle_name, $this->last_name, $this->name_suffix]));
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 134 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
290
291
        return $name ?: $this->username;
292
    }
293
294
    /**
295
     * Route notifications for the authy channel.
296
     *
297
     * @return int|null
298
     */
299
    public function routeNotificationForAuthy(): ?int
300
    {
301
        if (! ($authyId = array_get($this->getTwoFactor(), 'phone.authy_id')) && $this->getEmailForVerification() && $this->getPhoneForVerification() && $this->getCountryForVerification()) {
0 ignored issues
show
Bug introduced by
It seems like $this->getTwoFactor() targeting Rinvex\Auth\Traits\Authe...oFactor::getTwoFactor() can also be of type null; however, array_get() does only seem to accept object<ArrayAccess>|array, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 190 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
302
            $result = app('rinvex.authy.user')->register($this->getEmailForVerification(), preg_replace('/[^0-9]/', '', $this->getPhoneForVerification()), $this->getCountryForVerification());
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 191 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
303
            $authyId = $result->get('user')['id'];
304
305
            // Prepare required variables
306
            $twoFactor = $this->getTwoFactor();
307
308
            // Update user account
309
            array_set($twoFactor, 'phone.authy_id', $authyId);
0 ignored issues
show
Bug introduced by
It seems like $twoFactor defined by $this->getTwoFactor() on line 306 can also be of type null; however, array_set() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
310
311
            $this->fill(['two_factor' => $twoFactor])->forceSave();
312
        }
313
314
        return $authyId;
315
    }
316
317
    /**
318
     * Get the user's country.
319
     *
320
     * @return \Rinvex\Country\Country
321
     */
322
    public function getCountryAttribute(): Country
323
    {
324
        return country($this->country_code);
325
    }
326
327
    /**
328
     * Get the user's language.
329
     *
330
     * @return \Rinvex\Language\Language
331
     */
332
    public function getLanguageAttribute(): Language
333
    {
334
        return language($this->language_code);
335
    }
336
337
    /**
338
     * Activate the user.
339
     *
340
     * @return $this
341
     */
342
    public function activate()
343
    {
344
        $this->update(['is_active' => true]);
345
346
        return $this;
347
    }
348
349
    /**
350
     * Deactivate the user.
351
     *
352
     * @return $this
353
     */
354
    public function deactivate()
355
    {
356
        $this->update(['is_active' => false]);
357
358
        return $this;
359
    }
360
}
361