Skip to content

Commit 9a43872

Browse files
committed
Apply same team detach sync to permissions
1 parent 5084699 commit 9a43872

4 files changed

Lines changed: 249 additions & 2 deletions

File tree

src/Traits/HasPermissions.php

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use BackedEnum;
66
use Illuminate\Database\Eloquent\Builder;
77
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
8+
use Illuminate\Database\Eloquent\Relations\Pivot;
89
use Illuminate\Support\Arr;
910
use Illuminate\Support\Collection;
1011
use Spatie\Permission\Contracts\Permission;
@@ -385,6 +386,32 @@ private function collectPermissions(...$permissions): array
385386
}, []);
386387
}
387388

389+
private function detachPermissions(?array $permissions = null): int
390+
{
391+
$relation = $this->permissions();
392+
393+
if (! Config::teamsEnabled() || $this instanceof Role || $relation->getPivotClass() === Pivot::class) {
394+
return $relation->detach($permissions);
395+
}
396+
397+
// Custom pivot deletes do not include the team key, so keep deletion on the scoped pivot query.
398+
$query = $relation->newPivotQuery();
399+
400+
if (! is_null($permissions)) {
401+
if (empty($permissions)) {
402+
return 0;
403+
}
404+
405+
$query->whereIn($relation->getQualifiedRelatedPivotKeyName(), $permissions);
406+
}
407+
408+
$results = $query->delete();
409+
410+
$relation->touchIfTouching();
411+
412+
return $results;
413+
}
414+
388415
/**
389416
* Grant the given permission(s) to a role.
390417
*
@@ -458,7 +485,7 @@ public function syncPermissions(...$permissions): static
458485
$this->revokePermissionTo($currentPermissions);
459486
}
460487
} else {
461-
$this->permissions()->detach();
488+
$this->detachPermissions();
462489
$this->setRelation('permissions', collect());
463490
}
464491
}
@@ -475,8 +502,9 @@ public function syncPermissions(...$permissions): static
475502
public function revokePermissionTo($permission): static
476503
{
477504
$storedPermission = $this->getStoredPermission($permission);
505+
$permissions = $this->collectPermissions($storedPermission);
478506

479-
$this->permissions()->detach($storedPermission);
507+
$this->detachPermissions($permissions);
480508

481509
if ($this instanceof Role) {
482510
$this->forgetCachedPermissions();

tests/Traits/HasPermissionsTest.php

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
<?php
22

33
use Illuminate\Database\Eloquent\Model;
4+
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
5+
use Illuminate\Database\Eloquent\Relations\MorphPivot;
46
use Illuminate\Support\Facades\Event;
57
use Spatie\Permission\Contracts\Permission;
68
use Spatie\Permission\Contracts\Role;
@@ -11,6 +13,22 @@
1113
use Spatie\Permission\Tests\TestSupport\TestModels\SoftDeletingUser;
1214
use Spatie\Permission\Tests\TestSupport\TestModels\TestRolePermissionsEnum;
1315
use Spatie\Permission\Tests\TestSupport\TestModels\User;
16+
use Spatie\Permission\Tests\TestSupport\TestModels\UserWithoutHasRoles;
17+
use Spatie\Permission\Traits\HasRoles;
18+
19+
class HasPermissionsCustomPivot extends MorphPivot {}
20+
21+
class HasPermissionsCustomPivotUser extends UserWithoutHasRoles
22+
{
23+
use HasRoles {
24+
permissions as traitPermissions;
25+
}
26+
27+
public function permissions(): BelongsToMany
28+
{
29+
return $this->traitPermissions()->using(HasPermissionsCustomPivot::class);
30+
}
31+
}
1432

1533
it('can assign a permission to a user', function () {
1634
$this->testUser->givePermissionTo($this->testUserPermission);
@@ -52,6 +70,26 @@
5270
expect($this->testUser->hasPermissionTo($this->testUserPermission))->toBeFalse();
5371
});
5472

73+
it('can revoke a permission when using a custom pivot class without teams', function () {
74+
config()->set('auth.providers.users.model', HasPermissionsCustomPivotUser::class);
75+
76+
$user = HasPermissionsCustomPivotUser::create(['email' => 'custom-pivot-permissions-without-teams@test.com']);
77+
78+
$user->givePermissionTo('edit-articles', 'edit-news');
79+
80+
expect($user->getPermissionNames()->sort()->values())
81+
->toEqual(collect(['edit-articles', 'edit-news']));
82+
83+
$user->revokePermissionTo('edit-articles');
84+
85+
expect($user->getPermissionNames()->sort()->values())
86+
->toEqual(collect(['edit-news']));
87+
88+
$user->syncPermissions([]);
89+
90+
expect($user->getPermissionNames())->toBeEmpty();
91+
});
92+
5593
it('can assign and remove a permission using enums', function () {
5694
$enum = TestRolePermissionsEnum::ViewArticles;
5795

tests/Traits/TeamHasPermissionsTest.php

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
<?php
22

33
use Illuminate\Database\Eloquent\Model;
4+
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
5+
use Illuminate\Database\Eloquent\Relations\MorphPivot;
46
use Illuminate\Support\Facades\Event;
57
use Spatie\Permission\Contracts\Permission;
68
use Spatie\Permission\Contracts\Role;
@@ -11,6 +13,24 @@
1113
use Spatie\Permission\Tests\TestSupport\TestModels\SoftDeletingUser;
1214
use Spatie\Permission\Tests\TestSupport\TestModels\TestRolePermissionsEnum;
1315
use Spatie\Permission\Tests\TestSupport\TestModels\User;
16+
use Spatie\Permission\Tests\TestSupport\TestModels\UserWithoutHasRoles;
17+
use Spatie\Permission\Traits\HasRoles;
18+
19+
class TeamHasPermissionsCustomPivot extends MorphPivot {}
20+
21+
class TeamHasPermissionsCustomPivotUser extends UserWithoutHasRoles
22+
{
23+
use HasRoles {
24+
permissions as traitPermissions;
25+
}
26+
27+
public function permissions(): BelongsToMany
28+
{
29+
return $this->traitPermissions()
30+
->withPivot('team_test_id')
31+
->using(TeamHasPermissionsCustomPivot::class);
32+
}
33+
}
1434

1535
beforeEach(fn () => $this->setUpTeams());
1636

@@ -756,6 +776,114 @@
756776
->toEqual(collect(['edit-articles', 'edit-blog']));
757777
});
758778

779+
it('can revoke a permission from one team when using a custom pivot class', function () {
780+
config()->set('auth.providers.users.model', TeamHasPermissionsCustomPivotUser::class);
781+
782+
$user = TeamHasPermissionsCustomPivotUser::create(['email' => 'custom-pivot-revoke-permission@test.com']);
783+
784+
setPermissionsTeamId(1);
785+
$user->givePermissionTo('edit-articles', 'edit-news');
786+
787+
setPermissionsTeamId(2);
788+
$user->givePermissionTo('edit-articles', 'edit-blog');
789+
$user->load('permissions');
790+
791+
expect($user->getPermissionNames()->sort()->values())
792+
->toEqual(collect(['edit-articles', 'edit-blog']));
793+
794+
setPermissionsTeamId(1);
795+
$user->load('permissions');
796+
797+
expect($user->getPermissionNames()->sort()->values())
798+
->toEqual(collect(['edit-articles', 'edit-news']));
799+
800+
$user->revokePermissionTo('edit-articles');
801+
802+
expect($user->getPermissionNames()->sort()->values())
803+
->toEqual(collect(['edit-news']));
804+
805+
setPermissionsTeamId(2);
806+
$user->load('permissions');
807+
808+
expect($user->getPermissionNames()->sort()->values())
809+
->toEqual(collect(['edit-articles', 'edit-blog']));
810+
});
811+
812+
it('can sync permissions for one team when using a custom pivot class', function () {
813+
config()->set('auth.providers.users.model', TeamHasPermissionsCustomPivotUser::class);
814+
815+
$user = TeamHasPermissionsCustomPivotUser::create(['email' => 'custom-pivot-sync-permissions@test.com']);
816+
817+
setPermissionsTeamId(1);
818+
$user->givePermissionTo('edit-articles', 'edit-news');
819+
820+
setPermissionsTeamId(2);
821+
$user->givePermissionTo('edit-articles', 'edit-blog');
822+
823+
setPermissionsTeamId(1);
824+
$user->syncPermissions('edit-news');
825+
826+
expect($user->getPermissionNames()->sort()->values())
827+
->toEqual(collect(['edit-news']));
828+
829+
setPermissionsTeamId(2);
830+
$user->load('permissions');
831+
832+
expect($user->getPermissionNames()->sort()->values())
833+
->toEqual(collect(['edit-articles', 'edit-blog']));
834+
});
835+
836+
it('can sync permissions with events for one team when using a custom pivot class', function () {
837+
Event::fake([PermissionDetachedEvent::class, PermissionAttachedEvent::class]);
838+
app('config')->set('permission.events_enabled', true);
839+
config()->set('auth.providers.users.model', TeamHasPermissionsCustomPivotUser::class);
840+
841+
$user = TeamHasPermissionsCustomPivotUser::create(['email' => 'custom-pivot-sync-permissions-events@test.com']);
842+
843+
setPermissionsTeamId(1);
844+
$user->givePermissionTo('edit-articles', 'edit-news');
845+
846+
setPermissionsTeamId(2);
847+
$user->givePermissionTo('edit-articles', 'edit-blog');
848+
849+
setPermissionsTeamId(1);
850+
$user->syncPermissions('edit-news');
851+
852+
expect($user->getPermissionNames()->sort()->values())
853+
->toEqual(collect(['edit-news']));
854+
855+
Event::assertDispatched(PermissionDetachedEvent::class);
856+
857+
setPermissionsTeamId(2);
858+
$user->load('permissions');
859+
860+
expect($user->getPermissionNames()->sort()->values())
861+
->toEqual(collect(['edit-articles', 'edit-blog']));
862+
});
863+
864+
it('can sync to no permissions for one team when using a custom pivot class', function () {
865+
config()->set('auth.providers.users.model', TeamHasPermissionsCustomPivotUser::class);
866+
867+
$user = TeamHasPermissionsCustomPivotUser::create(['email' => 'custom-pivot-sync-empty-permissions@test.com']);
868+
869+
setPermissionsTeamId(1);
870+
$user->givePermissionTo('edit-articles', 'edit-news');
871+
872+
setPermissionsTeamId(2);
873+
$user->givePermissionTo('edit-articles');
874+
875+
setPermissionsTeamId(1);
876+
$user->syncPermissions([]);
877+
878+
expect($user->getPermissionNames())->toBeEmpty();
879+
880+
setPermissionsTeamId(2);
881+
$user->load('permissions');
882+
883+
expect($user->getPermissionNames()->sort()->values())
884+
->toEqual(collect(['edit-articles']));
885+
});
886+
759887
it('can scope users on different teams', function () {
760888
$user1 = User::create(['email' => 'user1@test.com']);
761889
$user2 = User::create(['email' => 'user2@test.com']);

tests/Traits/TeamHasRolesTest.php

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1026,6 +1026,59 @@ public function roles(): BelongsToMany
10261026
->toEqual(collect(['testRole', 'testRole3']));
10271027
});
10281028

1029+
it('can sync roles with events for one team when using a custom pivot class', function () {
1030+
Event::fake([RoleDetachedEvent::class, RoleAttachedEvent::class]);
1031+
app('config')->set('permission.events_enabled', true);
1032+
config()->set('auth.providers.users.model', TeamHasRolesCustomPivotUser::class);
1033+
1034+
app(Role::class)->create(['name' => 'testRole3', 'team_test_id' => 2]);
1035+
1036+
$user = TeamHasRolesCustomPivotUser::create(['email' => 'custom-pivot-sync-roles-events@test.com']);
1037+
1038+
setPermissionsTeamId(1);
1039+
$user->assignRole('testRole', 'testRole2');
1040+
1041+
setPermissionsTeamId(2);
1042+
$user->assignRole('testRole', 'testRole3');
1043+
1044+
setPermissionsTeamId(1);
1045+
$user->syncRoles('testRole2');
1046+
1047+
expect($user->getRoleNames()->sort()->values())
1048+
->toEqual(collect(['testRole2']));
1049+
1050+
Event::assertDispatched(RoleDetachedEvent::class);
1051+
1052+
setPermissionsTeamId(2);
1053+
$user->load('roles');
1054+
1055+
expect($user->getRoleNames()->sort()->values())
1056+
->toEqual(collect(['testRole', 'testRole3']));
1057+
});
1058+
1059+
it('can sync to no roles for one team when using a custom pivot class', function () {
1060+
config()->set('auth.providers.users.model', TeamHasRolesCustomPivotUser::class);
1061+
1062+
$user = TeamHasRolesCustomPivotUser::create(['email' => 'custom-pivot-sync-empty-roles@test.com']);
1063+
1064+
setPermissionsTeamId(1);
1065+
$user->assignRole('testRole', 'testRole2');
1066+
1067+
setPermissionsTeamId(2);
1068+
$user->assignRole('testRole');
1069+
1070+
setPermissionsTeamId(1);
1071+
$user->syncRoles([]);
1072+
1073+
expect($user->getRoleNames())->toBeEmpty();
1074+
1075+
setPermissionsTeamId(2);
1076+
$user->load('roles');
1077+
1078+
expect($user->getRoleNames()->sort()->values())
1079+
->toEqual(collect(['testRole']));
1080+
});
1081+
10291082
it('can scope users on different teams', function () {
10301083
User::all()->each(fn ($item) => $item->delete());
10311084
$user1 = User::create(['email' => 'user1@test.com']);

0 commit comments

Comments
 (0)