laravel-addresses/README.md

206 lines
7.3 KiB
Markdown

[![Blax Software OSS](https://raw.githubusercontent.com/blax-software/laravel-workkit/master/art/oss-initiative-banner.svg)](https://github.com/blax-software)
# Laravel Addresses
[![PHP Version](https://img.shields.io/badge/php-%5E8.1-blue)](https://php.net)
[![Laravel](https://img.shields.io/badge/laravel-9.x--12.x-orange)](https://laravel.com)
Universal Laravel address management — from rural GPS coordinates to specific rooms inside skyscrapers, worldwide.
## Overview
This package provides a complete address management system for Laravel applications built on a **three-layer architecture**:
```
Address → The physical place (street, city, coordinates …)
└── AddressLink → Connects an address to a model with a purpose (User's "Office")
└── AddressAssignment → References a link from another context (Job's "pickup")
```
**Example:** A user has an office address. A job references that office as its pickup location — without duplicating the address data.
## Features
- **15 address fields** — street-level to room-level precision, with GPS coordinates (WGS-84) and altitude
- **Polymorphic links** — attach addresses to any Eloquent model
- **17 built-in link types** — Home, Office, Shipping, Billing, Warehouse and more
- **Address assignments** — reference someone else's address in another context
- **Temporal validity** — `active_from` / `active_until` on every link
- **AddressService** — distance calculations (Haversine), proximity queries, duplicate detection, coordinate conversion
- **Auto-geocoding** — saves call Nominatim (OpenStreetMap) for lat/lon, serialized by a Cache lock and paced at 1 req/sec
- **Fully configurable** — custom model classes, table names, default link type
- **Soft deletes** on addresses, cascade deletes on links and assignments
## Requirements
- PHP 8.1+
- Laravel 9, 10, 11 or 12
- `blax-software/laravel-workkit` (installed automatically)
## Installation
```bash
composer require blax-software/laravel-addresses
```
Publish and run the migrations:
```bash
php artisan vendor:publish --tag="addresses-migrations"
php artisan migrate
```
Optionally publish the config:
```bash
php artisan vendor:publish --tag="addresses-config"
```
## Quick Start
### 1. Add the trait to your model
```php
use Blax\Addresses\Traits\HasAddresses;
class User extends Model
{
use HasAddresses;
}
```
### 2. Create and attach an address
```php
use Blax\Addresses\Enums\AddressLinkType;
$link = $user->addAddress([
'street' => '350 Fifth Avenue',
'city' => 'New York',
'state' => 'NY',
'postal_code' => '10118',
'country_code' => 'US',
'latitude' => 40.748817,
'longitude' => -73.985428,
], AddressLinkType::Office);
```
### 3. Query addresses
```php
$user->addresses; // all addresses
$user->addressesOfType(AddressLinkType::Office); // only offices
$user->primaryAddress(); // primary across all types
$user->activeAddressLinks(); // only currently active links
```
### 4. Assign an address to another model
```php
use Blax\Addresses\Traits\HasAddressAssignments;
class Job extends Model
{
use HasAddressAssignments;
}
$job->assignAddressLink($link, 'pickup');
$job->assignedAddressForRole('pickup'); // → the Address model
```
### 5. Automatic geocoding (opt-in)
Set `ADDRESSES_GEOCODING_ENABLED=true` to have an observer ask Nominatim
(OpenStreetMap) for `latitude` / `longitude` after every save. Calls are
serialized cluster-wide through a `Cache::lock` and paced at 1 req/sec
to honour the OSMF usage policy. **Off by default** so an upgrade
doesn't surprise existing apps with new outbound HTTP traffic.
```ini
# .env
ADDRESSES_GEOCODING_ENABLED=true
# OSMF policy: identify your app — generic UAs get blocked.
ADDRESSES_GEOCODING_USER_AGENT="my-app (https://example.com)"
ADDRESSES_GEOCODING_EMAIL="ops@example.com"
```
```php
$address = Address::create([
'street' => 'Stephansplatz 1',
'postal_code' => '1010',
'city' => 'Vienna',
'country_code' => 'AT',
]);
// Observer has filled these in by the time create() returns.
$address->refresh();
$address->latitude; // 48.2082…
$address->longitude; // 16.3738…
```
Tunable knobs (see [`config/addresses.php`](config/addresses.php) →
`geocoding`):
- `enabled` — master switch (env: `ADDRESSES_GEOCODING_ENABLED`)
- `driver` — only `nominatim` ships out of the box, but you can rebind
the `Geocoder` contract to plug in Google Maps / Mapbox / Mapquest /
a self-hosted Nominatim
- `update_only_when_missing` — leave manually-entered coordinates alone
- `min_interval_seconds` — global ceiling on outbound traffic (default
`1.0`, matches Nominatim's published policy)
- `lock_wait_seconds`, `lock_ttl_seconds` — Cache lock parameters
- `accept_language` — Nominatim's `display_name` localization
- `drivers.nominatim.user_agent` / `email` — required by Nominatim for
attribution; set both in production
For tests / imports, disable per-environment with
`ADDRESSES_GEOCODING_ENABLED=false` (or
`config()->set('addresses.geocoding.enabled', false)` inside a TestCase
hook).
### 6. Use the AddressService
```php
// Via helper
$distance = address()->distanceBetween($addressA, $addressB); // km
// Nearby addresses within 10 km
$nearby = address()->nearby(48.2082, 16.3738, 10);
// Format for display
echo address()->formatMultiline($address);
```
## Documentation
| Guide | Description |
|----------------------------------------------------------------|------------------------------------------------------|
| [Installation & Configuration](docs/installation.md) | Setup, publishing, config options |
| [Core Concepts](docs/core-concepts.md) | The three-layer architecture explained |
| [HasAddresses Trait](docs/has-addresses.md) | Full API for address-owning models |
| [HasAddressAssignments Trait](docs/has-address-assignments.md) | Full API for address-consuming models |
| [AddressService](docs/address-service.md) | Distance, proximity, formatting, conversion |
| [Geocoding](docs/geocoding.md) | Auto lat/lon via Nominatim, cache lock, rate limit |
| [AddressLinkType Enum](docs/address-link-types.md) | All 17 built-in types with descriptions |
| [Customization](docs/customization.md) | Extending models, custom tables, overriding defaults |
## Testing
```bash
composer test
```
## License
MIT
## Star History
<a href="https://www.star-history.com/?repos=blax-software%2Flaravel-addresses&type=date&legend=top-left">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/chart?repos=blax-software/laravel-addresses&type=date&theme=dark&legend=top-left" />
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/chart?repos=blax-software/laravel-addresses&type=date&legend=top-left" />
<img alt="Star History Chart" src="https://api.star-history.com/chart?repos=blax-software/laravel-addresses&type=date&legend=top-left" />
</picture>
</a>