NewWe open-sourced 50+ Laravel packages
Custom AI apps, agents and automation — Roundly ConsultingRoundly
All packages
Addresses for Laravel

Database schema & key types

The migration creates one table, addresses. Every owner shares it through the addressable morph pair:

$keyType = KeyType::fromConfig('addresses.key_type');

Schema::create('addresses', function (Blueprint $table) use ($keyType): void {
    $table->id();
    $table->morphKey('addressable', $keyType, nullable: false);   // morphs / uuidMorphs / ulidMorphs
    $table->boolean('is_primary')->default(false);
    $table->string('type')->default('default');
    $table->string('name')->nullable();
    $table->string('city')->nullable();
    $table->string('street')->nullable();
    $table->string('postal_code')->nullable();
    $table->string('country_iso')->nullable();
    $table->jsonb('meta')->nullable();
    $table->timestamps();
    $table->softDeletes();
});

$this->onePrimaryPerOwnerAndType();   // PostgreSQL and SQLite: partial unique index
ColumnTypeNotes
idbigint, auto-incrementPrimary key — always an integer, whatever key_type says.
addressable_type / addressable_idmorph pairThe owner. The id column follows key_type; indexed together; not nullable.
is_primarybooleanDefaults to false.
typestringDefaults to 'default'; stores the AddressType value.
namestring, nullableOptional label, e.g. HQ.
city, street, postal_codestring, nullableThe address lines — required by the builder and DTO, nullable in the schema.
country_isostring, nullableISO code, normalised on write.
metajsonb, nullableFree-form extras; cast to a Collection.
created_at, updated_attimestamps—
deleted_attimestamp, nullableSoft deletes.
  • There is no country_name column — the name is resolved at read time by your CountryResolver.
  • At most one live primary per owner and type: the package’s actions promote under a lock on the owner + type group, and on PostgreSQL and SQLite the migration also adds the partial unique index addresses_one_primary_per_type on (addressable_type, addressable_id, type) where is_primary and deleted_at is null — soft-deleted rows hold no slot.
  • The migration is forward-only — it has no down() method.

Owner key types

key_type describes the models that own addresses — never the addresses table itself, which always keeps an auto-incrementing id. It picks the morph column the migration creates:

key_typeaddressable_id columnUse when
bigintunsigned big integer — morphs()Owners use auto-incrementing keys. The default.
uuiduuid — uuidMorphs()Owners use Laravel’s HasUuids.
ulidchar(26) — ulidMorphs()Owners use Laravel’s HasUlids.

All models that own addresses must share one key type. Set it before you migrate — an unrecognised value throws InvalidConfigurationException when the migration runs, naming the key and the value:

# .env — set before you publish and run the migration
ADDRESSES_KEY_TYPE=uuid
use Illuminate\Database\Eloquent\Concerns\HasUuids;
use Illuminate\Database\Eloquent\Model;
use RoundlyConsulting\Addresses\Traits\HasAddresses;

class Customer extends Model
{
    use HasAddresses;
    use HasUuids;
}

Show your open-source love

This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.

More ways to support, including crypto

By donating, you agree to our donation terms.

Want this built into your product?

We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.