Completed
Push — master ( 20e9a9...2bf64c )
by Freek
02:11
created

HasRoles::syncRoles()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 12
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 12
rs 9.4285
cc 1
eloc 8
nc 1
nop 1
1
<?php
2
3
namespace Spatie\Permission\Traits;
4
5
use Spatie\Permission\Contracts\Permission;
6
use Spatie\Permission\Contracts\Role;
7
8
trait HasRoles
9
{
10
    use HasPermissions;
11
    use RefreshesPermissionCache;
12
13
    /**
14
     * A user may have multiple roles.
15
     *
16
     * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
17
     */
18
    public function roles()
19
    {
20
        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...
21
            config('laravel-permission.models.role'),
22
            config('laravel-permission.table_names.user_has_roles')
23
        );
24
    }
25
26
    /**
27
     * A user may have multiple direct permissions.
28
     *
29
     * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
30
     */
31
    public function permissions()
32
    {
33
        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...
34
            config('laravel-permission.models.permission'),
35
            config('laravel-permission.table_names.user_has_permissions')
36
        );
37
    }
38
39
    /**
40
     * Assign the given role to the user.
41
     *
42
     * @param string|Role $role
43
     *
44
     * @return Role
45
     */
46
    public function assignRole($role)
47
    {
48
        $this->roles()->save($this->getStoredRole($role));
0 ignored issues
show
Documentation introduced by
$this->getStoredRole($role) is of type object<Spatie\Permission\Contracts\Role>, but the function expects a object<Illuminate\Database\Eloquent\Model>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
49
    }
50
51
    /**
52
     * Revoke the given role from the user.
53
     *
54
     * @param string|Role $role
55
     *
56
     * @return mixed
57
     */
58
    public function removeRole($role)
59
    {
60
        $this->roles()->detach($this->getStoredRole($role));
0 ignored issues
show
Documentation introduced by
$this->getStoredRole($role) is of type object<Spatie\Permission\Contracts\Role>, but the function expects a integer|array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
61
    }
62
63
    /**
64
     * Sync the given roles.
65
     *
66
     * @param array ...$roles
67
     */
68
    public function syncRoles(...$roles)
69
    {
70
        $this->roles()->detach();
71
72
        collect($roles)
73
            ->flatten()
74
            ->map(function ($role) {
75
                return $this->getStoredRole($role);
76
            })->each(function (Role $role) {
77
                $this->roles()->save($role);
0 ignored issues
show
Documentation introduced by
$role is of type object<Spatie\Permission\Contracts\Role>, but the function expects a object<Illuminate\Database\Eloquent\Model>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
78
            });
79
    }
80
81
    /**
82
     * Determine if the user has (one of) the given role(s).
83
     *
84
     * @param string|array|Role|\Illuminate\Support\Collection $roles
85
     *
86
     * @return bool
87
     */
88
    public function hasRole($roles)
89
    {
90
        if (is_string($roles)) {
91
            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...
92
        }
93
94
        if ($roles instanceof Role) {
95
            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...
96
        }
97
98
        if (is_array($roles)) {
99
            foreach ($roles as $role) {
100
                if ($this->hasRole($role)) {
101
                    return true;
102
                }
103
            }
104
105
            return false;
106
        }
107
108
        return (bool) $roles->intersect($this->roles)->count();
109
    }
110
111
    /**
112
     * Determine if the user has any of the given role(s).
113
     *
114
     * @param string|array|Role|\Illuminate\Support\Collection $roles
115
     *
116
     * @return bool
117
     */
118
    public function hasAnyRole($roles)
119
    {
120
        return $this->hasRole($roles);
121
    }
122
123
    /**
124
     * Determine if the user has all of the given role(s).
125
     *
126
     * @param string|Role|\Illuminate\Support\Collection $roles
127
     *
128
     * @return bool
129
     */
130
    public function hasAllRoles($roles)
131
    {
132
        if (is_string($roles)) {
133
            return $this->roles->contains('name', $roles);
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
        $roles = collect()->make($roles)->map(function ($role) {
141
            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...
142
        });
143
144
        return $roles->intersect($this->roles->lists('name')) == $roles;
145
    }
146
147
    /**
148
     * Determine if the user may perform the given permission.
149
     *
150
     * @param string|Permission $permission
151
     *
152
     * @return bool
153
     */
154
    public function hasPermissionTo($permission)
155
    {
156
        if (is_string($permission)) {
157
            $permission = app(Permission::class)->findByName($permission);
158
        }
159
160
        return $this->hasDirectPermission($permission) || $this->hasPermissionViaRole($permission);
161
    }
162
163
    /**
164
     * @deprecated deprecated since version 1.0.1, use hasPermissionTo instead
165
     *
166
     * Determine if the user may perform the given permission.
167
     *
168
     * @param Permission $permission
169
     *
170
     * @return bool
171
     */
172
    public function hasPermission($permission)
173
    {
174
        return $this->hasPermissionTo($permission);
175
    }
176
177
    /**
178
     * Determine if the user has, via roles, the given permission.
179
     *
180
     * @param Permission $permission
181
     *
182
     * @return bool
183
     */
184
    protected function hasPermissionViaRole(Permission $permission)
185
    {
186
        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...
187
    }
188
189
    /**
190
     * Determine if the user has the given permission.
191
     *
192
     * @param string|Permission $permission
193
     *
194
     * @return bool
195
     */
196
    protected function hasDirectPermission($permission)
197
    {
198
        if (is_string($permission)) {
199
            $permission = app(Permission::class)->findByName($permission);
200
201
            if (!$permission) {
202
                return false;
203
            }
204
        }
205
206
        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...
207
    }
208
209
    /**
210
     * @param $role
211
     *
212
     * @return Role
213
     */
214
    protected function getStoredRole($role)
215
    {
216
        if (is_string($role)) {
217
            return app(Role::class)->findByName($role);
218
        }
219
220
        return $role;
221
    }
222
}
223