Completed
Push — master ( 8ec89a...5db323 )
by Arjay
14:29
created

HasRole::getRoleClass()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 8
ccs 0
cts 6
cp 0
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 0
crap 6
1
<?php
2
3
namespace Yajra\Acl\Traits;
4
5
use Yajra\Acl\Models\Role;
6
use Illuminate\Support\Str;
7
8
trait HasRole
9
{
10
    private $roleClass;
11
12
    /**
13
     * Check if user have access using any of the acl.
14
     *
15
     * @param array $acl
16
     * @return boolean
17
     */
18
    public function canAccess(array $acl)
19
    {
20
        return $this->canAtLeast($acl) || $this->hasRole($acl);
21
    }
22
23
    /**
24
     * Check if user has at least one of the given permissions
25
     *
26
     * @param array $permissions
27
     * @return bool
28
     */
29
    public function canAtLeast(array $permissions)
30
    {
31
        $can = false;
32
33
        if (auth()->check()) {
0 ignored issues
show
Bug introduced by
The method check does only exist in Illuminate\Contracts\Auth\Guard, but not in Illuminate\Contracts\Auth\Factory.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
34
            foreach ($this->roles as $role) {
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...
35
                if ($role->canAtLeast($permissions)) {
36
                    $can = true;
37
                }
38
            }
39
        } else {
40
            $guest = $this->getRoleClass()->whereSlug('guest')->first();
0 ignored issues
show
Documentation Bug introduced by
The method whereSlug does not exist on object<Yajra\Acl\Models\Role>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
41
42
            if ($guest) {
43
                return $guest->canAtLeast($permissions);
44
            }
45
        }
46
47
        return $can;
48
    }
49
50
    /**
51
     * Get Role class.
52
     *
53
     * @return Role
54
     */
55
    public function getRoleClass()
56
    {
57
        if (! isset($this->roleClass)) {
58
            $this->roleClass = resolve(config('acl.role'));
59
        }
60
61
        return $this->roleClass;
62
    }
63
64
    /**
65
     * Check if user has the given role.
66
     *
67
     * @param string|array $role
68
     * @return bool
69
     */
70
    public function hasRole($role)
71
    {
72
        if (is_string($role)) {
73
            return $this->roles->contains('slug', $role);
74
        }
75
76
        if (is_array($role)) {
77
            $roles = $this->getRoleSlugs();
78
79
            $intersection      = array_intersect($roles, (array) $role);
80
            $intersectionCount = count($intersection);
81
82
            return ($intersectionCount > 0) ? true : false;
83
        }
84
85
        return ! ! $role->intersect($this->roles)->count();
86
    }
87
88
    /**
89
     * Get all user roles.
90
     *
91
     * @return array|null
92
     */
93
    public function getRoleSlugs()
94
    {
95
        if (! is_null($this->roles)) {
96
            return $this->roles->pluck('slug')->toArray();
97
        }
98
99
        return null;
100
    }
101
102
    /**
103
     * Attach a role to user using slug.
104
     *
105
     * @param $slug
106
     * @return bool
107
     */
108
    public function attachRoleBySlug($slug)
109
    {
110
        $role = $this->getRoleClass()->where('slug', $slug)->first();
0 ignored issues
show
Documentation Bug introduced by
The method where does not exist on object<Yajra\Acl\Models\Role>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
111
112
        return $this->attachRole($role);
113
    }
114
115
    /**
116
     * Attach a role to user
117
     *
118
     * @param Role $role
119
     * @return boolean
120
     */
121
    public function attachRole(Role $role)
122
    {
123
        return $this->assignRole($role->id);
0 ignored issues
show
Documentation introduced by
The property id does not exist on object<Yajra\Acl\Models\Role>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
124
    }
125
126
    /**
127
     * Assigns the given role to the user.
128
     *
129
     * @param int $roleId
130
     * @return bool
131
     */
132
    public function assignRole($roleId = null)
133
    {
134
        $roles = $this->roles;
135
136
        if (! $roles->contains($roleId)) {
137
            return $this->roles()->attach($roleId);
138
        }
139
140
        return false;
141
    }
142
143
    /**
144
     * Model can have many roles.
145
     *
146
     * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
147
     */
148
    public function roles()
149
    {
150
        return $this->belongsToMany(config('acl.role', Role::class))->withTimestamps();
0 ignored issues
show
Documentation Bug introduced by
The method belongsToMany does not exist on object<Yajra\Acl\Traits\HasRole>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
151
    }
152
153
    /**
154
     * Query scope for user having the given roles.
155
     *
156
     * @param \Illuminate\Database\Eloquent\Builder $query
157
     * @param array $roles
158
     * @return mixed
159
     */
160
    public function scopeHavingRoles($query, array $roles)
161
    {
162
        return $query->whereExists(function ($query) use ($roles) {
163
            $query->selectRaw('1')
164
                  ->from('role_user')
165
                  ->whereRaw('role_user.user_id = users.id')
166
                  ->whereIn('role_id', $roles);
167
        });
168
    }
169
170
    /**
171
     * Revokes the given role from the user using slug.
172
     *
173
     * @param string $slug
174
     * @return bool
175
     */
176
    public function revokeRoleBySlug($slug)
177
    {
178
        $role = $this->getRoleClass()->where('slug', $slug)->first();
0 ignored issues
show
Documentation Bug introduced by
The method where does not exist on object<Yajra\Acl\Models\Role>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
179
180
        return $this->roles()->detach($role);
181
    }
182
183
    /**
184
     * Revokes the given role from the user.
185
     *
186
     * @param mixed $role
187
     * @return bool
188
     */
189
    public function revokeRole($role = "")
190
    {
191
        return $this->roles()->detach($role);
192
    }
193
194
    /**
195
     * Syncs the given role(s) with the user.
196
     *
197
     * @param array $roles
198
     * @return bool
199
     */
200
    public function syncRoles(array $roles)
201
    {
202
        return $this->roles()->sync($roles);
203
    }
204
205
    /**
206
     * Revokes all roles from the user.
207
     *
208
     * @return bool
209
     */
210
    public function revokeAllRoles()
211
    {
212
        return $this->roles()->detach();
213
    }
214
215
    /**
216
     * Get all user role permissions.
217
     *
218
     * @return array|null
219
     */
220
    public function getPermissions()
221
    {
222
        $permissions = [[], []];
223
224
        foreach ($this->roles as $role) {
225
            $permissions[] = $role->getPermissions();
226
        }
227
228
        return call_user_func_array('array_merge', $permissions);
229
    }
230
231
    /**
232
     * Magic __call method to handle dynamic methods.
233
     *
234
     * @param string $method
235
     * @param array $arguments
236
     * @return mixed
237
     */
238
    public function __call($method, $arguments = [])
239
    {
240
        // Handle isRoleSlug() methods
241
        if (Str::startsWith($method, 'is') and $method !== 'is') {
242
            $role = substr($method, 2);
243
244
            return $this->isRole($role);
245
        }
246
247
        // Handle canDoSomething() methods
248
        if (Str::startsWith($method, 'can') and $method !== 'can') {
249
            $permission = substr($method, 3);
250
251
            return $this->can($permission);
0 ignored issues
show
Documentation Bug introduced by
The method can does not exist on object<Yajra\Acl\Traits\HasRole>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
252
        }
253
254
        return parent::__call($method, $arguments);
255
    }
256
257
    /**
258
     * Checks if the user has the given role.
259
     *
260
     * @param string $slug
261
     * @return bool
262
     */
263
    public function isRole($slug)
264
    {
265
        $slug = Str::lower($slug);
266
267
        foreach ($this->roles as $role) {
268
            if ($role->slug == $slug) {
269
                return true;
270
            }
271
        }
272
273
        return false;
274
    }
275
276
    /**
277
     * Check if the given entity/model is owned by the user.
278
     *
279
     * @param \Illuminate\Database\Eloquent\Model $entity
280
     * @param string $relation
281
     * @return bool
282
     */
283
    public function owns($entity, $relation = 'user_id')
284
    {
285
        return $this->id === $entity->{$relation};
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...
286
    }
287
}
288