Completed
Push — master ( eb78c6...4dc200 )
by Freek
01:46
created

HasRoles::hasAnyRole()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
c 0
b 0
f 0
rs 10
cc 1
eloc 2
nc 1
nop 1
1
<?php
2
3
namespace Spatie\Permission\Traits;
4
5
use Illuminate\Support\Collection;
6
use Spatie\Permission\Contracts\Permission;
7
use Spatie\Permission\Contracts\Role;
8
9
trait HasRoles
10
{
11
    use HasPermissions;
12
    use RefreshesPermissionCache;
13
14
    /**
15
     * A user may have multiple roles.
16
     *
17
     * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
18
     */
19
    public function roles()
20
    {
21
        return $this->belongsToMany(
0 ignored issues
show
Bug introduced by
It seems like belongsToMany() 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...
22
            config('laravel-permission.models.role'),
23
            config('laravel-permission.table_names.user_has_roles')
24
        );
25
    }
26
27
    /**
28
     * A user may have multiple direct permissions.
29
     *
30
     * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
31
     */
32
    public function permissions()
33
    {
34
        return $this->belongsToMany(
0 ignored issues
show
Bug introduced by
It seems like belongsToMany() 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...
35
            config('laravel-permission.models.permission'),
36
            config('laravel-permission.table_names.user_has_permissions')
37
        );
38
    }
39
40
    /**
41
     * Scope the user query to certain roles only.
42
     *
43
     * @param string|array|Role|\Illuminate\Support\Collection $roles
44
     *
45
     * @return bool
46
     */
47
    public function scopeRole($query, $roles)
48
    {
49
        if ($roles instanceof Collection) {
50
            $roles = $roles->toArray();
51
        }
52
53
        if (! is_array($roles)) {
54
            $roles = [$roles];
55
        }
56
57
        $roles = array_map(function ($role) {
58
            if ($role instanceof Role) {
59
                return $role;
60
            }
61
62
            return app(Role::class)->findByName($role);
63
        }, $roles);
64
        
65
        return $query->whereHas('roles', function ($query) use ($roles) {
66
            $query->where(function ($query) use ($roles) {
67
                foreach ($roles as $role) {
68
                    $query->orWhere('id', $role->id);
69
                }
70
            });
71
        });
72
73
        return $query;
0 ignored issues
show
Unused Code introduced by
return $query; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
74
    }
75
76
    /**
77
     * Assign the given role to the user.
78
     *
79
     * @param array|string|\Spatie\Permission\Models\Role ...$roles
80
     *
81
     * @return \Spatie\Permission\Contracts\Role
82
     */
83
    public function assignRole(...$roles)
84
    {
85
        $roles = collect($roles)
86
            ->flatten()
87
            ->map(function ($role) {
88
                return $this->getStoredRole($role);
89
            })
90
            ->all();
91
92
        $this->roles()->saveMany($roles);
93
94
        $this->forgetCachedPermissions();
95
96
        return $this;
97
    }
98
99
    /**
100
     * Revoke the given role from the user.
101
     *
102
     * @param string|Role $role
103
     */
104
    public function removeRole($role)
105
    {
106
        $this->roles()->detach($this->getStoredRole($role));
107
    }
108
109
    /**
110
     * Remove all current roles and set the given ones.
111
     *
112
     * @param array ...$roles
113
     *
114
     * @return $this
115
     */
116
    public function syncRoles(...$roles)
117
    {
118
        $this->roles()->detach();
119
120
        return $this->assignRole($roles);
121
    }
122
123
    /**
124
     * Determine if the user has (one of) the given role(s).
125
     *
126
     * @param string|array|Role|\Illuminate\Support\Collection $roles
127
     *
128
     * @return bool
129
     */
130
    public function hasRole($roles)
131
    {
132
        if (is_string($roles)) {
133
            return $this->roles->contains('name', $roles);
0 ignored issues
show
Bug introduced by
The property roles 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...
134
        }
135
136
        if ($roles instanceof Role) {
137
            return $this->roles->contains('id', $roles->id);
0 ignored issues
show
Bug introduced by
Accessing id on the interface Spatie\Permission\Contracts\Role suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
138
        }
139
140
        if (is_array($roles)) {
141
            foreach ($roles as $role) {
142
                if ($this->hasRole($role)) {
143
                    return true;
144
                }
145
            }
146
147
            return false;
148
        }
149
150
        return (bool)$roles->intersect($this->roles)->count();
151
    }
152
153
    /**
154
     * Determine if the user has any of the given role(s).
155
     *
156
     * @param string|array|Role|\Illuminate\Support\Collection $roles
157
     *
158
     * @return bool
159
     */
160
    public function hasAnyRole($roles)
161
    {
162
        return $this->hasRole($roles);
163
    }
164
165
    /**
166
     * Determine if the user has all of the given role(s).
167
     *
168
     * @param string|Role|\Illuminate\Support\Collection $roles
169
     *
170
     * @return bool
171
     */
172
    public function hasAllRoles($roles)
173
    {
174
        if (is_string($roles)) {
175
            return $this->roles->contains('name', $roles);
176
        }
177
178
        if ($roles instanceof Role) {
179
            return $this->roles->contains('id', $roles->id);
0 ignored issues
show
Bug introduced by
Accessing id on the interface Spatie\Permission\Contracts\Role suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
180
        }
181
182
        $roles = collect()->make($roles)->map(function ($role) {
183
            return $role instanceof Role ? $role->name : $role;
0 ignored issues
show
Bug introduced by
Accessing name on the interface Spatie\Permission\Contracts\Role suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
184
        });
185
186
        return $roles->intersect($this->roles->pluck('name')) == $roles;
187
    }
188
189
    /**
190
     * Determine if the user may perform the given permission.
191
     *
192
     * @param string|Permission $permission
193
     *
194
     * @return bool
195
     */
196
    public function hasPermissionTo($permission)
197
    {
198
        if (is_string($permission)) {
199
            $permission = app(Permission::class)->findByName($permission);
200
        }
201
202
        return $this->hasDirectPermission($permission) || $this->hasPermissionViaRole($permission);
203
    }
204
205
    /**
206
     * @deprecated deprecated since version 1.0.1, use hasPermissionTo instead
207
     *
208
     * Determine if the user may perform the given permission.
209
     *
210
     * @param Permission $permission
211
     *
212
     * @return bool
213
     */
214
    public function hasPermission($permission)
215
    {
216
        return $this->hasPermissionTo($permission);
217
    }
218
219
    /**
220
     * Determine if the user has, via roles, the given permission.
221
     *
222
     * @param Permission $permission
223
     *
224
     * @return bool
225
     */
226
    protected function hasPermissionViaRole(Permission $permission)
227
    {
228
        return $this->hasRole($permission->roles);
0 ignored issues
show
Bug introduced by
Accessing roles on the interface Spatie\Permission\Contracts\Permission suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
229
    }
230
231
    /**
232
     * Determine if the user has the given permission.
233
     *
234
     * @param string|Permission $permission
235
     *
236
     * @return bool
237
     */
238
    protected function hasDirectPermission($permission)
239
    {
240
        if (is_string($permission)) {
241
            $permission = app(Permission::class)->findByName($permission);
242
243
            if (!$permission) {
244
                return false;
245
            }
246
        }
247
248
        return $this->permissions->contains('id', $permission->id);
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...
249
    }
250
251
    /**
252
     * @param $role
253
     *
254
     * @return Role
255
     */
256
    protected function getStoredRole($role)
257
    {
258
        if (is_string($role)) {
259
            return app(Role::class)->findByName($role);
260
        }
261
262
        return $role;
263
    }
264
}
265