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| Column | Type | Notes |
|---|---|---|
id | bigint, auto-increment | Primary key — always an integer, whatever key_type says. |
addressable_type / addressable_id | morph pair | The owner. The id column follows key_type; indexed together; not nullable. |
is_primary | boolean | Defaults to false. |
type | string | Defaults to 'default'; stores the AddressType value. |
name | string, nullable | Optional label, e.g. HQ. |
city, street, postal_code | string, nullable | The address lines — required by the builder and DTO, nullable in the schema. |
country_iso | string, nullable | ISO code, normalised on write. |
meta | jsonb, nullable | Free-form extras; cast to a Collection. |
created_at, updated_at | timestamps | — |
deleted_at | timestamp, nullable | Soft 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_type | addressable_id column | Use when |
|---|---|---|
bigint | unsigned big integer — morphs() | Owners use auto-incrementing keys. The default. |
uuid | uuid — uuidMorphs() | Owners use Laravel’s HasUuids. |
ulid | char(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=uuiduse 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 cryptoBy 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.