Completed
Push — master ( a9d673...d0cde4 )
by Arjay
06:58
created

HasRole   A

Complexity

Total Complexity 34

Size/Duplication

Total Lines 264
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 1

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
wmc 34
c 0
b 0
f 0
lcom 2
cbo 1
dl 0
loc 264
ccs 0
cts 122
cp 0
rs 9.2

17 Methods

Rating   Name   Duplication   Size   Complexity  
A canAccess() 0 4 2
B canAtLeast() 0 20 5
A hasRole() 0 17 4
A getRoleSlugs() 0 8 2
A attachRoleBySlug() 0 6 1
A attachRole() 0 4 1
A assignRole() 0 10 2
A roles() 0 4 1
A scopeHavingRoles() 0 9 1
A revokeRoleBySlug() 0 6 1
A revokeRole() 0 4 1
A syncRoles() 0 4 1
A revokeAllRoles() 0 4 1
A getPermissions() 0 10 2
B __call() 0 18 5
A isRole() 0 12 3
A owns() 0 4 1
1
<?php
2
3
namespace Yajra\Acl\Traits;
4
5
use Illuminate\Support\Str;
6
use Yajra\Acl\Models\Role;
7
8
trait HasRole
9
{
10
    /**
11
     * Check if user have access using any of the acl.
12
     *
13
     * @param  array $acl
14
     * @return boolean
15
     */
16
    public function canAccess(array $acl)
17
    {
18
        return $this->canAtLeast($acl) || $this->hasRole($acl);
19
    }
20
21
    /**
22
     * Check if user has at least one of the given permissions
23
     *
24
     * @param  array $permissions
25
     * @return bool
26
     */
27
    public function canAtLeast(array $permissions)
28
    {
29
        $can = false;
30
31
        if (auth()->check()) {
32
            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...
33
                if ($role->canAtLeast($permissions)) {
34
                    $can = true;
35
                }
36
            }
37
        } else {
38
            $guest = Role::whereSlug('guest')->first();
39
40
            if ($guest) {
41
                return $guest->canAtLeast($permissions);
42
            }
43
        }
44
45
        return $can;
46
    }
47
48
    /**
49
     * Check if user has the given role.
50
     *
51
     * @param string|array $role
52
     * @return bool
53
     */
54
    public function hasRole($role)
55
    {
56
        if (is_string($role)) {
57
            return $this->roles->contains('name', $role);
58
        }
59
60
        if (is_array($role)) {
61
            $roles = $this->getRoleSlugs();
62
63
            $intersection      = array_intersect($roles, (array) $role);
64
            $intersectionCount = count($intersection);
65
66
            return ($intersectionCount > 0) ? true : false;
67
        }
68
69
        return ! ! $role->intersect($this->roles)->count();
70
    }
71
72
    /**
73
     * Get all user roles.
74
     *
75
     * @return array|null
76
     */
77
    public function getRoleSlugs()
78
    {
79
        if (! is_null($this->roles)) {
80
            return $this->roles->pluck('slug')->toArray();
81
        }
82
83
        return null;
84
    }
85
86
    /**
87
     * Attach a role to user using slug.
88
     *
89
     * @param $slug
90
     * @return bool
91
     */
92
    public function attachRoleBySlug($slug)
93
    {
94
        $role = Role::where('slug', $slug)->first();
95
96
        return $this->attachRole($role);
97
    }
98
99
    /**
100
     * Attach a role to user
101
     *
102
     * @param  Role $role
103
     * @return boolean
104
     */
105
    public function attachRole(Role $role)
106
    {
107
        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...
108
    }
109
110
    /**
111
     * Assigns the given role to the user.
112
     *
113
     * @param  int $roleId
114
     * @return bool
115
     */
116
    public function assignRole($roleId = null)
117
    {
118
        $roles = $this->roles;
119
120
        if (! $roles->contains($roleId)) {
121
            return $this->roles()->attach($roleId);
122
        }
123
124
        return false;
125
    }
126
127
    /**
128
     * Model can have many roles.
129
     *
130
     * @return \Illuminate\Database\Eloquent\Model
131
     */
132
    public function roles()
133
    {
134
        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...
135
    }
136
137
    /**
138
     * Query scope for user having the given roles.
139
     *
140
     * @param \Illuminate\Database\Eloquent\Builder $query
141
     * @param array $roles
142
     * @return mixed
143
     */
144
    public function scopeHavingRoles($query, array $roles)
145
    {
146
        return $query->whereExists(function ($query) use ($roles) {
147
            $query->selectRaw('1')
148
                  ->from('role_user')
149
                  ->whereRaw('role_user.user_id = users.id')
150
                  ->whereIn('role_id', $roles);
151
        });
152
    }
153
154
    /**
155
     * Revokes the given role from the user using slug.
156
     *
157
     * @param  string $slug
158
     * @return bool
159
     */
160
    public function revokeRoleBySlug($slug)
161
    {
162
        $role = Role::where('slug', $slug)->first();
163
164
        return $this->roles()->detach($role);
165
    }
166
167
    /**
168
     * Revokes the given role from the user.
169
     *
170
     * @param  mixed $role
171
     * @return bool
172
     */
173
    public function revokeRole($role = "")
174
    {
175
        return $this->roles()->detach($role);
176
    }
177
178
    /**
179
     * Syncs the given role(s) with the user.
180
     *
181
     * @param  array $roles
182
     * @return bool
183
     */
184
    public function syncRoles(array $roles)
185
    {
186
        return $this->roles()->sync($roles);
187
    }
188
189
    /**
190
     * Revokes all roles from the user.
191
     *
192
     * @return bool
193
     */
194
    public function revokeAllRoles()
195
    {
196
        return $this->roles()->detach();
197
    }
198
199
    /**
200
     * Get all user role permissions.
201
     *
202
     * @return array|null
203
     */
204
    public function getPermissions()
205
    {
206
        $permissions = [[], []];
207
208
        foreach ($this->roles as $role) {
209
            $permissions[] = $role->getPermissions();
210
        }
211
212
        return call_user_func_array('array_merge', $permissions);
213
    }
214
215
    /**
216
     * Magic __call method to handle dynamic methods.
217
     *
218
     * @param  string $method
219
     * @param  array $arguments
220
     * @return mixed
221
     */
222
    public function __call($method, $arguments = [])
223
    {
224
        // Handle isRoleSlug() methods
225
        if (Str::startsWith($method, 'is') and $method !== 'is') {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
226
            $role = substr($method, 2);
227
228
            return $this->isRole($role);
229
        }
230
231
        // Handle canDoSomething() methods
232
        if (Str::startsWith($method, 'can') and $method !== 'can') {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
233
            $permission = substr($method, 3);
234
235
            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...
236
        }
237
238
        return parent::__call($method, $arguments);
239
    }
240
241
    /**
242
     * Checks if the user has the given role.
243
     *
244
     * @param  string $slug
245
     * @return bool
246
     */
247
    public function isRole($slug)
248
    {
249
        $slug = Str::lower($slug);
250
251
        foreach ($this->roles as $role) {
252
            if ($role->slug == $slug) {
253
                return true;
254
            }
255
        }
256
257
        return false;
258
    }
259
260
    /**
261
     * Check if the given entity/model is owned by the user.
262
     *
263
     * @param \Illuminate\Database\Eloquent\Model $entity
264
     * @param string $relation
265
     * @return bool
266
     */
267
    public function owns($entity, $relation = 'user_id')
268
    {
269
        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...
270
    }
271
}
272