1. Go to this page and download the library: Download byte5/addressable library. Choose the download type require.
2. Extract the ZIP file and open the index.php.
3. Add this code to the index.php.
<?php
require_once('vendor/autoload.php');
/* Start to develop here. Best regards https://php-download.com/ */
namespace App\Models;
use Byte5\Addressable\App\Models\Address as BaseAddress;
use Illuminate\Database\Eloquent\Concerns\HasUuids;
class Address extends BaseAddress
{
use HasUuids;
}
use Byte5\Addressable\App\Concerns\HasAddresses;
use Byte5\Addressable\App\Contracts\Addressable;
class User extends Model implements Addressable
{
use HasAddresses;
}
// All addresses (morphMany)
$user->addresses;
$user->addresses()->create([
'street' => 'Main 10',
'postal' => '10115',
'city' => 'Berlin',
'region' => 'Berlin',
'country' => 'DE',
'type' => 'billing', // optional role/label
]);
// The most recently attached address (morphOne)
$user->latestAddress;
// The owning model from an address (morphTo)
$address->addressable;
use Byte5\Addressable\App\Data\AddressData;
use Byte5\Addressable\App\Enums\AddressType;
// From a typed DTO
$user->addAddress(new AddressData(
street: 'Pariser Platz 1',
postal: '10117',
city: 'Berlin',
country: 'DE',
), AddressType::Billing);
// From a loose array — internally calls AddressData::fromArray()
$user->addAddress([
'street' => 'Pariser Platz 1',
'postal' => '10117',
'city' => 'Berlin',
'country' => 'DE',
'type' => 'billing',
]);
// From a resolved Google Place
$details = AddressLookup::details($placeId); // PlaceDetails
$data = $details->toAddressData(AddressType::Shipping);
// From a schema.org PostalAddress DTO
$postal = $address->toSchemaOrg(); // PostalAddress
$data = $postal->toAddressData(AddressType::Billing);
use Byte5\Addressable\App\Contracts\CreatesAddresses;
$this->app->bind(CreatesAddresses::class, MyDedupingAddressCreator::class);
use Byte5\Addressable\App\Rules\AddressExists;
use Byte5\Addressable\App\Rules\Country;
use Byte5\Addressable\App\Rules\PostalFormat;
public function rules(): array
{
return [
'country' => ['untry inputs and makes a single
// (billable) provider call per validation run.
'street' => ['
use Byte5\Addressable\App\Facades\AddressRules;
AddressRules::country(); // new Country()
AddressRules::postalFormat('DE'); // new PostalFormat('DE')
AddressRules::exists(); // new AddressExists()
new AddressExists(['postal' => 'zip_code', 'country' => 'address.country']);
use Byte5\Addressable\App\Facades\Countries;
Countries::list(); // ['DE' => 'Germany', 'FR' => 'France', …] in the app locale
Countries::list('de'); // ['DE' => 'Deutschland', 'FR' => 'Frankreich', …]
// config/byte5-addressable.php
// Autocomplete + geocoding (AddressLookup::suggest/details)
'lookup' => [
'provider' => env('ADDRESSABLE_LOOKUP_PROVIDER', 'google'),
'providers' => [
'google' => [
'key' => env('ADDRESSABLE_LOOKUP_GOOGLE_KEY'),
'language' => env('ADDRESSABLE_LOOKUP_GOOGLE_LANGUAGE'), // Places languageCode; falls back to app locale
'region' => env('ADDRESSABLE_LOOKUP_GOOGLE_REGION'), // region bias (regionCode)
'country' => env('ADDRESSABLE_LOOKUP_GOOGLE_COUNTRY'), // single ISO 3166-1 alpha-2 code (set an array in the published config for multiple)
],
],
],
// Address validation (AddressValidator::validate + the AddressExists rule) — separate provider + key
'validation' => [
'provider' => env('ADDRESSABLE_VALIDATION_PROVIDER', 'google'),
// How the AddressExists rule reacts when the provider is unreachable:
// false (default) surfaces the error; true lets the address through.
'pass_on_outage' => env('ADDRESSABLE_VALIDATION_PASS_ON_OUTAGE', false),
'providers' => [
'google' => [
'key' => env('ADDRESSABLE_VALIDATION_GOOGLE_KEY'),
],
],
],
use Byte5\Addressable\App\Facades\AddressLookup;
// 1. Suggestions as the user types
$suggestions = AddressLookup::suggest('Branden');
// => Byte5\Addressable\App\Data\Suggestion[] { placeId, description, mainText, secondaryText }
// 2. Resolve the chosen suggestion into a structured address
$details = AddressLookup::details($suggestions[0]->placeId);
// => Byte5\Addressable\App\Data\PlaceDetails (or null if not found)
// 3. Persist it — toArray() matches the Address columns
$user->addresses()->create($details->toArray());
use Byte5\Addressable\App\Data\AddressInput;
use Byte5\Addressable\App\Facades\AddressValidator;
$validation = AddressValidator::validate(new AddressInput(
street: 'Pariser Platz 1',
postal: '10117',
city: 'Berlin',
country: 'DE', // ISO 3166-1 alpha-2 (regionCode)
));
$validation->valid; // normalised: deliverable / exists (every provider)
$validation->provider; // 'google'
$validation->formattedAddress; // standardised address
$validation->raw; // full provider payload
// Typed Google specifics — narrow to the provider's result:
use Byte5\Addressable\App\Data\GoogleAddressValidation;
if ($validation instanceof GoogleAddressValidation) {
$validation->granularity; // PREMISE | SUB_PREMISE | ROUTE | LOCALITY | OTHER | ...
$validation->complete; // Google addressComplete
$validation->hasUnconfirmedComponents;
$validation->hasInferredComponents;
$validation->hasReplacedComponents;
}
use Byte5\Addressable\App\Events\AddressResolved;
use Illuminate\Support\Facades\Event;
Event::listen(function (AddressResolved $event) {
logger()->info("Resolved {$event->placeId} via {$event->provider}", $event->details->toArray());
});
use Byte5\Addressable\App\Facades\AddressLookup;
use Livewire\Component;
class AddressForm extends Component
{
public string $query = '';
/** @var array<int, array{placeId: string, description: string}> */
public array $suggestions = [];
// Bound form fields — filled from the chosen address.
public ?string $street = null;
public ?string $postal = null;
public ?string $city = null;
public ?string $region = null;
public ?string $country = null;
public function updatedQuery(): void
{
// Map to plain arrays — Livewire only serialises primitive/array props,
// not the Suggestion DTO.
$this->suggestions = strlen($this->query) >= 3
? array_map(
fn ($s) => ['placeId' => $s->placeId, 'description' => $s->description],
AddressLookup::suggest($this->query),
)
: [];
}
public function select(string $placeId): void
{
if ($details = AddressLookup::details($placeId)) {
$this->street = $details->street;
$this->postal = $details->postal;
$this->city = $details->city;
$this->region = $details->region;
$this->country = $details->country;
}
$this->suggestions = [];
}
public function render()
{
return view('livewire.address-form');
}
}
use Byte5\Addressable\App\Facades\AddressLookup;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
Route::middleware(['auth', 'throttle:30,1'])->group(function () {
Route::get('/address/suggest', fn (Request $r) => AddressLookup::suggest($r->string('q')));
Route::get('/address/details/{placeId}', fn (string $placeId) => AddressLookup::details($placeId));
});