Completed
Push — master ( d0a6e2...c1e6c3 )
by Stephen
07:22
created

UserAndLevel::getPermissions()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10
Code Lines 6

Duplication

Lines 10
Ratio 100 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 10
loc 10
c 0
b 0
f 0
ccs 2
cts 2
cp 1
rs 9.4285
cc 1
eloc 6
nc 1
nop 0
crap 1
1
<?php
2
3
namespace z1haze\Acl\Traits;
4
5
use Illuminate\Support\Collection;
6
use ReflectionClass;
7
use z1haze\Acl\Exceptions\PermissionNotFoundException;
8
use Illuminate\Support\Facades\Cache;
9
use z1haze\Acl\Models\Level;
10
use z1haze\Acl\Models\Permission;
11
12
trait UserAndLevel
13
{
14
    /**
15
     * USER & LEVEL
16
     * Check if a model has a given level
17
     *
18
     * @param mixed level
19 1
     * @return bool
20
     */
21 1
    public function isLevel($level)
22
    {
23 1
        $level = $this->getALevel($level);
0 ignored issues
show
Bug introduced by
It seems like getALevel() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
24
25
        return is_a($this, config('laravel-acl.level'), true) ? $this->name == $level->name : $this->level->name == $level->name;
0 ignored issues
show
Bug introduced by
The property name does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
Bug introduced by
The property level does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
26
    }
27
28
    /**
29
     * USER & LEVEL
30
     * Check if a user has or has inherited
31
     * a given level
32
     *
33
     * @param $level
34 1
     * @return bool
35
     */
36 1
    public function hasLevel($level)
37
    {
38 1
        $level = $this->getLevel($level);
0 ignored issues
show
Bug introduced by
It seems like getLevel() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
39
40 1
        return is_a($this, config('laravel-acl.level'), true) ? $this->rank <= $level->rank : $this->level->rank <= $level->rank;
0 ignored issues
show
Bug introduced by
The property rank does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
41
    }
42
43
    /**
44
     * USER & LEVEL
45
     * Add a single permission to a model
46
     *
47
     * @param $permission
48
     */
49 15
    public function addPermission($permission)
50
    {
51 15
        $this->modifyPermissions([$permission], 'add');
52 14
    }
53
54
    /**
55
     * USER & LEVEL
56
     * Add an array of permissions to a model
57
     *
58
     * @param $permissions
59
     */
60 9
    public function addPermissions(array $permissions)
61
    {
62 9
        $this->modifyPermissions($permissions, 'add');
63 9
    }
64
65
    /**
66
     * USER & LEVEL
67
     * Remove a permission from a model
68
     *
69
     * @param $permission
70
     */
71 2
    public function removePermission($permission)
72
    {
73 2
        $this->modifyPermissions([$permission], 'remove');
74 2
    }
75
76
    /**
77
     * USER & LEVEL
78
     * Remove an array of permissions objects or permission names (can be mixed)
79
     *
80
     * @param $permissions
81
     */
82 1
    public function removePermissions(array $permissions)
83
    {
84 1
        $this->modifyPermissions($permissions, 'remove');
85 1
    }
86
87
    /**
88
     * USER & LEVEL
89
     * Sync permissions on a model
90
     *
91
     * @param $permissions
92
     */
93 2
    public function syncPermissions(array $permissions)
94
    {
95 2
        $this->modifyPermissions($permissions, 'sync');
96 2
    }
97
98
    /**
99
     * USER & LEVEL
100
     * Clear permissions from a model
101
     */
102
    public function clearPermissions()
103
    {
104 3
        if (is_a($this, config('laravel-acl.user'), true))
105
            $this->permissions()->detach();
0 ignored issues
show
Bug introduced by
The method permissions() does not exist on z1haze\Acl\Traits\UserAndLevel. Did you maybe mean addPermissions()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
106 3
107 1
        if (is_a($this, config('laravel-acl.level'), true))
108
            config('laravel-acl.permission', Permission::class)::whereLevelId($this->id)->update(['level_id' => null]);
0 ignored issues
show
Bug introduced by
The property id does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
109 3
    }
110 2
111 3
    /**
112
     * USER & LEVEL
113
     * First try the cache to return the collection,
114
     * then fetch it from the database.
115
     *
116
     * See @cacheGetPermissions()
117
     *
118
     * @return \Illuminate\Support\Collection
119
     */
120 View Code Duplication
    public function getPermissions()
1 ignored issue
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
121
    {
122
        return Cache::remember(
123
            'laravel-acl.getPermissionsFor' . (new ReflectionClass($this))->getShortName() . '_' . $this->id,
124
            config('laravel-acl.cacheMinutes'),
125
            function () {
126
                return $this->cachegetPermissions();
127 6
            }
128
        );
129 6
    }
130
131 6
    /**
132 6
     * USER & LEVEL
133 6
     * First try the cache to return the collection,
134
     * then fetch it from the database.
135 6
     *
136 6
     * See @cacheGetAllPermissions()
137
     *
138
     * @return \Illuminate\Support\Collection
139
     */
140 View Code Duplication
    public function getAllPermissions()
1 ignored issue
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
141
    {
142
        return Cache::remember(
143
            'laravel-acl.getAllPermissionsFor' . (new ReflectionClass($this))->getShortName() . '_' . $this->id,
144
            config('laravel-acl.cacheMinutes'),
145
            function () {
146
                return $this->cacheGetAllPermissions();
147
            }
148
        );
149 3
    }
150
151 3
    /**
152
     * USER & LEVEL
153 3
     * First try the cache to return the collection,
154 3
     * then fetch it from the database.
155 3
     *
156
     * See @cacheGetInheritedPermissions()
157 3
     *
158 3
     * @return \Illuminate\Support\Collection
159
     */
160 View Code Duplication
    public function getInheritedPermissions()
1 ignored issue
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
161
    {
162
        return Cache::remember(
163
            'laravel-acl.getInheritedPermissionsFor' . (new ReflectionClass($this))->getShortName() . '_' . $this->id,
164
            config('laravel-acl.cacheMinutes'),
165
            function () {
166
                return $this->cacheGetInheritedPermissions();
167
            }
168
        );
169
    }
170
171 2
    /**
172
     * USER & LEVEL
173 2
     * First try the cache to return the collection,
174
     * then fetch it from the database.
175 2
     *
176 2
     * See @cacheGetAvailablePermissions()
177 2
     *
178
     * @return \Illuminate\Support\Collection
179 2
     */
180 2 View Code Duplication
    public function getAvailablePermissions()
1 ignored issue
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
181
    {
182
        return Cache::remember(
183
            'laravel-acl.getAvailablePermissionsFor' . (new ReflectionClass($this))->getShortName() . '_' . $this->id,
184
            config('laravel-acl.cacheMinutes'),
185
            function () {
186
                return $this->cacheGetAvailablePermissions();
187
            }
188
        );
189
    }
190
191
    /**
192
     * USER & LEVEL
193
     * First try the cache to return the collection,
194 2
     * then fetch it from the database.
195
     *
196 2
     * See @cacheHasPermissionTo()
197
     *
198 2
     * @param $permission
199 2
     * @return bool
200 2
     */
201
    public function hasPermissionTo($permission)
202 2
    {
203 2
        return Cache::remember(
204
            'laravel-acl.hasPermissionTo_' . (new ReflectionClass($this))->getShortName() . '_' . $this->id . '_Permission_' . $permission,
205
            config('laravel-acl.cacheMinutes'),
206
            function () use ($permission) {
207
                return $this->cacheHasPermissionTo($permission);
208
            }
209
        );
210
    }
211
212
    /**
213
     * USER & LEVEL
214
     * Returns if a level is lower ranking than another level,
215 3
     * or if a user is lower ranking than another user.
216
     *
217 3
     * @param $other
218 3
     * @return bool
219 3
     */
220
    public function isLowerThan($other)
221
    {
222
        return is_a($this, config('laravel-acl.user'), true) ? $this->level->rank > $other->level->rank : $this->rank > $other->rank;
223
    }
224
225
    /**
226
     * USER & LEVEL
227
     * Returns if a level is higher ranking than another level,
228
     * or if a user is higher ranking than another user.
229
     *
230 3
     * @param $other
231
     * @return bool
232 3
     */
233 3
    public function isHigherThan($other)
234 3
    {
235
        return is_a($this, config('laravel-acl.user'), true) ? $this->level->rank < $other->level->rank : $this->rank < $other->rank;
236
    }
237
238
    /**
239
     * USER & LEVEL
240
     * Returns if a equal is lower ranking than another level,
241
     * or if a user is equal ranking than another user.
242
     *
243
     * @param $other
244
     * @return bool
245 1
     */
246
    public function isEqualTo($other)
247 1
    {
248 1
        return is_a($this, config('laravel-acl.user'), true) ? $this->level->rank == $other->level->rank : $this->rank == $other->rank;
249 1
    }
250
251
252
    /* ------------------------------------------------------------------------------------------------
253
     |  Other Functions
254
     | ------------------------------------------------------------------------------------------------
255
     */
256
    /**
257
     * USER & LEVEL
258
     * Return a collection of permissions
259
     * assigned to a user
260
     *
261
     * @return \Illuminate\Support\Collection
262
     */
263
    protected function cacheGetPermissions()
264
    {
265 6
        return $this->permissions;
0 ignored issues
show
Bug introduced by
The property permissions does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
266
    }
267 6
268 3
    /**
269 6
     * USER & LEVEL
270
     * Return a collection of all permissions
271 6
     * associated to a user by direct or
272
     * inheritance
273 6
     *
274
     * @return \Illuminate\Support\Collection
275
     */
276 6
    protected function cacheGetAllPermissions()
277 6
    {
278
        $rank = is_a($this, config('laravel-acl.level'), true) ? $this->rank : $this->level->rank;
279 6
280
        $levels = config('laravel-acl.level', Level::class)::where('rank', '>=', $rank)->with('permissions')->get();
281
282
        $allPerms = new Collection();
283
284
        $levels->each(function ($level, $key) use (&$allPerms) {
0 ignored issues
show
Unused Code introduced by
The parameter $key is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
285
            $allPerms = $allPerms->merge($level->permissions);
286
        });
287
288 3
        return $allPerms->merge($this->permissions)->unique('id');
289
    }
290 3
291 1
    /**
292 3
     * USER & LEVEL
293
     * Return a collection of permissions that
294 3
     * are inherited from a higher level
295 2
     *
296 3
     * @return \Illuminate\Support\Collection
297
     */
298 3
    protected function cacheGetInheritedPermissions()
299
    {
300
        $rank = is_a($this, config('laravel-acl.level'), true) ? $this->rank : $this->level->rank;
301 3
302 3
        $levels = is_a($this, config('laravel-acl.user'), true) ?
303
            config('laravel-acl.level', Level::class)::where('rank', '>=', $rank)->with('permissions')->get() :
304 3
            config('laravel-acl.level', Level::class)::where('rank', '>', $rank)->with('permissions')->get();
305
306
        $inheritedPerms = new Collection();
307
308
        $levels->each(function ($level, $key) use (&$inheritedPerms) {
0 ignored issues
show
Unused Code introduced by
The parameter $key is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
309
            $inheritedPerms = $inheritedPerms->merge($level->permissions->load('level'));
310
        });
311
312
        return $inheritedPerms->unique('id');
313 2
    }
314
315 2
    /**
316
     * USER & LEVEL
317 2
     * Return a collection of permissions still able to be assigned
318
     * ie, not already inherited or explicitly assigned
319
     *
320
     * @return \Illuminate\Support\Collection
321
     */
322
    protected function cacheGetAvailablePermissions()
323
    {
324
        $allPerms = config('laravel-acl.permission', Permission::class)::with('level')->get();
325
326
        return $allPerms->diff($this->permissions);
327 2
    }
328
329 2
    /**
330
     * USER & LEVEL
331 2
     * Check if a model has permission to do something
332 1
     *
333
     * @param $permission
334 1
     * @return bool
335
     */
336
    protected function cacheHasPermissionTo($permission)
337
    {
338
        $permission = $this->getPermission($permission);
339 2
340 2
        if (is_a($this, config('laravel-acl.user'), true)) {
341
            $negatedPermissions = $this->permissions()->wherePivot('negated', true)->get();
0 ignored issues
show
Bug introduced by
The method permissions() does not exist on z1haze\Acl\Traits\UserAndLevel. Did you maybe mean addPermissions()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
342
343
            if ($this->getAllPermissions()->contains('id', $permission->id)) {
344
                return !$negatedPermissions->contains('id', $permission->id);
345
            }
346
        }
347
348
        return $this->getAllPermissions()->contains('id', $permission->id) ||
349
            $this->getAllPermissions()->contains('name', '*');
350
    }
351 23
352
    /**
353 23
     * USER & LEVEL
354
     * Modify a given set of permissions, given the action to be taken
355 23
     *
356 23
     * @param $permissions
357 23
     * @param $action
358
     */
359 23
    protected function modifyPermissions($permissions, $action)
360
    {
361 22
        Cache::forget('laravel-acl.getAvailablePermissionsFor' . (new ReflectionClass($this))->getShortName() . '_' . $this->id);
362 22
        Cache::forget('laravel-acl.getAllPermissionsFor' . (new ReflectionClass($this))->getShortName() . '_' . $this->id);
363
        Cache::forget('laravel-acl.getNegatedPermissionsForUser_' . $this->id);
364
365 22
        $permissionObjects = $this->buildPermissionsArray($permissions, $action);
366 3
367
        if ($action == 'add' || $action == 'negate') {
368
            $this->addOrNegate($permissionObjects, $action);
0 ignored issues
show
Unused Code introduced by
The call to UserAndLevel::addOrNegate() has too many arguments starting with $action.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
369 22
        }
370 2
371
        if ($action == 'remove') {
372 22
            $this->remove($permissionObjects);
373
        }
374
375
        if ($action == 'sync') {
376
            $this->sync($permissionObjects);
377
        }
378
    }
379
380
    /**
381
     * USER & LEVEL
382
     * Helper function to build the array
383
     * for modifyPermissions
384
     *
385 23
     * @param $permissions
386
     * @param $action
387 23
     * @return array
388
     * @throws PermissionNotFoundException
389 23
     */
390 23
    protected function buildPermissionsArray($permissions, $action)
391 22
    {
392
        $permissionsObjects = [];
393 22
394 2
        foreach ($permissions as $permission) {
395
            $permission = $this->getPermission($permission);
396 22
            Cache::forget('laravel-acl.hasPermissionTo_' . (new ReflectionClass($this))->getShortName() . '_' . $this->id . '_Permission_' . $permission->id);
397 12
398 22
            if ($action == 'negate') {
399
                $permissionsObjects[$permission->id] = ['negated' => true];
400
            } else {
401
                is_a($this, config('laravel-acl.user')) ?
402 22
                    array_push($permissionsObjects, $permission->id) :
403
                    array_push($permissionsObjects, $permission);
404
            }
405
        }
406
407
        return $permissionsObjects;
408
    }
409
410
    /**
411
     * USER & LEVEL
412 22
     * Helper function to handle adding or negating permissions
413
     *
414 22
     * @param $permissionObjects
415 12
     */
416
    protected function addOrNegate($permissionObjects)
417 22
    {
418 12
        if (is_a($this, config('laravel-acl.user')))
419 22
            $this->permissions()->attach($permissionObjects);
0 ignored issues
show
Bug introduced by
The method permissions() does not exist on z1haze\Acl\Traits\UserAndLevel. Did you maybe mean addPermissions()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
420
421
        if (is_a($this, config('laravel-acl.level')))
422
            $this->permissions()->saveMany($permissionObjects);
0 ignored issues
show
Bug introduced by
The method permissions() does not exist on z1haze\Acl\Traits\UserAndLevel. Did you maybe mean addPermissions()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
423
    }
424
425
    /**
426
     * USER & LEVEL
427
     * Helper function to handle removing permissions
428 3
     *
429
     * @param $permissionObjects
430 3
     */
431 2 View Code Duplication
    protected function remove($permissionObjects)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
432
    {
433 3
        if (is_a($this, config('laravel-acl.user')))
434
            $this->permissions()->detach($permissionObjects);
0 ignored issues
show
Bug introduced by
The method permissions() does not exist on z1haze\Acl\Traits\UserAndLevel. Did you maybe mean addPermissions()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
435 1
436 1
        if (is_a($this, config('laravel-acl.level'))) {
437
            config('laravel-acl.permission', Permission::class)::whereIn('id', array_map(function ($permission) {
438 3
                return $permission->id;
439
            }, $permissionObjects))->update(['level_id' => null]);
440
        }
441
    }
442
443
    /**
444
     * USER & LEVEL
445
     * Help function to handle syncing permissions
446
     *
447 2
     * @param $permissionObjects
448
     */
449 2 View Code Duplication
    protected function sync($permissionObjects)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
450 1
    {
451
        if (is_a($this, config('laravel-acl.user')))
452 2
            $this->permissions()->sync($permissionObjects);
0 ignored issues
show
Bug introduced by
The method permissions() does not exist on z1haze\Acl\Traits\UserAndLevel. Did you maybe mean addPermissions()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
453 1
454
        if (is_a($this, config('laravel-acl.level'))) {
455 1
            $this->clearPermissions();
456 1
457 1
            config('laravel-acl.permission', Permission::class)::whereIn('id', array_map(function ($permission) {
458 1
                return $permission->id;
459
            }, $permissionObjects))
460 2
                ->update(['level_id' => $this->id]);
461
        }
462
    }
463
464
    /**
465
     * USER & LEVEL
466
     * Helper function to get the permission whether it is the permission ID
467
     * or the permission name, or the permission object itself.
468
     *
469
     * @param $permission
470
     * @return \z1haze\Acl\Models\Permission
471 23
     * @throws PermissionNotFoundException
472
     */
473 23 View Code Duplication
    protected function getPermission($permission)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
474 5
    {
475
        if (is_string($permission))
476 23
            $permission = config('laravel-acl.permission', Permission::class)::whereName($permission)->first();
477 4
478
        if (is_int($permission))
479 23
            $permission = config('laravel-acl.permission', Permission::class)::find($permission);
480 1
481
        if (!$permission)
482 22
            throw new PermissionNotFoundException;
483
484
        return $permission;
485
    }
486
}