Skip to content

Commit

Permalink
Added permission scope to HasRoles trait (#454)
Browse files Browse the repository at this point in the history
* Added permission scope to HasRoles trait

* Fix CI notes

* Fix CI notes

* Fix CI notes

* Fix CI notes

* CR Fix

* Fix CI Notes

* Fix CI Notes

* CR fix #2

* Add documentation
  • Loading branch information
idoadiv authored and freekmurze committed Sep 10, 2017
1 parent c975ab2 commit 8b57afd
Show file tree
Hide file tree
Showing 3 changed files with 162 additions and 1 deletion.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -225,10 +225,12 @@ $permissions = $user->getAllPermissions();
$roles = $user->getRoleNames(); // Returns a collection
```

The `HasRoles` trait also adds a scope to your models to scope the query to certain roles:
The `HasRoles` trait also adds a scope to your models to scope the query to certain roles or permissions:

```php
$users = User::role('writer')->get(); // Returns only users with the role 'writer'

$users = User::permission('edit articles')->get(); // Returns only users with the permission 'edit articles' (inherited or directly)
```

The scope can accept a string, a `\Spatie\Permission\Models\Role` object or an `\Illuminate\Support\Collection` object.
Expand Down
59 changes: 59 additions & 0 deletions src/Traits/HasRoles.php
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,65 @@ public function scopeRole(Builder $query, $roles): Builder
});
}

/**
* @param string|array|\Spatie\Permission\Contracts\Permission|\Illuminate\Support\Collection $permissions
*
* @return array
*/
protected function convertToPermissionModels($permissions): array
{
if ($permissions instanceof Collection) {
$permissions = $permissions->toArray();
}

$permissions = array_wrap($permissions);

return array_map(function ($permission) {
if ($permission instanceof Permission) {
return $permission;
}

return app(Permission::class)->findByName($permission, $this->getDefaultGuardName());
}, $permissions);
}

/**
* Scope the model query to certain permissions only.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param string|array|\Spatie\Permission\Contracts\Permission|\Illuminate\Support\Collection $permissions
*
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopePermission(Builder $query, $permissions): Builder
{
$permissions = $this->convertToPermissionModels($permissions);

$rolesWithPermissions = array_unique(array_reduce($permissions, function ($result, $permission) {
return array_merge($result, $permission->roles->all());
}, []));

return $query->
where(function ($query) use ($permissions, $rolesWithPermissions) {
$query->whereHas('permissions', function ($query) use ($permissions) {
$query->where(function ($query) use ($permissions) {
foreach ($permissions as $permission) {
$query->orWhere(config('permission.table_names.permissions').'.id', $permission->id);
}
});
});
if (count($rolesWithPermissions) > 0) {
$query->orWhereHas('roles', function ($query) use ($rolesWithPermissions) {
$query->where(function ($query) use ($rolesWithPermissions) {
foreach ($rolesWithPermissions as $role) {
$query->orWhere(config('permission.table_names.roles').'.id', $role->id);
}
});
});
}
});
}

/**
* Assign the given role to the model.
*
Expand Down
100 changes: 100 additions & 0 deletions tests/HasPermissionsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,104 @@ public function it_can_revoke_a_permission_from_a_user()

$this->assertFalse($this->testUser->hasPermissionTo($this->testUserPermission));
}

/** @test */
public function it_can_scope_users_using_a_string()
{
$user1 = User::create(['email' => 'user1@test.com']);
$user2 = User::create(['email' => 'user2@test.com']);
$user1->givePermissionTo(['edit-articles', 'edit-news']);
$this->testUserRole->givePermissionTo('edit-articles');
$user2->assignRole('testRole');

$scopedUsers1 = User::permission('edit-articles')->get();
$scopedUsers2 = User::permission(['edit-news'])->get();

$this->assertEquals($scopedUsers1->count(), 2);
$this->assertEquals($scopedUsers2->count(), 1);
}

/** @test */
public function it_can_scope_users_using_an_array()
{
$user1 = User::create(['email' => 'user1@test.com']);
$user2 = User::create(['email' => 'user2@test.com']);
$user1->givePermissionTo(['edit-articles', 'edit-news']);
$this->testUserRole->givePermissionTo('edit-articles');
$user2->assignRole('testRole');

$scopedUsers1 = User::permission(['edit-articles', 'edit-news'])->get();
$scopedUsers2 = User::permission(['edit-news'])->get();

$this->assertEquals($scopedUsers1->count(), 2);
$this->assertEquals($scopedUsers2->count(), 1);
}

/** @test */
public function it_can_scope_users_using_a_collection()
{
$user1 = User::create(['email' => 'user1@test.com']);
$user2 = User::create(['email' => 'user2@test.com']);
$user1->givePermissionTo(['edit-articles', 'edit-news']);
$this->testUserRole->givePermissionTo('edit-articles');
$user2->assignRole('testRole');

$scopedUsers1 = User::permission(collect(['edit-articles', 'edit-news']))->get();
$scopedUsers2 = User::permission(collect(['edit-news']))->get();

$this->assertEquals($scopedUsers1->count(), 2);
$this->assertEquals($scopedUsers2->count(), 1);
}

/** @test */
public function it_can_scope_users_using_an_object()
{
$user1 = User::create(['email' => 'user1@test.com']);
$user1->givePermissionTo($this->testUserPermission->name);

$scopedUsers1 = User::permission($this->testUserPermission)->get();
$scopedUsers2 = User::permission([$this->testUserPermission])->get();

$this->assertEquals($scopedUsers1->count(), 1);
$this->assertEquals($scopedUsers2->count(), 1);
}

/** @test */
public function it_can_scope_users_without_permissions_only_role()
{
$user1 = User::create(['email' => 'user1@test.com']);
$user2 = User::create(['email' => 'user2@test.com']);
$this->testUserRole->givePermissionTo('edit-articles');
$user1->assignRole('testRole');
$user2->assignRole('testRole');

$scopedUsers = User::permission('edit-articles')->get();

$this->assertEquals($scopedUsers->count(), 2);
}

/** @test */
public function it_can_scope_users_without_permissions_only_permission()
{
$user1 = User::create(['email' => 'user1@test.com']);
$user2 = User::create(['email' => 'user2@test.com']);
$user1->givePermissionTo(['edit-news']);
$user2->givePermissionTo(['edit-articles', 'edit-news']);

$scopedUsers = User::permission('edit-news')->get();

$this->assertEquals($scopedUsers->count(), 2);
}

/** @test */
public function it_throws_an_exception_when_trying_to_scope_a_permission_from_another_guard()
{
$this->expectException(PermissionDoesNotExist::class);

User::permission('testAdminPermission')->get();

$this->expectException(GuardDoesNotMatch::class);

User::permission($this->testAdminPermission)->get();
}
}

0 comments on commit 8b57afd

Please sign in to comment.