328 lines
8.1 KiB
Markdown
328 lines
8.1 KiB
Markdown
|
|
# HasAddresses Trait
|
||
|
|
|
||
|
|
Add the `HasAddresses` trait to any Eloquent model that **owns** addresses.
|
||
|
|
|
||
|
|
```php
|
||
|
|
use Blax\Addresses\Traits\HasAddresses;
|
||
|
|
|
||
|
|
class User extends Model
|
||
|
|
{
|
||
|
|
use HasAddresses;
|
||
|
|
}
|
||
|
|
|
||
|
|
class Company extends Model
|
||
|
|
{
|
||
|
|
use HasAddresses;
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## Relationships
|
||
|
|
|
||
|
|
### `addressLinks()`
|
||
|
|
|
||
|
|
Returns all `AddressLink` pivot rows for this model.
|
||
|
|
|
||
|
|
```php
|
||
|
|
$links = $user->addressLinks;
|
||
|
|
|
||
|
|
foreach ($links as $link) {
|
||
|
|
echo $link->type->label(); // "Office"
|
||
|
|
echo $link->label; // "Main Office"
|
||
|
|
echo $link->address->city; // "Vienna"
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Return:** `MorphMany` of `AddressLink`
|
||
|
|
|
||
|
|
### `addresses()`
|
||
|
|
|
||
|
|
Returns all `Address` models linked to this model (many-to-many through `address_links`). Pivot columns are included automatically.
|
||
|
|
|
||
|
|
```php
|
||
|
|
$addresses = $user->addresses;
|
||
|
|
|
||
|
|
foreach ($addresses as $address) {
|
||
|
|
echo $address->city;
|
||
|
|
echo $address->pivot->type; // "office"
|
||
|
|
echo $address->pivot->is_primary; // true/false
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Return:** `MorphToMany` of `Address` (with pivot: `id`, `type`, `label`, `is_primary`, `active_from`, `active_until`, `meta`)
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Adding Addresses
|
||
|
|
|
||
|
|
### `addAddress(array $attributes, AddressLinkType|string|null $type = null, array $pivot = []): AddressLink`
|
||
|
|
|
||
|
|
Creates a new `Address` record and links it to this model in one step.
|
||
|
|
|
||
|
|
```php
|
||
|
|
use Blax\Addresses\Enums\AddressLinkType;
|
||
|
|
|
||
|
|
// Minimal
|
||
|
|
$link = $user->addAddress([
|
||
|
|
'city' => 'Vienna',
|
||
|
|
'country_code' => 'AT',
|
||
|
|
]);
|
||
|
|
|
||
|
|
// With type
|
||
|
|
$link = $user->addAddress([
|
||
|
|
'street' => '350 Fifth Avenue',
|
||
|
|
'city' => 'New York',
|
||
|
|
'postal_code' => '10118',
|
||
|
|
'country_code' => 'US',
|
||
|
|
], AddressLinkType::Office);
|
||
|
|
|
||
|
|
// With type and pivot data
|
||
|
|
$link = $user->addAddress([
|
||
|
|
'street' => 'Baker Street 221B',
|
||
|
|
'city' => 'London',
|
||
|
|
'country_code' => 'GB',
|
||
|
|
], AddressLinkType::Home, [
|
||
|
|
'label' => 'Primary Residence',
|
||
|
|
'is_primary' => true,
|
||
|
|
'meta' => ['floor' => 'ground'],
|
||
|
|
]);
|
||
|
|
```
|
||
|
|
|
||
|
|
**Parameters:**
|
||
|
|
- `$attributes` — Address fields (street, city, latitude, etc.)
|
||
|
|
- `$type` — `AddressLinkType` enum, string value, or `null` (uses config default)
|
||
|
|
- `$pivot` — Extra link data: `label`, `is_primary`, `active_from`, `active_until`, `meta`
|
||
|
|
|
||
|
|
**Returns:** The created `AddressLink` with the `address` relation loaded.
|
||
|
|
|
||
|
|
### `linkAddress(Address|int $address, AddressLinkType|string|null $type = null, array $pivot = []): AddressLink`
|
||
|
|
|
||
|
|
Links an **existing** address to this model. Useful when the same address should be shared by multiple models.
|
||
|
|
|
||
|
|
```php
|
||
|
|
use Blax\Addresses\Models\Address;
|
||
|
|
|
||
|
|
$address = Address::create([
|
||
|
|
'street' => 'Shared Office Road 1',
|
||
|
|
'city' => 'Berlin',
|
||
|
|
'country_code' => 'DE',
|
||
|
|
]);
|
||
|
|
|
||
|
|
// Link to multiple models
|
||
|
|
$user->linkAddress($address, AddressLinkType::Office);
|
||
|
|
$company->linkAddress($address, AddressLinkType::Headquarters);
|
||
|
|
|
||
|
|
// Also accepts an address ID
|
||
|
|
$user->linkAddress($address->id, AddressLinkType::Home);
|
||
|
|
```
|
||
|
|
|
||
|
|
**Returns:** The created `AddressLink` with the `address` relation loaded.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Removing Addresses
|
||
|
|
|
||
|
|
### `removeAddressLink(int $linkId): bool`
|
||
|
|
|
||
|
|
Removes a specific address link by its pivot ID. The address record is preserved.
|
||
|
|
|
||
|
|
```php
|
||
|
|
$link = $user->addAddress(['city' => 'Vienna'], AddressLinkType::Office);
|
||
|
|
|
||
|
|
$user->removeAddressLink($link->id); // true
|
||
|
|
```
|
||
|
|
|
||
|
|
### `detachAddress(Address|int $address): int`
|
||
|
|
|
||
|
|
Removes **all** links between this model and a specific address.
|
||
|
|
|
||
|
|
```php
|
||
|
|
// If the user has multiple links to the same address (e.g. Office + Billing)
|
||
|
|
$removed = $user->detachAddress($address); // 2
|
||
|
|
```
|
||
|
|
|
||
|
|
**Returns:** Number of links removed.
|
||
|
|
|
||
|
|
### `detachAllAddresses(): int`
|
||
|
|
|
||
|
|
Removes all address links from this model.
|
||
|
|
|
||
|
|
```php
|
||
|
|
$removed = $user->detachAllAddresses(); // 5
|
||
|
|
```
|
||
|
|
|
||
|
|
**Returns:** Number of links removed.
|
||
|
|
|
||
|
|
> **Note:** These methods only remove the `AddressLink` pivot rows. The `Address` records themselves are never deleted by these operations.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Querying
|
||
|
|
|
||
|
|
### `addressesOfType(AddressLinkType|string $type): Collection`
|
||
|
|
|
||
|
|
Get all addresses linked with a specific type.
|
||
|
|
|
||
|
|
```php
|
||
|
|
$offices = $user->addressesOfType(AddressLinkType::Office);
|
||
|
|
$homes = $user->addressesOfType('home'); // string value also works
|
||
|
|
```
|
||
|
|
|
||
|
|
### `activeAddressLinks(): Collection`
|
||
|
|
|
||
|
|
Get all address links that are currently active (respecting `active_from` / `active_until`).
|
||
|
|
|
||
|
|
```php
|
||
|
|
$activeLinks = $user->activeAddressLinks();
|
||
|
|
|
||
|
|
foreach ($activeLinks as $link) {
|
||
|
|
echo $link->address->formatted;
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
A link is active when:
|
||
|
|
- `active_from` is `null` OR in the past/present **AND**
|
||
|
|
- `active_until` is `null` OR in the future
|
||
|
|
|
||
|
|
### `primaryAddress(AddressLinkType|string|null $type = null): ?Address`
|
||
|
|
|
||
|
|
Get the primary address, optionally filtered by type.
|
||
|
|
|
||
|
|
```php
|
||
|
|
// Primary address across all types
|
||
|
|
$primary = $user->primaryAddress();
|
||
|
|
|
||
|
|
// Primary office address specifically
|
||
|
|
$primaryOffice = $user->primaryAddress(AddressLinkType::Office);
|
||
|
|
```
|
||
|
|
|
||
|
|
**Returns:** `Address` or `null`.
|
||
|
|
|
||
|
|
### `hasAddresses(): bool`
|
||
|
|
|
||
|
|
Check whether this model has any address linked.
|
||
|
|
|
||
|
|
```php
|
||
|
|
if ($user->hasAddresses()) {
|
||
|
|
// ...
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### `hasAddressOfType(AddressLinkType|string $type): bool`
|
||
|
|
|
||
|
|
Check whether this model has an address of a specific type.
|
||
|
|
|
||
|
|
```php
|
||
|
|
if (! $user->hasAddressOfType(AddressLinkType::Billing)) {
|
||
|
|
// prompt user to add a billing address
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Updating
|
||
|
|
|
||
|
|
### `setPrimaryAddressLink(int $linkId): bool`
|
||
|
|
|
||
|
|
Set an address link as the primary for its type. Automatically unsets any previous primary of the same type on this model.
|
||
|
|
|
||
|
|
```php
|
||
|
|
$officeA = $user->addAddress(['city' => 'Vienna'], AddressLinkType::Office);
|
||
|
|
$officeB = $user->addAddress(['city' => 'Berlin'], AddressLinkType::Office);
|
||
|
|
|
||
|
|
$user->setPrimaryAddressLink($officeA->id); // Vienna is primary
|
||
|
|
$user->setPrimaryAddressLink($officeB->id); // Berlin is now primary, Vienna is unset
|
||
|
|
```
|
||
|
|
|
||
|
|
**Returns:** `true` on success, `false` if the link ID was not found.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Temporal Validity
|
||
|
|
|
||
|
|
Address links support time-based activation via `active_from` and `active_until`:
|
||
|
|
|
||
|
|
```php
|
||
|
|
$link = $user->addAddress([
|
||
|
|
'street' => 'Summer Cottage Lane 3',
|
||
|
|
'city' => 'Hallstatt',
|
||
|
|
], AddressLinkType::SecondaryResidence, [
|
||
|
|
'active_from' => '2025-06-01',
|
||
|
|
'active_until' => '2025-09-01',
|
||
|
|
]);
|
||
|
|
|
||
|
|
// Check if a specific link is active
|
||
|
|
$link->isActive(); // depends on current date
|
||
|
|
|
||
|
|
// Get only active links
|
||
|
|
$user->activeAddressLinks();
|
||
|
|
|
||
|
|
// Query scopes on AddressLink
|
||
|
|
AddressLink::active()->get();
|
||
|
|
AddressLink::expired()->get();
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Working with the pivot
|
||
|
|
|
||
|
|
When accessing addresses through the `addresses()` relationship, all pivot columns are available:
|
||
|
|
|
||
|
|
```php
|
||
|
|
foreach ($user->addresses as $address) {
|
||
|
|
$address->pivot->type; // "office"
|
||
|
|
$address->pivot->label; // "Main Office"
|
||
|
|
$address->pivot->is_primary; // true
|
||
|
|
$address->pivot->active_from; // "2025-01-01 00:00:00"
|
||
|
|
$address->pivot->active_until; // null
|
||
|
|
$address->pivot->meta; // "{...}"
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Full Example
|
||
|
|
|
||
|
|
```php
|
||
|
|
use App\Models\User;
|
||
|
|
use Blax\Addresses\Enums\AddressLinkType;
|
||
|
|
|
||
|
|
$user = User::create(['name' => 'Jane Doe', 'email' => 'jane@example.com']);
|
||
|
|
|
||
|
|
// Add a home address (primary)
|
||
|
|
$home = $user->addAddress([
|
||
|
|
'street' => 'Musterstraße 42',
|
||
|
|
'postal_code' => '1010',
|
||
|
|
'city' => 'Vienna',
|
||
|
|
'country_code' => 'AT',
|
||
|
|
'latitude' => 48.2082,
|
||
|
|
'longitude' => 16.3738,
|
||
|
|
], AddressLinkType::Home, [
|
||
|
|
'is_primary' => true,
|
||
|
|
]);
|
||
|
|
|
||
|
|
// Add an office address
|
||
|
|
$office = $user->addAddress([
|
||
|
|
'street' => 'Kärntner Straße 21',
|
||
|
|
'postal_code' => '1010',
|
||
|
|
'city' => 'Vienna',
|
||
|
|
'country_code' => 'AT',
|
||
|
|
], AddressLinkType::Office, [
|
||
|
|
'label' => 'Downtown Office',
|
||
|
|
]);
|
||
|
|
|
||
|
|
// Query
|
||
|
|
$user->hasAddresses(); // true
|
||
|
|
$user->hasAddressOfType(AddressLinkType::Shipping); // false
|
||
|
|
$user->primaryAddress(AddressLinkType::Home); // → Address { Musterstraße 42 }
|
||
|
|
$user->addressesOfType(AddressLinkType::Office); // → Collection with 1 Address
|
||
|
|
|
||
|
|
// Switch primary
|
||
|
|
$newHome = $user->addAddress(['city' => 'Graz'], AddressLinkType::Home);
|
||
|
|
$user->setPrimaryAddressLink($newHome->id);
|
||
|
|
$user->primaryAddress(AddressLinkType::Home); // → Address { city: Graz }
|
||
|
|
|
||
|
|
// Clean up
|
||
|
|
$user->removeAddressLink($office->id); // remove office link
|
||
|
|
$user->detachAllAddresses(); // remove everything
|
||
|
|
```
|