Membership Service
The Membership service manages personable ↔ role links with soft deletes, default role resolution, permission overrides, and cache invalidation.
Namespace
JobMetric\Rolix\Services\Membership
Facade
use JobMetric\Rolix\Facades\Membership;
Soft-Delete Flags
The service enables:
$softDelete = true$hasRestore = true$hasForceDelete = true
Basic CRUD Operations
Store
$response = Membership::store([
'personable_type' => User::class,
'personable_id' => $user->id,
'role_id' => $role->id, // optional if a default role exists
'memberable_type' => Tenant::class, // null for system
'memberable_id' => $tenant->id,
'collection' => 'ops',
'is_owner' => false,
'expired_at' => null,
'allow' => ['extra.perm'],
'deny' => [],
]);
Validation: StoreMembershipRequest.
Default Role Resolution
When role_id is omitted:
- If memberable is null → type
system - Else → first registered type whose
modelmorph matchesmemberable_type - Load role with
is_default = truefor that type - If missing →
MembershipDefaultRoleMissingException
Duplicates
Unique constraint violations are rethrown as MembershipDuplicateException (HTTP 422 semantics).
Show / Update
Membership::show($id, ['role', 'personable']);
Membership::update($id, [
'allow' => ['hero.view'],
'expired_at' => now()->addMonth(),
]);
Destroy (Soft Delete)
Membership::destroy($id);
Soft-deleted memberships no longer grant permissions (HasRole queries exclude them via relation defaults / non-trashed).
Restore
$response = Membership::restore($id);
Re-grants permissions and logs restore_membership.
Force Delete
$response = Membership::forceDelete($id);
Permanently removes the row and logs force_delete_membership.
Query Helpers
Membership::paginate($filters);
Membership::all($filters);
Allowed QueryBuilder fields include morph keys, role_id, collection, is_owner, expired_at, timestamps, deleted_at.
Side Effects (afterCommon)
For store, update, destroy, restore, forceDelete:
- Invalidate permission cache for the personable (
PermissionCache::forget/forgetByMorph) ActivityLogger::log(...)- Dispatch the matching DomainEvent class
Prefer Traits for App Code
// Prefer:
$user->assignRole($role, $tenant);
$tenant->assignMember($user, $role);
// Equivalent low-level:
Membership::store([/* morph arrays */]);