Skip to main content

Entity Relationships

This document describes the relationships between all models in the OPBX system.

Core Relationships

Organization (Tenant)

Organization
├── hasMany: User[]
├── hasMany: Extension[]
├── hasMany: DidNumber[]
├── hasMany: RingGroup[]
├── hasMany: ConferenceRoom[]
├── hasMany: BusinessHoursSchedule[]
├── hasMany: CallLog[]
├── hasOne: CloudonixSettings
└── hasMany: CallDetailRecord[]

User

User
├── belongsTo: Organization
├── hasOne: Extension (user type)
└── hasMany: PlatformAuditLog[] (as platform manager)

Extension (Central Entity)

Extension
├── belongsTo: Organization
├── belongsTo: User (for USER type)
├── belongsTo: AiAssistant (for AI_ASSISTANT type)
└── hasOne: AiLoadBalancer (via configuration)

PBX Resource Relationships

RingGroup

RingGroup
├── belongsTo: Organization
├── hasMany: RingGroupMember[]
├── belongsTo: Extension (fallback)
├── belongsTo: RingGroup (fallback)
├── belongsTo: IvrMenu (fallback)
└── belongsTo: AiAssistant (fallback)

DidNumber (DID)

DidNumber
├── belongsTo: Organization
└── hasDynamic: Extension | RingGroup | IvrMenu | AiAssistant
(based on routing_type configuration)

ConferenceRoom

ConferenceRoom
└── belongsTo: Organization

AI Feature Relationships

AiAssistant

AiAssistant
├── belongsTo: Organization
├── belongsTo: User (creator)
├── belongsTo: User (updater)
└── hasMany: Extension[]

AiAssistantLoadBalancer

AiAssistantLoadBalancer
├── belongsTo: Organization
├── hasMany: AiAssistantLoadBalancerMember[]
├── belongsTo: Extension (fallback)
├── belongsTo: RingGroup (fallback)
├── belongsTo: IvrMenu (fallback)
└── belongsTo: AiAssistant (fallback)

AiAssistantLoadBalancerMember (Join)

AiAssistantLoadBalancerMember
├── belongsTo: AiAssistantLoadBalancer
└── belongsTo: AiAssistant

Call Management Relationships

CallLog

CallLog
├── belongsTo: Organization
├── belongsTo: DidNumber
├── belongsTo: Extension
└── belongsTo: RingGroup

CallDetailRecord

CallDetailRecord
└── belongsTo: Organization

Recording

Recording
├── belongsTo: Organization
├── belongsTo: User (creator)
└── belongsTo: User (updater)

Configuration Relationships

IvrMenu

IvrMenu
├── belongsTo: Organization
└── hasMany: IvrMenuOption[]

IvrMenuOption (Join)

IvrMenuOption
├── belongsTo: IvrMenu
└── hasDynamic: Extension | RingGroup | IvrMenu | AiAssistant
(based on destination_type)

BusinessHoursSchedule

BusinessHoursSchedule
├── belongsTo: Organization
├── hasMany: BusinessHoursScheduleDay[]
└── hasMany: BusinessHoursException[]

CloudonixSettings

CloudonixSettings
└── belongsTo: Organization

Security Relationships

InboundBlacklist

InboundBlacklist
└── belongsTo: Organization

OutboundWhitelist

OutboundWhitelist
└── belongsTo: Organization

Multi-Tenancy

All models (except CallDetailRecord and PlatformAuditLog) use the OrganizationScope global scope:

#[ScopedBy([OrganizationScope::class])]
class Model extends Model
{
// Automatically filtered by organization_id
}

This ensures complete data isolation between organizations.

Relationship Diagram

┌─────────────────┐     ┌─────────────────┐     ┌─────────────────┐
│ Organization │◄────┤ User │◄────┤ Extension │
│ (Tenant) │ │ (has role) │ │ (has type) │
└────────┬────────┘ └─────────────────┘ └────────┬────────┘
│ │
│ ┌─────────────────┐ ┌─────────────────┐│
├───►│ DidNumber │ │ AiAssistant │◄┘
│ │ (routes to) │ │ (AI type) │
│ └─────────────────┘ └─────────────────┘

│ ┌─────────────────┐ ┌─────────────────┐
├───►│ RingGroup │ │ ConferenceRoom │
│ │ (has members) │ │ │
│ └─────────────────┘ └─────────────────┘

│ ┌─────────────────┐ ┌─────────────────┐
├───►│ IvrMenu │ │ CallLog/CDR │
│ │ (has options) │ │ │
│ └─────────────────┘ └─────────────────┘

│ ┌─────────────────┐ ┌─────────────────┐
└───►│ Settings │ │ Security Lists │
│ (Cloudonix) │ │ (Black/White) │
└─────────────────┘ └─────────────────┘

Eager Loading Recommendations

When querying related data, use eager loading to avoid N+1 problems:

// Load users with their extensions
$users = User::with('extension')->get();

// Load ring groups with members and extensions
$ringGroups = RingGroup::with([
'members.extension.user',
'fallbackExtension'
])->get();

// Load call logs with all related entities
$calls = CallLog::with([
'didNumber',
'extension.user',
'ringGroup'
])->get();