laravel-addresses/docs/core-concepts.md

173 lines
6.9 KiB
Markdown
Raw Normal View History

2026-04-14 08:20:42 +00:00
# Core Concepts
## The Three-Layer Architecture
Laravel Addresses uses three models that work together:
```
Address
└── AddressLink
└── AddressAssignment
```
Each layer has a distinct responsibility:
### Layer 1: Address
The **physical place**. An `Address` is a standalone record containing street data, postal information, and optional GPS coordinates. It knows nothing about who uses it or why.
```php
Address::create([
'street' => '350 Fifth Avenue',
'building' => 'Empire State Building',
'floor' => '32',
'room' => '3201',
'postal_code' => '10118',
'city' => 'New York',
'state' => 'NY',
'country_code' => 'US',
'latitude' => 40.748817,
'longitude' => -73.985428,
'altitude' => 443.0,
]);
```
All fields are nullable — an address can be as minimal as a GPS coordinate pair or as detailed as a full postal address with indoor precision.
**Available fields:**
| Field | Type | Description |
|----------------|--------|------------------------------------------------|
| `street` | string | Street name + house number |
| `street_extra` | string | c/o, suite, P.O. box |
| `building` | string | Building or complex name |
| `floor` | string | Floor/level (supports "GF", "B2", "Mezzanine") |
| `room` | string | Room, suite or unit identifier |
| `postal_code` | string | Postal / ZIP code |
| `city` | string | City, town, village |
| `state` | string | State, province, canton |
| `county` | string | County, district |
| `country_code` | string | ISO 3166-1 alpha-2 ("US", "AT", "JP") |
| `latitude` | float | WGS-84 decimal degrees (90 … +90) |
| `longitude` | float | WGS-84 decimal degrees (180 … +180) |
| `altitude` | float | Metres above mean sea level (AMSL) |
| `notes` | text | Free-form notes, delivery instructions |
| `meta` | JSON | Arbitrary extra data |
Addresses use **soft deletes** — calling `$address->delete()` sets `deleted_at` instead of removing the row.
### Layer 2: AddressLink
The **ownership pivot**. An `AddressLink` connects an `Address` to an Eloquent model (User, Company, Order …) and describes the **purpose** of that connection.
```php
$link = $user->addAddress([
'street' => 'Stephansplatz 1',
'city' => 'Vienna',
'country_code' => 'AT',
], AddressLinkType::Office, [
'label' => 'Main Office',
'is_primary' => true,
]);
// $link->type → AddressLinkType::Office
// $link->label → "Main Office"
// $link->address → the Address model
```
**Key properties:**
| Property | Type | Description |
|----------------|-----------------|-----------------------------------------------|
| `type` | AddressLinkType | The purpose (Home, Office, Shipping …) |
| `label` | string\|null | Free-text label to refine the type |
| `is_primary` | bool | Whether this is the primary link for its type |
| `active_from` | datetime\|null | When the link becomes effective |
| `active_until` | datetime\|null | When the link expires |
| `meta` | object\|null | Arbitrary JSON data |
**Important:** The same address can be linked to multiple models, and each model can have multiple addresses. A user can have a Home address, an Office address, and a Billing address — each as a separate `AddressLink`.
**Polymorphic:** Uses `addressable_type` / `addressable_id` morphs, so any Eloquent model can own addresses.
### Layer 3: AddressAssignment
The **contextual reference**. An `AddressAssignment` lets one model reference another model's address link without owning the address.
**The problem it solves:** A transport job needs a pickup and delivery address. Those addresses belong to the customer, not the job. Instead of duplicating address data, the job *assigns* the customer's existing address links.
```php
// User owns the address
$link = $user->addAddress([
'street' => 'Kärntner Straße 21',
'city' => 'Vienna',
], AddressLinkType::Office);
// Job references it as "pickup"
$job->assignAddressLink($link, 'pickup');
// Later, retrieve it
$pickupAddress = $job->assignedAddressForRole('pickup');
// → Address { street: "Kärntner Straße 21", city: "Vienna" }
```
**Key properties:**
| Property | Type | Description |
|-------------------|--------------|---------------------------------------------------|
| `address_link_id` | int | FK to the address link being referenced |
| `role` | string\|null | Context-specific purpose ("pickup", "delivery" …) |
| `label` | string\|null | Free-text label |
| `meta` | object\|null | Arbitrary JSON data |
## Cascade Behaviour
- **Deleting an Address** → all its `AddressLink` rows are cascade-deleted
- **Deleting an AddressLink** → all its `AddressAssignment` rows are cascade-deleted
- Addresses use **soft deletes**; links and assignments use **hard deletes**
## Traits
The package provides two traits to add to your Eloquent models:
| Trait | Purpose | Use on |
|-------------------------|-----------------------------------|-----------------------------------------------------|
| `HasAddresses` | Own and manage addresses | Models that **have** addresses (User, Company …) |
| `HasAddressAssignments` | Reference other models' addresses | Models that **use** addresses (Job, Order, Event …) |
A model can use both traits if it both owns and references addresses.
## The AddressService
A singleton service for operations that go beyond CRUD:
```php
// Distance between two addresses (Haversine)
address()->distanceBetween($a, $b);
// Find nearby addresses
address()->nearby($lat, $lng, $radiusKm);
// Detect duplicates
address()->findDuplicates($address);
// Format for display
address()->formatMultiline($address);
```
Access via the `address()` helper or dependency injection:
```php
use Blax\Addresses\Services\AddressService;
public function __construct(private AddressService $addressService) {}
```
See the individual documentation pages for complete API references:
- [HasAddresses Trait](has-addresses.md)
- [HasAddressAssignments Trait](has-address-assignments.md)
- [AddressService](address-service.md)
- [AddressLinkType Enum](address-link-types.md)
- [Customization](customization.md)