Skip to content

Locality

Locality is the core context management mechanism in Retail API. It identifies the current request’s organization, advisor, and household, then restricts data access accordingly.

  1. Identify current context - Determine which organization/advisor/household the request operates on
  2. Reduce permission resolution overhead - Avoid recalculating accessible IDs for every operation
Federation
└── Organization
└── Advisor
└── Household
TypeInheritancePurpose
UserLocalityLocalityDefault when no route parameters present
AdvisorLocalityLocalityAdvisor context, no impersonation support
ImpersonatableAdvisorLocalityAdvisorLocalitySupports team collaboration impersonation
HouseholdLocalityImpersonatableAdvisorLocalityHousehold context
OrganizationLocalityLocalityOrganization context, cannot get advisor
FederationLocalityLocalityFederation context

Code location: retail-api/app/Locality/

Context is initialized from route parameters via InitializeLocality middleware. See Locality Middleware for details.

Resolution priority in LocalityManager::initializeLocalityFromRoute():

  1. federation parameter → FederationLocality
  2. advisor + household parameters → HouseholdLocality
  3. advisor parameter → ImpersonatableAdvisorLocality
  4. organization parameter → OrganizationLocality
  5. No parameters → UserLocality

Route parameters use encrypted IDs:

// retail-api/app/Locality/Services/LocalityManager.php:323
$id = Crypt::decryptId($encrypted_id);
OperationRestricted to
Readcontext organization/advisor/household
Writecontext household only

Models under Advisor (excluding Household sub-models)

Section titled “Models under Advisor (excluding Household sub-models)”
OperationRestricted to
Readcontext organization/advisor
Writecontext advisor only

Access restricted to context organization.

// Get current context
LocalityManager::getAdvisor();
LocalityManager::getHousehold();
LocalityManager::getOrganization();
// Temporarily switch household
LocalityManager::switchHousehold($household, function() {
// Uses new household context within this closure
});
// Temporarily switch entire locality
LocalityManager::switchLocality($locality, function() {
// ...
});
// Ignore license status check
LocalityManager::switchIgnoreLicenseStatus(true, function() {
// ...
});

Code location: retail-api/app/Locality/Services/LocalityManager.php

Advisors can impersonate other advisors on the same team:

// retail-api/app/Locality/ImpersonatableAdvisorLocality.php:130-135
match ($login_user->type) {
UserType::ADVISOR => (
$login_user->id === $advisor->id
|| in_array($advisor->id, AdvisorPolicy::getReadableIdsForDependent(), true)
) || throw new AccessDeniedHttpException(...),
};

Permission is checked via AdvisorPolicy::getReadableIdsForDependent().

The advisor parameter in the route may be an impersonated advisor, not necessarily the logged-in user.

Context comes entirely from route parameters. If the route has no organization/household parameter, calling getHousehold() will throw LogicException.

OrganizationLocality cannot retrieve an Advisor. Calling getAdvisor() throws an exception.