Passed
Pull Request — master (#153)
by
unknown
17:35
created

HasPermissions::permissionsQuery()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 2
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 4
ccs 1
cts 1
cp 1
crap 1
rs 10
1
<?php
2
3
namespace Maklad\Permission\Traits;
4
5
use Illuminate\Support\Collection;
6
use MongoDB\Laravel\Eloquent\Builder;
7
use MongoDB\Laravel\Eloquent\Model;
8
use Maklad\Permission\Contracts\PermissionInterface;
9
use Maklad\Permission\Contracts\PermissionInterface as Permission;
10
use Maklad\Permission\Exceptions\GuardDoesNotMatch;
11
use Maklad\Permission\Guard;
12
use Maklad\Permission\Helpers;
13
use Maklad\Permission\PermissionRegistrar;
14
use ReflectionException;
15
use function collect;
16
use function is_array;
17
use function is_string;
18
19
/**
20
 * Trait HasPermissions
21
 * @package Maklad\Permission\Traits
22
 */
23
trait HasPermissions
24 123
{
25 6
    private $permissionClass;
26 2
27
    public function getPermissionClass()
28
    {
29 4
        if ($this->permissionClass === null) {
30 123
            $this->permissionClass = app(PermissionRegistrar::class)->getPermissionClass();
31 123
        }
32
        return $this->permissionClass;
33
    }
34
35
    /**
36
     * Query the permissions
37 54
     */
38
    public function permissionsQuery()
39 54
    {
40
        $permission = $this->getPermissionClass();
41
        return $permission::whereIn('_id', $this->permission_ids ?? []);
42
    }
43
44
    /**
45 2
     * gets the permissions Attribute
46
     */
47 2
    public function getPermissionsAttribute()
48
    {
49
        return $this->permissionsQuery()->get();
50
    }
51
52
    /**
53
     * Grant the given permission(s) to a role.
54
     *
55
     * @param string|array|Permission|Collection $permissions
56
     *
57
     * @return $this
58 46
     * @throws GuardDoesNotMatch
59
     */
60 46
    public function givePermissionTo(...$permissions): self
61 46
    {
62 46
        $this->permission_ids = collect($this->permission_ids ?? [])
0 ignored issues
show
Bug Best Practice introduced by
The property permission_ids does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
Bug introduced by
It seems like $this->permission_ids ?? array() can also be of type array; however, parameter $value of collect() does only seem to accept Illuminate\Contracts\Support\Arrayable, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

62
        $this->permission_ids = collect(/** @scrutinizer ignore-type */ $this->permission_ids ?? [])
Loading history...
63 44
            ->merge($this->getPermissionIds($permissions))
0 ignored issues
show
Bug introduced by
$this->getPermissionIds($permissions) of type array is incompatible with the type Illuminate\Contracts\Support\Arrayable expected by parameter $items of Illuminate\Support\Collection::merge(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

63
            ->merge(/** @scrutinizer ignore-type */ $this->getPermissionIds($permissions))
Loading history...
64 46
            ->all();
65 44
66 42
        $this->save();
0 ignored issues
show
Bug introduced by
It seems like save() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

66
        $this->/** @scrutinizer ignore-call */ 
67
               save();
Loading history...
67 44
68 41
        $this->forgetCachedPermissions();
69
70 41
        return $this;
71
    }
72 41
73
    /**
74 41
     * Remove all current permissions and set the given ones.
75
     *
76
     * @param string|array|Permission|Collection $permissions
77
     *
78
     * @return $this
79
     * @throws GuardDoesNotMatch
80
     */
81
    public function syncPermissions(...$permissions): self
82
    {
83
        $this->permission_ids = $this->getPermissionIds($permissions);
0 ignored issues
show
Bug Best Practice introduced by
The property permission_ids does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
84
85 4
        $this->save();
86
        return $this->givePermissionTo($permissions);
87 4
    }
88
89 4
    /**
90
     * Revoke the given permission.
91
     *
92
     * @param string|array|Permission|Collection $permissions
93
     *
94
     * @return $this
95
     * @throws GuardDoesNotMatch
96
     */
97
    public function revokePermissionTo(...$permissions): self
98
    {
99
        $permissions = $this->getPermissionIds($permissions);
100 6
101
        $this->permission_ids = collect($this->permission_ids ?? [])
0 ignored issues
show
Bug introduced by
It seems like $this->permission_ids ?? array() can also be of type array; however, parameter $value of collect() does only seem to accept Illuminate\Contracts\Support\Arrayable, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

101
        $this->permission_ids = collect(/** @scrutinizer ignore-type */ $this->permission_ids ?? [])
Loading history...
Bug Best Practice introduced by
The property permission_ids does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
102 6
            ->filter(function ($permission) use ($permissions) {
103 6
                return ! in_array($permission, $permissions, true);
104 6
            })
105 6
            ->all();
106 6
107
        $this->save();
108 6
109 6
        $this->forgetCachedPermissions();
110
111 6
        return $this;
112
    }
113 6
114
    /**
115
     * @param string|Permission $permission
116
     *
117
     * @return Permission
118
     * @throws ReflectionException
119
     */
120
    protected function getStoredPermission($permission): Permission
121
    {
122 45
        if (is_string($permission)) {
123
            return $this->getPermissionClass()->findByName($permission, $this->getDefaultGuardName());
124 45
        }
125 32
126
        return $permission;
127
    }
128 16
129
    /**
130
     * @param Model $roleOrPermission
131
     *
132
     * @throws GuardDoesNotMatch
133
     * @throws ReflectionException
134
     */
135
    protected function ensureModelSharesGuard(Model $roleOrPermission): void
136
    {
137 83
        if (! $this->getGuardNames()->contains($roleOrPermission->guard_name)) {
0 ignored issues
show
Bug introduced by
The property guard_name does not exist on MongoDB\Laravel\Eloquent\Model. Did you mean guarded?
Loading history...
138
            $expected = $this->getGuardNames();
139 83
            $given    = $roleOrPermission->guard_name;
140 5
            $helpers  = new Helpers();
141 5
142 5
            throw new GuardDoesNotMatch($helpers->getGuardDoesNotMatchMessage($expected, $given));
143
        }
144 5
    }
145
146 79
    /**
147
     * @return Collection
148
     * @throws ReflectionException
149
     */
150
    protected function getGuardNames(): Collection
151
    {
152 86
        return (new Guard())->getNames($this);
153
    }
154 86
155
    /**
156
     * @return string
157
     * @throws ReflectionException
158
     */
159
    protected function getDefaultGuardName(): string
160
    {
161 78
        return (new Guard())->getDefaultName($this);
162
    }
163 78
164
    /**
165
     * Forget the cached permissions.
166
     */
167
    public function forgetCachedPermissions(): void
168
    {
169 80
        app(PermissionRegistrar::class)->forgetCachedPermissions();
170
    }
171 80
172 80
    /**
173
     * Convert to Permission Models
174
     *
175
     * @param array|string|Collection $permissions
176
     *
177
     * @return Collection
178
     */
179
    private function convertToPermissionModels($permissions): Collection
180
    {
181 7
        if (is_array($permissions)) {
182
            $permissions = collect($permissions);
0 ignored issues
show
Bug introduced by
$permissions of type array is incompatible with the type Illuminate\Contracts\Support\Arrayable expected by parameter $value of collect(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

182
            $permissions = collect(/** @scrutinizer ignore-type */ $permissions);
Loading history...
183 7
        }
184 3
185
        if (! $permissions instanceof Collection) {
186
            $permissions = collect([$permissions]);
0 ignored issues
show
Bug introduced by
array($permissions) of type array<integer,string> is incompatible with the type Illuminate\Contracts\Support\Arrayable expected by parameter $value of collect(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

186
            $permissions = collect(/** @scrutinizer ignore-type */ [$permissions]);
Loading history...
187 7
        }
188 5
189
        return $permissions->map(function ($permission) {
190
            return $this->getStoredPermission($permission);
191 7
        });
192 7
    }
193 7
194
    /**
195 6
     * Return a collection of permission names associated with this user.
196
     *
197
     * @return Collection
198
     */
199
    public function getPermissionNames(): Collection
200
    {
201
        return $this->getAllPermissions()->pluck('name');
202
    }
203 1
204
    /**
205 1
     * Return all the permissions the model has via roles.
206
     */
207
    public function getPermissionsViaRoles(): Collection
208
    {
209
        $permissionIds = $this->roles->pluck('permission_ids')->flatten()->unique()->values();
210
        return $this->getPermissionClass()->query()->whereIn('_id', $permissionIds)->get();
211 3
        /*return $this->load('roles', 'roles.permissions')
212
            ->roles->flatMap(function (Role $role) {
213 3
                return $role->permissions;
214 3
            })->sort()->values();*/
215 2
    }
216 3
217
    /**
218
     * Return all the permissions the model has, both directly and via roles.
219
     */
220
    public function getAllPermissions(): Collection
221
    {
222 2
        return $this->permissions
0 ignored issues
show
Bug introduced by
The property permissions does not exist on Maklad\Permission\Traits\HasPermissions. Did you mean permission_ids?
Loading history...
223
            ->merge($this->getPermissionsViaRoles())
224 2
            ->sort()
225 2
            ->values();
226 2
    }
227 2
228
    /**
229
     * Determine if the model may perform the given permission.
230
     *
231
     * @param string|PermissionInterface $permission
232
     * @param string|null $guardName
233
     * @return bool
234
     * @throws ReflectionException
235
     */
236
    public function hasPermissionTo($permission, string $guardName = null): bool
237
    {
238
        if (is_string($permission)) {
239 24
            $permission = $this->getPermissionClass()->findByName(
240
                $permission,
241 24
                $guardName ?? $this->getDefaultGuardName()
242 15
            );
243 15
        }
244 15
245
        return $this->hasDirectPermission($permission) || $this->hasPermissionViaRole($permission);
246
    }
247
248 22
    /**
249
     * Determine if the model has any of the given permissions.
250
     *
251
     * @param array ...$permissions
252
     *
253
     * @return bool
254
     * @throws ReflectionException
255
     */
256
    public function hasAnyPermission(...$permissions): bool
257
    {
258
        if (is_array($permissions[0])) {
259 8
            $permissions = $permissions[0];
260
        }
261 8
262 6
        foreach ($permissions as $permission) {
263
            if ($this->hasPermissionTo($permission)) {
264
                return true;
265 8
            }
266 8
        }
267 8
268
        return false;
269
    }
270
271 5
    /**
272
     * Determine if the model has all the given permissions(s).
273
     *
274
     * @param $permissions
275
     *
276
     * @return bool
277
     * @throws ReflectionException
278
     */
279
    public function hasAllPermissions(...$permissions): bool
280
    {
281 17
        $helpers = new Helpers();
282
        $permissions = $helpers->flattenArray($permissions);
283 17
284
        foreach ($permissions as $permission) {
285
            if (!$this->hasPermissionTo($permission)) {
286
                return false;
287
            }
288
        }
289
        return true;
290
    }
291
292
    /**
293
     * Determine if the model has, via roles, the given permission.
294 23
     *
295
     * @param Permission $permission
296 23
     *
297 1
     * @return bool
298
     */
299
    protected function hasPermissionViaRole(Permission $permission): bool
300 23
    {
301
        return $this->hasRole($permission->roles);
0 ignored issues
show
Bug introduced by
Accessing roles on the interface Maklad\Permission\Contracts\PermissionInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
Bug introduced by
It seems like hasRole() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

301
        return $this->/** @scrutinizer ignore-call */ hasRole($permission->roles);
Loading history...
302
    }
303
304
    /**
305
     * Determine if the model has the given permission.
306 1
     *
307
     * @param string|Permission $permission
308 1
     *
309
     * @return bool
310
     * @throws ReflectionException
311
     */
312
    public function hasDirectPermission($permission): bool
313
    {
314
        if (is_string($permission)) {
315
            $permission = $this->getPermissionClass()->findByName($permission, $this->getDefaultGuardName());
316
        }
317
318
        return $this->permissions->contains('_id', $permission->_id);
0 ignored issues
show
Bug introduced by
The property permissions does not exist on Maklad\Permission\Traits\HasPermissions. Did you mean permission_ids?
Loading history...
319 7
    }
320
321 7
    /**
322
     * Return all permissions the directory coupled to the model.
323 6
     */
324
    public function getDirectPermissions(): Collection
325 6
    {
326 6
        return $this->permissions;
0 ignored issues
show
Bug introduced by
The property permissions does not exist on Maklad\Permission\Traits\HasPermissions. Did you mean permission_ids?
Loading history...
327
    }
328 6
329
    /**
330 6
     * Scope the model query to certain permissions only.
331 6
     *
332
     * @param Builder $query
333
     * @param array|string|Permission|Collection $permissions
334
     *
335
     * @return Builder
336
     */
337
    public function scopePermission(Builder $query, $permissions): Builder
338
    {
339
        $permissions = $this->convertToPermissionModels($permissions);
340
341
        $roles = collect([]);
0 ignored issues
show
Bug introduced by
array() of type array is incompatible with the type Illuminate\Contracts\Support\Arrayable expected by parameter $value of collect(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

341
        $roles = collect(/** @scrutinizer ignore-type */ []);
Loading history...
342
343
        foreach ($permissions as $permission) {
344
            $roles = $roles->merge($permission->roles);
345
        }
346
        $roles = $roles->unique();
347
348
        return $query->orWhereIn('permission_ids', $permissions->pluck('_id'))
349
            ->orWhereIn('role_ids', $roles->pluck('_id'));
350
    }
351
352
    /**
353
     * @param string|array|Permission|Collection $permissions
354
     * @return array
355
     */
356
    protected function getPermissionIds(...$permissions): array
357
    {
358
        return collect($permissions)
359
            ->flatten()
360
            ->map(function ($permission) {
361
                $permission = $this->getStoredPermission($permission);
362
                $this->ensureModelSharesGuard($permission);
363
                return $permission->_id;
364
            })
365
            ->all();
366
    }
367
}
368