laravel-addresses/docs/has-addresses.md

328 lines
8.1 KiB
Markdown
Raw Normal View History

2026-04-14 08:20:42 +00:00
# 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
```