laravel-addresses/database/migrations/create_blax_address_tables....

212 lines
10 KiB
Plaintext
Raw Normal View History

2026-04-14 08:20:42 +00:00
<?php
namespace Blax\Addresses\Migrations;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* Creates two tables:
* - `addresses` stores the physical address data (location, coordinates, etc.)
* - `address_links` polymorphic pivot linking addresses to any Eloquent model
*/
public function up(): void
{
/*
|----------------------------------------------------------------------
| addresses — the canonical address record
|----------------------------------------------------------------------
|
| Designed to support worldwide formats, from a GPS point in the
| wilderness to a specific room inside a skyscraper.
|
| Coordinate system:
| latitude / longitude → WGS-84 decimal degrees
| altitude → metres Above Mean Sea Level (AMSL)
|
*/
Schema::create(config('addresses.table_names.addresses', 'addresses'), function (Blueprint $table) {
$table->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'));
}
};