Skip to content

Sub-Organizations

Introduction

When selecting Tenant as the organization type, the nubos:init command asks which sub-structure to scaffold within each tenant:

Sub-structureWhat gets generated
NoneFlat tenant, no sub-organizations
TeamsTeam model and actions, scoped to the current tenant
WorkspacesWorkspace model and actions, scoped to the current tenant
Workspaces + TeamsBoth Workspace and Team models with a Workspace > Team hierarchy, all scoped to the current tenant

Modifications Applied

When a sub-organization (Team, Workspace) is placed under a tenant, four modifications are applied to the generated code:

1. Tenant Foreign Key

A tenant_id foreign key is added to the sub-organization's migration:

php
$table->foreignUuid('tenant_id')->constrained()->cascadeOnDelete();

2. TenantScope Trait

The TenantScope trait is added to the sub-organization model. This global scope automatically filters all queries by the current tenant's ID and auto-sets tenant_id on new records. Without this trait, sub-organizations from all tenants would be visible.

3. TenantIdentification Middleware

The sub-organization's route file is modified to include TenantIdentification middleware before the organization middleware. This ensures the tenant is resolved from the subdomain before the sub-organization is resolved.

4. Scoped Slug Uniqueness

The slug uniqueness constraint changes from a global unique index to a composite unique index scoped to the tenant:

php
// Without tenant:
$table->string('slug')->unique();

// Under tenant:
$table->string('slug');
$table->unique(['tenant_id', 'slug']);

This allows different tenants to have sub-organizations with the same slug.

Migration Numbering

Sub-organization migrations are renumbered to run after tenant migrations. The nubos:init command remaps migration numbers:

OriginalRemapped
110000120003
110001120004
110002120006
110003120007
110004120005
110005120008

This ensures tenant tables (120000-120002) are created before sub-organization tables that reference them.

Configuration Examples

Tenant with Teams:

php
return [
    'organization_type' => 'tenant',
    'organization_model' => \App\Models\Tenant::class,
    'database_strategy' => 'single',
    'sub_organization' => 'team',
    'has_sub_teams' => true,
    'sub_team_model' => \App\Models\Team::class,
];

Tenant with Workspaces:

php
return [
    'organization_type' => 'tenant',
    'organization_model' => \App\Models\Tenant::class,
    'database_strategy' => 'single',
    'sub_organization' => 'workspace',
    'has_sub_teams' => true,
    'sub_organization_model' => \App\Models\Workspace::class,
];

Tenant with Workspaces + Teams:

php
return [
    'organization_type' => 'tenant',
    'organization_model' => \App\Models\Tenant::class,
    'database_strategy' => 'single',
    'sub_organization' => 'workspace-teams',
    'has_sub_teams' => true,
    'sub_organization_model' => \App\Models\Workspace::class,
    'sub_team_model' => \App\Models\Team::class,
];

All three sub-structure options work with both single and multi database strategies.