Test Failed
Push — master ( 857478...5079a8 )
by Jeremy
20:32 queued 15:15
created

HasRoleAndPermission::resetRoles()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
eloc 5
c 1
b 1
f 0
dl 0
loc 7
rs 10
cc 2
nc 2
nop 0
1
<?php
2
3
namespace jeremykenedy\LaravelRoles\Traits;
4
5
use Illuminate\Database\Eloquent\Builder;
6
use Illuminate\Database\Eloquent\Collection;
7
use Illuminate\Database\Eloquent\Model;
8
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
9
use Illuminate\Support\Str;
10
use InvalidArgumentException;
11
use jeremykenedy\LaravelRoles\Models\Permission;
12
use jeremykenedy\LaravelRoles\Models\Role;
13
14
trait HasRoleAndPermission
15
{
16
    /**
17
     * Property for caching roles.
18
     *
19
     * @var Collection|null
20
     */
21
    protected $roles;
22
23
    /**
24
     * Property for caching permissions.
25
     *
26
     * @var Collection|null
27
     */
28
    protected $permissions;
29
30
    /**
31
     * User belongs to many roles.
32
     *
33
     * @return BelongsToMany
34
     */
35
    public function roles()
36
    {
37
        return $this->belongsToMany(config('roles.models.role'), config('roles.roleUserTable'))->withTimestamps();
38
    }
39
40
    /**
41
     * Get all roles as collection.
42
     *
43
     * @return Collection
44
     */
45
    public function getRoles()
46
    {
47
        if (!$this->roles) {
48
            if (method_exists($this, 'loadMissing')) {
49
                $this->loadMissing('roles');
0 ignored issues
show
Bug introduced by
The method loadMissing() does not exist on jeremykenedy\LaravelRole...ts\HasRoleAndPermission. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

49
                $this->/** @scrutinizer ignore-call */ 
50
                       loadMissing('roles');
Loading history...
50
            } else {
51
                if (!array_key_exists('roles', $this->relations)) {
52
                    $this->load('roles');
0 ignored issues
show
Bug introduced by
The method load() does not exist on jeremykenedy\LaravelRole...ts\HasRoleAndPermission. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

52
                    $this->/** @scrutinizer ignore-call */ 
53
                           load('roles');
Loading history...
53
                }
54
            }
55
            if (method_exists($this, 'getRelation')) {
56
                $this->roles = $this->getRelation('roles');
0 ignored issues
show
Bug introduced by
The method getRelation() does not exist on jeremykenedy\LaravelRole...ts\HasRoleAndPermission. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

56
                /** @scrutinizer ignore-call */ 
57
                $this->roles = $this->getRelation('roles');
Loading history...
57
            } else {
58
                $this->roles = $this->relations['roles'];
59
            }
60
        }
61
62
        return $this->roles;
63
    }
64
65
    /**
66
     * Check if the user has a role or roles.
67
     *
68
     * @param int|string|array $role
69
     * @param bool             $all
70
     *
71
     * @return bool
72
     */
73
    public function hasRole($role, $all = false)
74
    {
75
        if ($this->isPretendEnabled()) {
76
            return $this->pretend('hasRole');
77
        }
78
79
        if (!$all) {
80
            return $this->hasOneRole($role);
81
        }
82
83
        return $this->hasAllRoles($role);
84
    }
85
86
    /**
87
     * Check if the user has at least one of the given roles.
88
     *
89
     * @param int|string|array $role
90
     *
91
     * @return bool
92
     */
93
    public function hasOneRole($role)
94
    {
95
        foreach ($this->getArrayFrom($role) as $role) {
0 ignored issues
show
introduced by
$role is overwriting one of the parameters of this function.
Loading history...
96
            if ($this->checkRole($role)) {
97
                return true;
98
            }
99
        }
100
101
        return false;
102
    }
103
104
    /**
105
     * Check if the user has all roles.
106
     *
107
     * @param int|string|array $role
108
     *
109
     * @return bool
110
     */
111
    public function hasAllRoles($role)
112
    {
113
        foreach ($this->getArrayFrom($role) as $role) {
0 ignored issues
show
introduced by
$role is overwriting one of the parameters of this function.
Loading history...
114
            if (!$this->checkRole($role)) {
115
                return false;
116
            }
117
        }
118
119
        return true;
120
    }
121
122
    /**
123
     * Check if the user has role.
124
     *
125
     * @param int|string $role
126
     *
127
     * @return bool
128
     */
129
    public function checkRole($role)
130
    {
131
        return $this->getRoles()->contains(function ($value) use ($role) {
132
            return $role == $value->id || Str::is($role, $value->slug);
133
        });
134
    }
135
136
    /**
137
     * Attach role to a user.
138
     *
139
     * @param int|Role $role
140
     *
141
     * @return null|bool
142
     */
143
    public function attachRole($role)
144
    {
145
        if ($this->getRoles()->contains($role)) {
146
            return true;
147
        }
148
        $this->resetRoles();
149
150
        return $this->roles()->attach($role);
151
    }
152
153
    /**
154
     * Detach role from a user.
155
     *
156
     * @param int|Role $role
157
     *
158
     * @return int
159
     */
160
    public function detachRole($role)
161
    {
162
        $this->resetRoles();
163
164
        return $this->roles()->detach($role);
165
    }
166
167
    /**
168
     * Detach all roles from a user.
169
     *
170
     * @return int
171
     */
172
    public function detachAllRoles()
173
    {
174
        $this->resetRoles();
175
176
        return $this->roles()->detach();
177
    }
178
179
    /**
180
     * Sync roles for a user.
181
     *
182
     * @param array|\jeremykenedy\LaravelRoles\Models\Role[]|\Illuminate\Database\Eloquent\Collection $roles
183
     *
184
     * @return array
185
     */
186
    public function syncRoles($roles)
187
    {
188
        $this->resetRoles();
189
190
        return $this->roles()->sync($roles);
191
    }
192
193
    /**
194
     * Get role level of a user.
195
     *
196
     * @return int
197
     */
198
    public function level()
199
    {
200
        return ($role = $this->getRoles()->sortByDesc('level')->first()) ? $role->level : 0;
201
    }
202
203
    /**
204
     * Get all permissions from roles.
205
     *
206
     * @return Builder
207
     */
208
    public function rolePermissions()
209
    {
210
        $permissionModel = app(config('roles.models.permission'));
211
        $permissionTable = config('roles.permissionsTable');
212
        $roleTable = config('roles.rolesTable');
213
214
        if (!$permissionModel instanceof Model) {
215
            throw new InvalidArgumentException('[roles.models.permission] must be an instance of \Illuminate\Database\Eloquent\Model');
216
        }
217
218
        if (config('roles.inheritance')) {
219
            return $permissionModel::select([$permissionTable.'.*', 'permission_role.created_at as pivot_created_at', 'permission_role.updated_at as pivot_updated_at'])
0 ignored issues
show
Bug Best Practice introduced by
The expression return $permissionModel:...', 'pivot_updated_at')) also could return the type Illuminate\Database\Query\Builder which is incompatible with the documented return type Illuminate\Database\Eloquent\Builder.
Loading history...
220
                ->join('permission_role', 'permission_role.permission_id', '=', $permissionTable.'.id')
221
                ->join($roleTable, $roleTable.'.id', '=', 'permission_role.role_id')
222
                ->whereIn($roleTable.'.id', $this->getRoles()->pluck('id')->toArray())
223
                ->orWhere($roleTable.'.level', '<', $this->level())
224
                ->groupBy([$permissionTable.'.id', $permissionTable.'.name', $permissionTable.'.slug', $permissionTable.'.description', $permissionTable.'.model', $permissionTable.'.created_at', 'permissions.updated_at', $permissionTable.'.deleted_at', 'pivot_created_at', 'pivot_updated_at']);
225
        } else {
226
            return $permissionModel::select([$permissionTable.'.*', 'permission_role.created_at as pivot_created_at', 'permission_role.updated_at as pivot_updated_at'])
0 ignored issues
show
Bug Best Practice introduced by
The expression return $permissionModel:...', 'pivot_updated_at')) also could return the type Illuminate\Database\Query\Builder which is incompatible with the documented return type Illuminate\Database\Eloquent\Builder.
Loading history...
227
                ->join('permission_role', 'permission_role.permission_id', '=', $permissionTable.'.id')
228
                ->join($roleTable, $roleTable.'.id', '=', 'permission_role.role_id')
229
                ->whereIn($roleTable.'.id', $this->getRoles()->pluck('id')->toArray())
230
                ->groupBy([$permissionTable.'.id', $permissionTable.'.name', $permissionTable.'.slug', $permissionTable.'.description', $permissionTable.'.model', $permissionTable.'.created_at', $permissionTable.'.updated_at', $permissionTable.'.deleted_at', 'pivot_created_at', 'pivot_updated_at']);
231
        }
232
    }
233
234
    /**
235
     * User belongs to many permissions.
236
     *
237
     * @return BelongsToMany
238
     */
239
    public function userPermissions()
240
    {
241
        return $this->belongsToMany(config('roles.models.permission'), config('permissionsUserTable'))->withTimestamps();
242
    }
243
244
    /**
245
     * Get all permissions as collection.
246
     *
247
     * @return Collection
248
     */
249
    public function getPermissions()
250
    {
251
        return (!$this->permissions) ? $this->permissions = $this->rolePermissions()->get()->merge($this->userPermissions()->get()) : $this->permissions;
252
    }
253
254
    /**
255
     * Check if the user has a permission or permissions.
256
     *
257
     * @param int|string|array $permission
258
     * @param bool             $all
259
     *
260
     * @return bool
261
     */
262
    public function hasPermission($permission, $all = false)
263
    {
264
        if ($this->isPretendEnabled()) {
265
            return $this->pretend('hasPermission');
266
        }
267
268
        if (!$all) {
269
            return $this->hasOnePermission($permission);
270
        }
271
272
        return $this->hasAllPermissions($permission);
273
    }
274
275
    /**
276
     * Check if the user has at least one of the given permissions.
277
     *
278
     * @param int|string|array $permission
279
     *
280
     * @return bool
281
     */
282
    public function hasOnePermission($permission)
283
    {
284
        foreach ($this->getArrayFrom($permission) as $permission) {
0 ignored issues
show
introduced by
$permission is overwriting one of the parameters of this function.
Loading history...
285
            if ($this->checkPermission($permission)) {
286
                return true;
287
            }
288
        }
289
290
        return false;
291
    }
292
293
    /**
294
     * Check if the user has all permissions.
295
     *
296
     * @param int|string|array $permission
297
     *
298
     * @return bool
299
     */
300
    public function hasAllPermissions($permission)
301
    {
302
        foreach ($this->getArrayFrom($permission) as $permission) {
0 ignored issues
show
introduced by
$permission is overwriting one of the parameters of this function.
Loading history...
303
            if (!$this->checkPermission($permission)) {
304
                return false;
305
            }
306
        }
307
308
        return true;
309
    }
310
311
    /**
312
     * Check if the user has a permission.
313
     *
314
     * @param int|string $permission
315
     *
316
     * @return bool
317
     */
318
    public function checkPermission($permission)
319
    {
320
        return $this->getPermissions()->contains(function ($value) use ($permission) {
321
            return $permission == $value->id || Str::is($permission, $value->slug);
322
        });
323
    }
324
325
    /**
326
     * Check if the user is allowed to manipulate with entity.
327
     *
328
     * @param string $providedPermission
329
     * @param Model  $entity
330
     * @param bool   $owner
331
     * @param string $ownerColumn
332
     *
333
     * @return bool
334
     */
335
    public function allowed($providedPermission, Model $entity, $owner = true, $ownerColumn = 'user_id')
336
    {
337
        if ($this->isPretendEnabled()) {
338
            return $this->pretend('allowed');
339
        }
340
341
        if ($owner === true && $entity->{$ownerColumn} == $this->id) {
342
            return true;
343
        }
344
345
        return $this->isAllowed($providedPermission, $entity);
346
    }
347
348
    /**
349
     * Check if the user is allowed to manipulate with provided entity.
350
     *
351
     * @param string $providedPermission
352
     * @param Model  $entity
353
     *
354
     * @return bool
355
     */
356
    protected function isAllowed($providedPermission, Model $entity)
357
    {
358
        foreach ($this->getPermissions() as $permission) {
359
            if ($permission->model != '' && get_class($entity) == $permission->model
360
                && ($permission->id == $providedPermission || $permission->slug === $providedPermission)
361
            ) {
362
                return true;
363
            }
364
        }
365
366
        return false;
367
    }
368
369
    /**
370
     * Attach permission to a user.
371
     *
372
     * @param int|Permission $permission
373
     *
374
     * @return null|bool
375
     */
376
    public function attachPermission($permission)
377
    {
378
        if ($this->getPermissions()->contains($permission)) {
379
            return true;
380
        }
381
        $this->permissions = null;
382
383
        return $this->userPermissions()->attach($permission);
384
    }
385
386
    /**
387
     * Detach permission from a user.
388
     *
389
     * @param int|Permission $permission
390
     *
391
     * @return int
392
     */
393
    public function detachPermission($permission)
394
    {
395
        $this->permissions = null;
396
397
        return $this->userPermissions()->detach($permission);
398
    }
399
400
    /**
401
     * Detach all permissions from a user.
402
     *
403
     * @return int
404
     */
405
    public function detachAllPermissions()
406
    {
407
        $this->permissions = null;
408
409
        return $this->userPermissions()->detach();
410
    }
411
412
    /**
413
     * Sync permissions for a user.
414
     *
415
     * @param array|\jeremykenedy\LaravelRoles\Models\Permission[]|\Illuminate\Database\Eloquent\Collection $permissions
416
     *
417
     * @return array
418
     */
419
    public function syncPermissions($permissions)
420
    {
421
        $this->permissions = null;
422
423
        return $this->userPermissions()->sync($permissions);
424
    }
425
426
    /**
427
     * Check if pretend option is enabled.
428
     *
429
     * @return bool
430
     */
431
    private function isPretendEnabled()
432
    {
433
        return (bool) config('roles.pretend.enabled');
434
    }
435
436
    /**
437
     * Allows to pretend or simulate package behavior.
438
     *
439
     * @param string $option
440
     *
441
     * @return bool
442
     */
443
    private function pretend($option)
444
    {
445
        return (bool) config('roles.pretend.options.'.$option);
446
    }
447
448
    /**
449
     * Get an array from argument.
450
     *
451
     * @param int|string|array $argument
452
     *
453
     * @return array
454
     */
455
    private function getArrayFrom($argument)
456
    {
457
        return (!is_array($argument)) ? preg_split('/ ?[,|] ?/', $argument) : $argument;
458
    }
459
460
    protected function resetRoles()
461
    {
462
        $this->roles = null;
463
        if (method_exists($this, 'unsetRelation')) {
464
            $this->unsetRelation('roles');
0 ignored issues
show
Bug introduced by
The method unsetRelation() does not exist on jeremykenedy\LaravelRole...ts\HasRoleAndPermission. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

464
            $this->/** @scrutinizer ignore-call */ 
465
                   unsetRelation('roles');
Loading history...
465
        } else {
466
            unset($this->relations['roles']);
467
        }
468
    }
469
470
    public function callMagic($method, $parameters)
471
    {
472
        if (starts_with($method, 'is')) {
473
            return $this->hasRole(snake_case(substr($method, 2), config('roles.separator')));
474
        } elseif (starts_with($method, 'can')) {
475
            return $this->hasPermission(snake_case(substr($method, 3), config('roles.separator')));
476
        } elseif (starts_with($method, 'allowed')) {
477
            return $this->allowed(snake_case(substr($method, 7), config('roles.separator')), $parameters[0], (isset($parameters[1])) ? $parameters[1] : true, (isset($parameters[2])) ? $parameters[2] : 'user_id');
478
        }
479
480
        return parent::__call($method, $parameters);
481
    }
482
483
    public function __call($method, $parameters)
484
    {
485
        return $this->callMagic($method, $parameters);
486
    }
487
}
488