id(); // ── Street-level addressing ───────────────────────────── // Primary street line (street name + house/building number). $table->string('street')->nullable(); // Additional line for c/o, suite, apartment, P.O. box, etc. $table->string('street_extra')->nullable(); // ── Building / indoor precision ───────────────────────── // Building or complex name (e.g. "Empire State Building", "Block C"). $table->string('building')->nullable(); // Floor / level inside the building (string to allow "GF", "B2", "Mezzanine"). $table->string('floor')->nullable(); // Room, suite or unit number / name. $table->string('room')->nullable(); // ── Postal / administrative divisions ─────────────────── // Postal / ZIP code. $table->string('postal_code')->nullable(); // City, town, village or locality name. $table->string('city')->nullable(); // State, province, canton, prefecture or equivalent. $table->string('state')->nullable(); // County, district or other sub-state administrative area. $table->string('county')->nullable(); // ISO 3166-1 alpha-2 country code (e.g. "AT", "US", "JP"). $table->string('country_code', 2)->nullable(); // ── Coordinates (WGS-84) ──────────────────────────────── // Latitude in decimal degrees (−90 to +90). $table->decimal('latitude', 10, 7)->nullable(); // Longitude in decimal degrees (−180 to +180). $table->decimal('longitude', 10, 7)->nullable(); // Altitude in metres above mean sea level (AMSL). Positive = above, negative = below. $table->decimal('altitude', 10, 2)->nullable(); // ── Additional properties ─────────────────────────────── // Free-form notes (e.g. delivery instructions, landmark descriptions). $table->text('notes')->nullable(); // Flexible JSON bucket for any extra data the consuming app needs // (e.g. Plus Codes, what3words, timezone, formatted display string). $table->json('meta')->nullable(); $table->timestamps(); $table->softDeletes(); // ── Indexes ───────────────────────────────────────────── $table->index('country_code'); $table->index('postal_code'); $table->index('city'); $table->index(['latitude', 'longitude']); }); /* |---------------------------------------------------------------------- | address_links — polymorphic pivot ("addressable") |---------------------------------------------------------------------- | | Links an address to any Eloquent model (User, Company, Order …). | The same address row can be linked to many models, and a single model | can have many addresses (each with a different purpose / type). | | `type` – enum value describing the purpose of this link | `label` – optional free-text override (handy for "Other" type) | `is_primary` – marks ONE link per type per model as the primary | `active_from` – when this link becomes effective | `active_until`– when this link expires / is superseded | `meta` – JSON bucket for developer-defined extra data | */ Schema::create(config('addresses.table_names.address_links', 'address_links'), function (Blueprint $table) { $table->id(); // ── Foreign key to the address ────────────────────────── $table->foreignId('address_id') ->constrained(config('addresses.table_names.addresses', 'addresses')) ->cascadeOnDelete(); // ── Polymorphic owner ─────────────────────────────────── // The model this address is linked to (e.g. App\Models\User, App\Models\Company). $table->morphs('addressable'); // ── Link semantics ────────────────────────────────────── // The purpose of this link, drawn from AddressLinkType enum. $table->string('type')->default('other'); // Optional human-readable label to refine or override the type // (useful when type = "other" or when several links share the same type). $table->string('label')->nullable(); // Whether this is the primary address for this type on the model. $table->boolean('is_primary')->default(false); // ── Temporal validity ─────────────────────────────────── // When this link becomes active (null = immediately). $table->timestamp('active_from')->nullable(); // When this link ceases to be active (null = indefinitely). $table->timestamp('active_until')->nullable(); // ── Extra data ────────────────────────────────────────── // JSON bucket for any developer-defined data on the pivot // (e.g. delivery window, access codes, department reference). $table->json('meta')->nullable(); $table->timestamps(); // ── Indexes ───────────────────────────────────────────── $table->index(['addressable_type', 'addressable_id', 'type'], 'addr_link_owner_type'); $table->index('type'); $table->index('is_primary'); }); /* |---------------------------------------------------------------------- | address_assignments — reference an AddressLink from another context |---------------------------------------------------------------------- | | While an AddressLink says "this Address belongs to User X as their | Office", an AddressAssignment says "Job Y uses that specific link | for its pickup location." | | `role` – context-specific purpose (e.g. "pickup", "delivery", "origin") | `label` – optional free-text label | `meta` – JSON bucket for developer-defined data | */ Schema::create(config('addresses.table_names.address_assignments', 'address_assignments'), function (Blueprint $table) { $table->id(); // ── Foreign key to the address link ───────────────────── $table->foreignId('address_link_id') ->constrained(config('addresses.table_names.address_links', 'address_links')) ->cascadeOnDelete(); // ── Polymorphic consumer ──────────────────────────────── // The model this address link is assigned to (e.g. Job, Order, Event). $table->morphs('assignable'); // ── Assignment semantics ──────────────────────────────── // Context-specific role for this assignment (e.g. "pickup", "delivery"). $table->string('role')->nullable(); // Optional human-readable label. $table->string('label')->nullable(); // ── Extra data ────────────────────────────────────────── $table->json('meta')->nullable(); $table->timestamps(); // ── Indexes ───────────────────────────────────────────── $table->index(['assignable_type', 'assignable_id', 'role'], 'addr_assign_owner_role'); $table->index('role'); }); } /** * Reverse the migrations. */ public function down(): void { Schema::dropIfExists(config('addresses.table_names.address_assignments', 'address_assignments')); Schema::dropIfExists(config('addresses.table_names.address_links', 'address_links')); Schema::dropIfExists(config('addresses.table_names.addresses', 'addresses')); } };