Completed
Push — master ( ce5ae0...137cbe )
by Arjay
15:06
created

HasRole   A

Complexity

Total Complexity 31

Size/Duplication

Total Lines 241
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 1

Test Coverage

Coverage 0%

Importance

Changes 4
Bugs 0 Features 3
Metric Value
wmc 31
c 4
b 0
f 3
lcom 2
cbo 1
dl 0
loc 241
ccs 0
cts 110
cp 0
rs 9.8

16 Methods

Rating   Name   Duplication   Size   Complexity  
A canAccess() 0 4 2
A canAtLeast() 0 12 3
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 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 owns() 0 4 1
A isRole() 0 12 3
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  string|array $acl
14
     * @return boolean
15
     */
16
    public function canAccess($acl)
17
    {
18
        return $this->canAtLeast($acl) || $this->hasRole($acl);
0 ignored issues
show
Bug introduced by
It seems like $acl defined by parameter $acl on line 16 can also be of type string; however, Yajra\Acl\Traits\HasRole::canAtLeast() does only seem to accept array, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
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
        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...
32
            if ($role->canAtLeast($permissions)) {
33
                $can = true;
34
            }
35
        }
36
37
        return $can;
38
    }
39
40
    /**
41
     * Check if user has the given role.
42
     *
43
     * @param $role
44
     * @return bool
45
     */
46
    public function hasRole($role)
47
    {
48
        if (is_string($role)) {
49
            return $this->roles->contains('name', $role);
50
        }
51
52
        if (is_array($role)) {
53
            $roles = $this->getRoleSlugs();
54
55
            $intersection      = array_intersect($roles, (array) $role);
56
            $intersectionCount = count($intersection);
57
58
            return ($intersectionCount > 0) ? true : false;
59
        }
60
61
        return ! ! $role->intersect($this->roles)->count();
62
    }
63
64
    /**
65
     * Get all user roles.
66
     *
67
     * @return array|null
68
     */
69
    public function getRoleSlugs()
70
    {
71
        if (! is_null($this->roles)) {
72
            return $this->roles->pluck('slug')->toArray();
73
        }
74
75
        return null;
76
    }
77
78
    /**
79
     * Attach a role to user using slug.
80
     *
81
     * @param $slug
82
     * @return bool
83
     */
84
    public function attachRoleBySlug($slug)
85
    {
86
        $role = Role::where('slug', $slug)->first();
87
88
        return $this->attachRole($role);
89
    }
90
91
    /**
92
     * Attach a role to user
93
     *
94
     * @param  Role $role
95
     * @return boolean
96
     */
97
    public function attachRole(Role $role)
98
    {
99
        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...
100
    }
101
102
    /**
103
     * Assigns the given role to the user.
104
     *
105
     * @param  int $roleId
106
     * @return bool
107
     */
108
    public function assignRole($roleId = null)
109
    {
110
        $roles = $this->roles;
111
112
        if (! $roles->contains($roleId)) {
113
            return $this->roles()->attach($roleId);
114
        }
115
116
        return false;
117
    }
118
119
    /**
120
     * Model can have many roles.
121
     *
122
     * @return \Illuminate\Database\Eloquent\Model
123
     */
124
    public function roles()
125
    {
126
        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...
127
    }
128
129
    /**
130
     * @param $query
131
     * @param array $roles
132
     * @return mixed
133
     */
134
    public function scopeHavingRoles($query, array $roles)
135
    {
136
        return $query->whereExists(function ($query) use ($roles) {
137
            $query->selectRaw('1')
138
                  ->from('role_user')
139
                  ->whereRaw('role_user.user_id = users.id')
140
                  ->whereIn('role_id', $roles);
141
        });
142
    }
143
144
    /**
145
     * Revokes the given role from the user.
146
     *
147
     * @param $role
148
     * @return bool
149
     */
150
    public function revokeRole($role = "")
151
    {
152
        return $this->roles()->detach($role);
153
    }
154
155
    /**
156
     * Syncs the given role(s) with the user.
157
     *
158
     * @param  array $roles
159
     * @return bool
160
     */
161
    public function syncRoles(array $roles)
162
    {
163
        return $this->roles()->sync($roles);
164
    }
165
166
    /**
167
     * Revokes all roles from the user.
168
     *
169
     * @return bool
170
     */
171
    public function revokeAllRoles()
172
    {
173
        return $this->roles()->detach();
174
    }
175
176
    /**
177
     * Get all user role permissions.
178
     *
179
     * @return array|null
180
     */
181
    public function getPermissions()
182
    {
183
        $permissions = [[], []];
184
185
        foreach ($this->roles as $role) {
186
            $permissions[] = $role->getPermissions();
187
        }
188
189
        return call_user_func_array('array_merge', $permissions);
190
    }
191
192
    /**
193
     * Magic __call method to handle dynamic methods.
194
     *
195
     * @param  string $method
196
     * @param  array $arguments
197
     * @return mixed
198
     */
199
    public function __call($method, $arguments = [])
200
    {
201
        // Handle isRoleSlug() methods
202
        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...
203
            $role = substr($method, 2);
204
205
            return $this->is($role);
0 ignored issues
show
Documentation Bug introduced by
The method is 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...
206
        }
207
208
        // Handle canDoSomething() methods
209
        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...
210
            $permission = substr($method, 3);
211
212
            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...
213
        }
214
215
        return parent::__call($method, $arguments);
216
    }
217
218
    /**
219
     * Checks if the user has the given role.
220
     *
221
     * @param  string $slug
222
     * @return bool
223
     */
224
    public function isRole($slug)
225
    {
226
        $slug = Str::lower($slug);
227
228
        foreach ($this->roles as $role) {
229
            if ($role->slug == $slug) {
230
                return true;
231
            }
232
        }
233
234
        return false;
235
    }
236
237
    /**
238
     * Check if the given entity/model is owned by the user.
239
     *
240
     * @param \Illuminate\Database\Eloquent\Model $entity
241
     * @param string $relation
242
     * @return bool
243
     */
244
    public function owns($entity, $relation = 'user_id')
245
    {
246
        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...
247
    }
248
}
249