Download the PHP package byte5/addressable without Composer
On this page you can find all versions of the php package byte5/addressable. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Download byte5/addressable
More information about byte5/addressable
Files in byte5/addressable
Informations about the package addressable
Addressable
A small Laravel package for attaching schema.org-aligned postal addresses to any Eloquent model via a polymorphic relationship.
Requirements
- PHP 8.2+
- Laravel 12 or 13
Installation
Install via Composer:
The service provider is auto-discovered. Publish the migration, then migrate:
The migration is published rather than loaded from the package so you can edit it before migrating — see Owner morph key below for UUID/ULID keys.
Publishing the config is optional; its defaults are merged automatically. Publish it only when you want to change a default:
Configuration
The config is merged under the byte5-addressable key. Publish it to change any
default:
Anything that affects the schema (
table_names,column_names) must be set, and the migration edited if needed, before running it. Changing it afterwards requires a rollback and re-migrate.
Models — swap the Address model
models.address is the Eloquent model used for addresses. To customise it (most
commonly to give addresses UUID/ULID primary keys), extend the package model, add
the relevant Laravel trait, and register your class:
The HasAddresses trait and the factory both resolve the model from this config
value, so your subclass is used everywhere.
Owner morph key — uuid / ulid
The polymorphic key that points at the owner model defaults to a
bigint unsigned column named addressable_id. Two things control it:
column_names.model_morph_key— the column name (rename to e.g.addressable_uuidif you prefer).- The column type lives in the published migration. If your owner models use UUID/ULID primary keys, edit it before migrating:
The Address model's own primary key is independent of this — it stays a
bigint unless you swap in a model using HasUuids/HasUlids (see Models).
Table name
table_names.addresses is the table used to store addresses (default
addresses). Both the migration and the Address model read it.
Address type enum
The type column is cast to a string-backed enum. The package ships a default
Byte5\Addressable\App\Enums\AddressType (billing, shipping, home, work). You can:
-
Replace it with your own enum to match your application's address roles:
- Disable casting and keep
typea plain string:
Your enum must be string-backed, and its backing values must match the values
already stored in the type column — switching the enum does not migrate
existing data. Rows with a type that isn't a valid case will throw on read.
No migration change is needed: the column stays a string; the enum is only the
application-layer representation.
Usage
Add the HasAddresses trait to any model that should own addresses:
You then get:
Creating addresses
$model->addAddress($data, $type) is the standardised entry point for persisting a
new address. It accepts either an AddressData DTO or a loose attribute array, and
an optional AddressType (or its backing string) that overrides whatever type is
already on the data:
Both forms return the persisted Address instance.
AddressData — the write DTO
AddressData is a readonly DTO that carries the nine address fields (type,
street, extra, postal, city, region, latitude, longitude, country).
All fields are optional (default null).
The lookup and schema.org DTOs provide typed bridges:
Pass the resulting AddressData straight to addAddress().
Swapping the creation implementation
Address creation is backed by Byte5\Addressable\App\Contracts\CreatesAddresses
(single method: create(Model&Addressable $owner, AddressData $data): Address). The
package binds the default AddressCreator service as a singleton, but you can
replace it in any service provider:
The package enforces no deduplication, per-type uniqueness, or default/primary address policy — that is intentional. Add whatever cardinality rules your application needs here.
schema.org mapping
The columns map to schema.org/PostalAddress:
| Column | schema.org |
|---|---|
street |
streetAddress |
extra |
extendedAddress |
postal |
postalCode |
city |
addressLocality |
region |
addressRegion |
country |
addressCountry (ISO 3166-1 alpha-2) |
latitude, longitude |
GeoCoordinates (on a Place.geo) |
latitude / longitude are stored as decimal(10,8) / decimal(11,8) and cast
to decimal:8.
Emitting a PostalAddress
$address->toSchemaOrg() returns a PostalAddress DTO that renders to either a
PHP array or a JSON-LD string:
toArray() is a fragment (@type, no @context) — nest it inside a parent
entity such as Organization/Person. toJsonLd() is a standalone document
(includes @context) — drop it straight into a <script type="application/ld+json">
tag. Latitude/longitude are intentionally excluded, since a schema.org
PostalAddress has no geo property (those belong on a Place.geo).
Form validation rules
Three Laravel validation rules ship for validating address input (e.g. in a
FormRequest). All three skip empty values, so pair them with required /
nullable / string, which own emptiness.
| Rule | Checks | Needs an API? |
|---|---|---|
Country |
a valid ISO 3166-1 alpha-2 country code (case-insensitive) | no |
PostalFormat |
the postal code matches the format for a given country (no-op for unknown countries or those without one) | no |
AddressExists |
the address is deliverable, via the configured validation provider | yes — see Validating an address |
The AddressRules facade is a small factory for the same rules:
AddressExists reads the address from sibling fields of the validation payload,
defaulting to the keys street, postal, city, region, country. Pass a map
to point at differently-named inputs (dot notation supported):
Because it calls the validation provider, the validation.pass_on_outage config
applies: when the provider is unreachable the rule throws by default, or passes the
value through when pass_on_outage is true.
Messages live in the byte5-addressable translation namespace (keys country,
postal_format, address_exists). Override them by creating
lang/vendor/byte5-addressable/{locale}/validation.php in your app.
Country list
Countries::list() returns an ISO 3166-1 alpha-2 code => name map, localised and
ordered via commerceguys/addressing — ready to drop into a <select>. Every key is
a code the Country rule accepts.
Address lookup (autocomplete + details)
Type-ahead address suggestions and place resolution via a pluggable provider (Google Places by default). The provider is selected in config; a future custom frontend component will call these through your own controller, keeping the API key server-side.
Configuration
Lookup and validation are independent: each has its own config-selected driver
(AddressLookupManager / AddressValidationManager) and its own API key, so you can
mix providers or use one key with both Google APIs enabled.
Set ADDRESSABLE_LOOKUP_GOOGLE_KEY in your .env (the project must have the
Places API (New) enabled). See .env.example for every supported variable.
Language: when no language is given per call or in config, results default to
the application locale (app()->getLocale()), resolved per request. A per-call
language option or the config/env value takes precedence. Use values Google
accepts as a languageCode (e.g. de, en, pt-BR).
Usage
Per-call overrides (and AddressLookup::driver('google')) are available:
Validating an address
Check whether a structured address actually exists / is deliverable via the
AddressValidator facade (backed by AddressValidationManager, separate from
lookup). It uses Google's Address Validation API — a separate SKU that must be
enabled in your Cloud project. The driver implements the
Byte5\Addressable\App\Contracts\ValidatesAddresses capability:
The base AddressValidation is provider-agnostic (valid, provider,
formattedAddress, raw); each driver maps its native verdict into valid. The Google
driver returns a GoogleAddressValidation subclass that adds the typed verdict fields —
through the facade you get the base type, so narrow with instanceof to read them (or
inject the driver directly, which returns the subclass). For Google, valid is true
when the address validates to PREMISE/SUB_PREMISE granularity, addressComplete is
true, and there are no unconfirmed components.
Validation is a separate capability:
validate()lives onValidatesAddresses, not the coreAddressLookupcontract — so a custom driver only implements it if its provider supports validation.
Events
Every lookup dispatches backend events from the driver, so they fire no matter how the lookup is triggered (facade, your own endpoint, a UI component). Use them for usage/cost tracking or to record selected addresses. Nothing is dispatched when a request fails.
| Event | Fires when | Payload |
|---|---|---|
Byte5\Addressable\App\Events\AddressSuggestionsRequested |
a suggest() request completes |
provider, query, options, count |
Byte5\Addressable\App\Events\AddressDetailsRequested |
a details() request completes |
provider, placeId, options, found |
Byte5\Addressable\App\Events\AddressValidationRequested |
a validate() request completes |
provider, address, options, valid |
Byte5\Addressable\App\Events\AddressResolved |
details() resolves a place |
provider, placeId, details (PlaceDetails) |
AddressSuggestionsRequestedfires on every keystroke-drivensuggest()call — debounce or sample in high-traffic listeners.
Building an autocomplete UI (reference)
The package is headless — it ships no UI component and adds no frontend
dependency, so you build the dropdown in whatever your app already uses (Livewire,
Alpine, Vue, Inertia, …). The only integration surface is the AddressLookup
facade; always call it server-side so your API key never reaches the browser.
The flow is the same in every stack:
- user types →
AddressLookup::suggest($query)→ render theSuggestion[] - user picks one →
AddressLookup::details($placeId)→PlaceDetails - fill your form fields from the result via shared state — no events needed.
PlaceDetailsexposesstreet,postal,city,region,country,latitude,longitude, andtoArray()matches theAddresscolumns.
The snippets below are reference starting points to copy and adapt, not shipped components.
Livewire
Alpine + JSON endpoint
When the UI runs in the browser (Alpine, Vue, …) it can't call the facade directly, so add a thin endpoint. Protect it (auth + throttle) — it spends your Google quota.
Testing
All versions of addressable with dependencies
commerceguys/addressing Version ^2.0
illuminate/contracts Version ^12.0|^13.0
illuminate/database Version ^12.0|^13.0
illuminate/http Version ^12.0|^13.0
illuminate/support Version ^12.0|^13.0
league/iso3166 Version ^4.1
saloonphp/saloon Version ^4.0