artesaos/cidadesbr
Laravel package that ships a ready-to-use database of Brazilian states and cities, complete with migrations, seeders, Eloquent models and relationships, plus query helpers for filtering cities by state—ideal for address forms and select inputs.
Architecture fit. CidadesBR is a data-shipping package rather than a service package: it injects migrations, seeders and Eloquent models for states and cities directly into the host application's schema and namespace. That is an acceptable pattern for reference data in a Laravel app, but it is architecturally opinionated in ways that matter to a TPM:
addresses/cities table or a location model from another vendor (ERP, freight, tax), this package will either collide or force a parallel source of truth.Artesaos\CidadesBr\Models\City and whatever the migration names the tables. Every call site becomes a migration cost if we later swap to the IBGE API or an in-house dataset.Integration feasibility. This is the dominant risk, and it is a hard blocker rather than a nuisance. The last release is 2015-07-15 — Laravel 5.0/5.1 era. Expect:
illuminate/support ~5.0, which will fail dependency resolution outright on Laravel 9/10/11. This is not a warning we can ignore; install simply won't complete.provides(), deferred providers, possibly App::make) that were removed or changed in 5.8+/6+.declare(strict_types=1), likely PHP 5.4+ syntax. On PHP 8.2+ we should expect deprecation noise at minimum.utf8mb4 defaults — relevant because Brazilian municipality names are accent-heavy ("São Paulo", "Bragança Paulista", "Belém").Net: viable only on a legacy Laravel 5.x application, or after a fork. Treat "install as-is" as a non-option for any current stack.
Technical risk.
| Risk | Severity | Notes |
|---|---|---|
| Abandonment | High | Single 2015 release, patch note references only issue #3, 53 stars, no CI/tests visible. Assume no upstream fixes. |
| Data staleness | Medium | IBGE municipality data is largely stable (~5,570 municipalities) but does change — boundary/name revisions and occasional municipality creation. Frozen-2015 data will have known gaps. |
| Schema conflict | Medium | Unprefixed states/cities tables are a common collision. Verify whether names are configurable. |
| IBGE code fidelity | Medium | If primary keys are the official IBGE codes, we get free interoperability with freight/tax/IBGE APIs. If they are surrogate auto-increments, we inherit a mapping problem forever. Must confirm before committing. |
| License/IP | Low-Medium | Confirm MIT (or similar) and that the bundled IBGE data carries no redistribution constraint — IBGE data is generally public, but the package's own license must be verified for a fork. |
| Security surface | Low | No network calls, no auth, no user input. Minimal CVE exposure; the risk is supply chain inertia, not exploitability. |
Key questions to close before a go/no-go.
updateOrCreate/upsert — or does re-running it duplicate or truncate? This decides whether we can run it safely in CI/CD.Stack fit. There is a clean fit for exactly one scenario: a Laravel 5.x application, or a greenfield service where we accept a small fork. For a modern Laravel stack the realistic path is not "install the package" but "harvest the package." The valuable asset here is the dataset and the state→city relationship definition, not the 2015 Laravel plumbing.
I would sequence the decision as three options, in order of preference:
config/cidadesbr.php for table names and connection, publish migrations, convert the seeder to chunked upsert keyed on IBGE code, add a Pest/PHPUnit test asserting record counts and a spot-check of known municipalities. Effort: small — this is a few days of work, not weeks, because the code volume is tiny.Migration path. If we adopt the schema as-is and later want out, every foreign key pointing at the package's cities.id must be remapped. To avoid that trap:
Artesaos\CidadesBr\Models\* outside a single adapter/repository. One file to change later.name + state, and produce a diff report of unmatched rows. Do this in a spike, not in a production deploy.Compatibility. Verify concretely, not by inspection: create a throwaway Laravel app at our target version, add the package with a dev- path repository, and attempt composer update. Expect failure at resolution — that is the signal to go to fork or extraction. Also verify collation on the cities.name column handles accents and that state_id/UF lookups are indexed (address forms query by UF on every keystroke in autocomplete).
Sequencing.
Maintenance. Assume we own this the day we adopt it. There is no maintainer to escalate to, no release cadence, and no deprecation runway. Budget a named owner and an explicit line item: an annual data refresh plus a dependency-version check whenever we upgrade Laravel/PHP majors. In practice the maintenance cost is low because the code is trivial — but only if we fork or extract. Adopting it as-is leaves us with an unmaintainable dependency pinned to an EOL framework.
Support. No support channel, no meaningful docs beyond a short README and a one-line changelog. Internal documentation is therefore a deliverable, not a nicety: schema, IBGE code semantics, refresh procedure, and how to extend to new municipalities. Onboarding an engineer to "where do cities come from?" should take minutes, not an afternoon of reading vendor source.
Scaling. The read profile is benign: ~27 states, ~5,570 cities, effectively immutable reference data. Guidance:
cities(state_id) and, for autocomplete, consider a prefix index or a name-normalized column (lower(unaccent(name))) since users will type "sao paulo".upsert (500–1,000 rows per statement) reduces it to seconds.Failure modes to design against.
addresses to cities. Never run the vendor seeder against production without reading it first.create_cities_table migration with the same timestamp/name as an existing one makes migrate non-deterministic across environments.utf8mb4, accented municipality names corrupt silently and surface as "city not found" in forms — a support ticket, not an exception.reference_data_versions row.Ramp-up. Low for consumers — the model surface is a state→cities relationship and a UF lookup, which any Laravel developer understands immediately. Medium for the adopter: the effort is concentrated in the compatibility spike, the fork, and the data-governance decisions, not in learning the package. Estimate 2–4 days for the spike and decision, and 3–5 days for a production-quality fork-or-extract with tests and an idempotent seeder, excluding any reconciliation with existing location data.
Recommendation. Do not adopt as a live dependency on a modern stack. Use it as a dataset source: extract, own, and version the IBGE data in-house with our own migration and seeder keyed on IBGE codes. The package is a legitimate shortcut for a legacy Laravel 5.x address form; for anything current, the abandoned Composer constraints and 2015 data vintage make it a liability that costs more to carry than to reimplement.
How can I help you explore Laravel packages today?