Skip to content

🧱 Roles & Permissions Schema Map

Last updated: 2025-10-09 (Europe/Paris)

This diagram shows how Spatie Laravel-Permission’s tables connect to the Accounts context (users, organizations, memberships) when team mode is enabled. Use it as the source of truth when adjusting migrations, seeding roles, or debugging authorization issues.


🔗 Schema Overview

%%{init: {'themeVariables': {'fontSize': '15px'}, 'class': {'useMaxWidth': true}}}%%
classDiagram
    direction LR

    class User {
        UUID id
        string email (unique)
        string password
        timestamps
    }

    class Organization {
        UUID id
        string name
        string slug (unique)
        UUID owner_id
        timestamps
    }

    class Membership {
        UUID id
        UUID user_id
        UUID organization_id
        string role
        string status
        json permissions
        timestamps
    }

    class Role {
        bigint id
        string name
        string guard_name
        bigint team_id (nullable)
        timestamps
    }

    class Permission {
        bigint id
        string name
        string guard_name
        timestamps
    }

    class ModelHasRoles {
        bigint role_id
        bigint model_id
        string model_type
        bigint team_id (nullable)
    }

    class ModelHasPermissions {
        bigint permission_id
        bigint model_id
        string model_type
        bigint team_id (nullable)
    }

    class RoleHasPermissions {
        bigint role_id
        bigint permission_id
    }

    User "1" --> "many" Membership : enrolls
    Organization "1" --> "many" Membership : contains

    Membership ..> Role : "syncs Spatie role"

    User "1" --> "many" ModelHasRoles : "model_has_roles"
    ModelHasRoles --> Role : "role_id"
    Organization "1" --> "many" ModelHasRoles : "team_id"

    User "1" --> "many" ModelHasPermissions : "model_has_permissions"
    ModelHasPermissions --> Permission : "permission_id"
    Organization "1" --> "many" ModelHasPermissions : "team_id"

    Role "1" --> "many" RoleHasPermissions : "role_has_permissions"
    RoleHasPermissions --> Permission : "permission_id"

    note for ModelHasRoles "team_id references organizations.id when team mode is enabled"
    note for ModelHasPermissions "team_id references organizations.id when team mode is enabled"

🧱 Domain Models in Scope

Model Stored in Description
Spatie\Permission\Models\Role roles Team-scoped roles mapped from organization memberships.
Spatie\Permission\Models\Permission permissions Atomic abilities (artist.manage, release.publish, etc.).
ModelHasRoles (pivot) model_has_roles Links a user (or other model) to a role within a specific team.
ModelHasPermissions (pivot) model_has_permissions Direct ability grants to a user/model with team scoping.
RoleHasPermissions (pivot) role_has_permissions Associates roles with their bundled permissions.

📝 Implementation Notes

  • model_has_roles and model_has_permissions both include team_id when permission.teams is true. We set this to the active organization so Spatie scopes checks correctly.
  • Membership::role mirrors the high-level role (owner/admin/manager/artist/viewer). When memberships change, sync the Spatie role for that organization via assignRole($role, $organization).
  • Per-member overrides (stored in memberships.permissions) become direct permission grants using givePermissionTo($ability, $organization), populating model_has_permissions.
  • role_has_permissions defines reusable bundles of abilities (e.g., artist.manage, release.publish). Seed these alongside organization creation.
  • docs/contexts/accounts/models.md — Accounts entities (users, organizations, memberships, invitations).
  • docs/contexts/auth/roles-permissions.md — Narrative overview of access domains and default mappings.
  • docs/contexts/auth/entity-diagram.md — High-level relationships between Spatie tables and other contexts.
  • docs/contexts/auth/index.md — Other auth documentation (flows, guides).