yajra/laravel-address
A Laravel package for address management. It provides models, migrations, and helpers for countries, states, and cities, making it easy to attach structured addresses to Eloquent models and build reliable address forms or APIs.
Architecture fit. Eloquent-native, which is the whole point: the package ships models, migrations, seeders, and a morphed Address relation that attaches to any entity, so it slots into an existing Laravel domain model without a new service or datastore. Config-driven table names and model bindings (config/address.php) make it an extension point rather than a fork target — we can add delivery_zone_id, geocoordinates, or an active flag on our own subclasses and repoint config. The surface area is small and the dependency footprint is Laravel-only, so there is no architectural argument against it; the argument is entirely about data stewardship and version coupling.
Integration feasibility. Days, not weeks, for a team already on Eloquent: composer require yajra/laravel-address:^13.0, auto-discovery, publish config + migrations + seeders, migrate, seed. The real effort is not the install — it is the write-path decision (store canonical codes and snapshot human-readable names into denormalized columns), the AddressResolver contract, and legacy free-text backfill if we already hold addresses. Plan for the install to be a half-day and the surrounding correctness work to be the sprint.
Technical risk.
| Risk | Severity | Why it matters to the product |
|---|---|---|
| Dataset freshness is invisible | High | The PSGC snapshot is bundled in the package. A stale dataset throws no errors — it silently decays and surfaces months later as failed deliveries to a renamed barangay. Freshness arrives only via composer update. |
| Data refresh coupled to major bumps | High | v13.0.0 shipped Laravel 13 support and the 1Q 2026 PSGC publication together. Pinned to v12.x for framework reasons means we are also on an older dataset. Framework upgrade and data refresh are not independent levers. |
| Adoption signal is weak | Medium | 41 stars, 0 dependents. Mitigated by MIT license, small surface area, and a recognizable maintainer, but there is no community cohort absorbing the edge cases before we do. |
| Single-maintainer bus factor | Medium | No commercial SLA. The AddressResolver contract is the hedge; without it we are exposed. |
| Structural model breaks the naive cascade | Medium | NCR has no provinces; independent/chartered cities sit outside provincial jurisdiction. province_code must be nullable and the UI must handle the empty-province branch, or the largest PH market is broken on day one. |
| "City" means city or municipality | Medium | Labeling ambiguity that propagates into every schema, query, and support conversation if we don't normalize terminology early. |
| Index assumptions | Medium | Reference-data packages commonly skip FK indexes assuming small tables — wrong at ~42k barangays. Must verify shipped migrations rather than benchmark blind. |
| Documentation/implementation discrepancy | Medium | Repo prose says "Philippines Address Lookup"; the generic description mentions countries/states/cities. Trust migrations and models over prose before designing against a countries table that may not exist. |
| Scope confusion: enumerate ≠ verify | High (product) | The package produces a correct list to select from. It does not confirm the address is real, deliverable, or geocodable. Stakeholders conflate these and silently over-scope toward a geocoding contract nobody budgeted. |
Key questions to close before committing.
province_code nullable and is the chain Region → Province → City/Municipality → Barangay strict or lenient?regions/provinces/barangays present, or is the "countries/states" description real?Stack fit. Laravel 12/13 with Eloquent and PHP 8.5 is the tested matrix. If we are not on a supported Laravel major, the conversation ends — there is no framework-agnostic fallback, and the dataset is not separately consumable. Confirmed fit is essentially a prerequisite, not an evaluation criterion.
Compatibility and pinning. Match the package major to the Laravel major before running composer: ^13.0 ↔ Laravel 13 with the 1Q 2026 PSGC; v12.x ↔ Laravel 12 with an older snapshot. Decide this as a joint framework/data decision, not two separate ones — a framework pin silently pins geography. Pin exactly in composer.json and record the bundled PSGC release in the README next to the dependency.
Migration path.
code — never truncate. Retire discontinued codes with retired_at/active rather than deleting, so historical orders still resolve.Sequencing (recommended).
DB::disableQueryLog(). Inspect actual schema and indexes. (~2–3 days; produces the answers to most key questions.)AddressResolver (regions, provinces, cities, barangays, validateChain) and bind an Eloquent implementation. This is the hedge against stale data, maintainer risk, and any future pivot to Google Places/Loqate.region_code/province_code — never by name strings.Maintenance. Two recurring line items, both small and predictable, neither of which exists unless we create it: (a) a quarterly "review upstream release" task to catch PSGC publications and decide on adoption; (b) a per-framework-cycle upgrade ticket, since the major tracks Laravel's. Add a smoke test asserting row counts and a handful of known codes so a silent data regression fails CI rather than a customer's delivery.
Support. MIT, community-only, no SLA, single maintainer. We own the failure. Practical mitigation: keep the package behind AddressResolver, keep customizations in subclasses via config bindings rather than forking, and keep a documented fallback plan (a static PSGC import into our own tables) so a maintainer hiatus is an inconvenience rather than an incident.
Scaling. Read-mostly reference data; ~42k barangays is trivial for any relational DB and will not be the scaling bottleneck. The real scaling costs are elsewhere:
Barangay::with('city.province.region') across a page is fine; inside a loop it is not. Resolve codes once, reuse.Failure modes to design against.
| Failure | Symptom | Control |
|---|---|---|
| Stale dataset | Failed deliveries to renamed barangays, no error surfaced | Version pin + quarterly review + known-code smoke test |
| Truncate on refresh | Historical orders lose their addresses | Upsert keyed on code; soft-retire instead of delete |
| Table rename after migrate | Half-renamed schema | Publish config + migrations in one pass; rename before migrating |
| NCR / independent city | Cascade UI breaks in the largest PH market | Nullable province_code, explicit empty-province UI branch |
| Chain spoofing | Cebu barangay filed under a Manila city | Server-side ancestor derivation; never trust client parent codes |
| Scope creep to validation | Stakeholder expects rooftop verification | Write enumerate-vs-verify into the PRD explicitly |
| Seeder in CI | CI runtime collapses | Trimmed fixture dataset only |
Ramp-up. Low for any Laravel developer — hours to productive, given models and migrations are provided. The non-obvious learning is domain, not code: PSGC hierarchy quirks, the city/municipality conflation, and the discipline of codes-plus-snapshot. Budget a short domain briefing for whoever owns the checkout and fulfillment paths, because that person — not the integrator — will be answering "why did this address change?" six months from now.
Bottom line. Cheap to adopt, cheap to reverse if wrapped properly, and it converts a multi-sprint data-sourcing and cleansing project into a configuration task — which is the actual product value here. The risks are concentrated in data currency and single-maintainer dependency, both manageable with a version pin, a quarterly calendar item, and the resolver contract. The deal-breaker conditions are narrow: not on a supported Laravel major, or a requirement for rooftop-level address verification.
How can I help you explore Laravel packages today?