Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Addressable Laravel Package

maggomann/addressable

Maggomann Addressable is a Laravel package that adds polymorphic address support to your Eloquent models, letting any model store one or more addresses through a simple trait and relation.

View on GitHub
Deep Wiki
Context7

Getting Started

composer require maggomann/addressable
php artisan vendor:publish --tag="addressable-config"
php artisan vendor:publish --tag="addressable-migrations"
php artisan migrate

Then load the reference data with the package's install/seed command (run php artisan list | grep addressable to see the exact name in your version — it ships an install-style command that seeds countries, subdivisions, and cities). Seeding is the slow part; do it once locally and snapshot the DB for teammates rather than re-seeding on every migrate:fresh.

Look first at:

  1. config/addressable.php — table names, model bindings, locale/translation settings.
  2. Maggomann\Addressable\Models\{Country,Subdivision,City} — relations and scopes.
  3. The published migrations — column names (iso_2, iso_3, name) and FK shape, before you write your own addresses table against them.

First use case: a cascading country → subdivision → city select. Country list is small; subdivisions and cities should be fetched per-parent over AJAX, not embedded in the initial page.

$countries = Country::query()->orderBy('name')->get(['id', 'iso_2', 'name']);

Implementation Patterns

Store codes, not IDs, at the edges. Persist iso_2 on your own addresses table (plus a nullable subdivision/city FK). ISO codes survive a re-seed; auto-increment IDs do not. Keep the FK only where you need referential integrity and accept that a re-seed may require re-linking.

Cascading selects. One endpoint per level, keyed by parent code:

Route::get('/geo/subdivisions/{country:iso_2}', fn (Country $country) =>
    $country->subdivisions()->orderBy('name')->get(['id', 'name'])
);

Use route-model binding on iso_2 so the controller stays thin, and return only the columns the <select> needs.

Validation. Validate against the package tables directly rather than duplicating lists:

'country'      => ['required', Rule::exists('addressable_countries', 'iso_2')],
'subdivision' => ['nullable', Rule::exists('addressable_subdivisions', 'id')
    ->where('country_id', $countryId)],

The where clause matters — a subdivision ID from the wrong country is a real bug class in address forms.

Cache the country list. It changes only when you re-seed:

$countries = Cache::rememberForever('geo.countries', fn () =>
    Country::orderBy('name')->get(['iso_2', 'name'])
);

Bust the key in your seeder/import command, not on a timer.

Translations. If you expose localized names, eager-load the translation relation and always fall back to the base name; translated rows can be missing for a locale. Don't call a translation accessor inside a loop over 250 countries without with().

Your own address model. Extend or reference the package models but keep your Address model separate — it will grow fields (line1, postcode, recipient) the package should not own. Add a small HasAddress/Addressable trait on your side for formatting.

Testing. Seed reference data once per test suite, not per test. A base TestCase that migrates + seeds and wraps each test in a transaction is far faster than RefreshDatabase plus a full city import. For unit tests that only need a country, insert the handful of rows you need by hand.

Gotchas and Tips

  • Pre-1.0 version. v0.2.2 is a patch, and the changelog between 0.2.1 and 0.2.2 is empty, so there is no migration guide to read. Pin exactly ("maggomann/addressable": "0.2.2") and diff the published migrations on every bump — 0.x minors can rename columns without a deprecation cycle.
  • Seeding volume. The cities table is large. Wrap imports in a transaction, chunk inserts, and disable FK checks during the seed (SET FOREIGN_KEY_CHECKS=0 on MySQL). SQLite will crawl or hit variable limits; use MySQL/Postgres for the initial import and copy the resulting DB.
  • Migration order. Package migrations must run before your addresses FK migration. If you publish them after your own migrations were timestamped, rename or re-order, otherwise migrate:fresh fails on a clean machine.
  • Never hardcode table names. They are configurable in config/addressable.php. Use (new Country)->getTable() in raw queries, or you will break for anyone who changes the prefix.
  • N+1 on nested payloads. Country::with('subdivisions.cities') across a full country list is enormous. Paginate or fetch per-parent.
  • Name collisions. City and subdivision names are not unique, and neither are some country names in translation. Match on ISO codes, never on name.
  • migrate:fresh --seed becomes unusable once the geo seed is in DatabaseSeeder. Keep reference-data seeding in a separate command so app seeders stay fast.
  • Extension points are the config model bindings and publishable migrations — if you need extra columns on countries, prefer a side table over editing published migrations, or the next version's migration will collide with yours.
  • Debugging empty selects is almost always an unseeded table or a locale with no translation rows, not a relation bug. Check row counts before touching the query.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource
noith/settings