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

Cidadesbr Laravel Package

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.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

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:

  • Schema ownership sits with the vendor. Table names, column names, indexes and primary keys are defined by the package, not by us. If we already have an 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.
  • No visible abstraction layer. There is no contract, repository, config file or DTO — consumers couple directly to 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.
  • Eloquent-only. Fine for server-rendered forms and filters, poor for anything needing the list outside PHP (frontend autocomplete, data warehouse, mobile bundle, search index).

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:

  • A Composer constraint pinned to 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.
  • Laravel 5.0-era service provider conventions (provides(), deferred providers, possibly App::make) that were removed or changed in 5.8+/6+.
  • No PHP 7.4/8.x type declarations, no declare(strict_types=1), likely PHP 5.4+ syntax. On PHP 8.2+ we should expect deprecation noise at minimum.
  • Migrations/seeders that may predate 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.

  1. What are the actual table names, and are they publishable/configurable? Do the migrations include unique indexes on IBGE codes?
  2. Are primary keys the official IBGE state/municipality codes, or surrogate IDs? (Determines downstream interop and migration cost.)
  3. Which IBGE reference year is the seed data from, and how many records does it contain?
  4. Does the service provider register cleanly on our Laravel version after a constraint bump, or does it need code changes (PSR-4, provider API, removed helpers)?
  5. Is the seeder idempotent — updateOrCreate/upsert — or does re-running it duplicate or truncate? This decides whether we can run it safely in CI/CD.
  6. Is the license permissive enough to fork and maintain internally?
  7. What is our actual requirement: a static picklist for address forms, or a queryable location dimension for reporting? The answer changes the recommendation.

Integration Approach

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:

  1. Fork-and-modernize (preferred if we want the package shape). Vendor the repo, bump the Composer constraints to our Laravel/PHP versions, add 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.
  2. Data-only extraction (preferred if we want zero dependency). Export states/cities to a versioned CSV or JSON fixture in our own repo and ship our own migration + seeder. We keep the data, drop the abandoned dependency entirely, and gain a reviewable artifact in code review whenever the dataset changes. This is my default recommendation.
  3. Live IBGE API (preferred if freshness matters more than offline determinism). Consume the IBGE localidades endpoints and cache aggressively. Rejected for most cases — it trades a stale dataset for a runtime dependency and a cache-invalidation problem, for reference data that changes roughly annually at most.

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:

  • Establish IBGE code as the canonical key in our own domain model from day one, even if the package uses surrogate IDs. Store both, key our FKs on the IBGE code.
  • Do not let application code import Artesaos\CidadesBr\Models\* outside a single adapter/repository. One file to change later.
  • If we already have location data, run a reconciliation before seeding: match on IBGE code, fall back to normalized 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.

  1. Spike (1–2 days): install against target Laravel; capture schema, record counts, IBGE code fidelity, seeder idempotency, license.
  2. Decision (same week): fork vs. extract vs. live API. Pick based on whether we need package-shaped artisan commands or just data.
  3. Build: own migration + idempotent seeder, cached state list, indexed city lookup, adapter model.
  4. Data governance: record the IBGE reference year in the repo and a README; define a refresh cadence (annual is sufficient).
  5. Cut over: backfill and reconcile, then point forms/filters at the adapter.

Operational Impact

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:

  • Cache the full state list and per-UF city lists in the application cache (or an in-process array) — an address form should never hit the DB per keystroke.
  • Index cities(state_id) and, for autocomplete, consider a prefix index or a name-normalized column (lower(unaccent(name))) since users will type "sao paulo".
  • The seeder is the only scaling concern: a naive row-by-row insert of 5,570 records is slow enough to be annoying in CI. Chunked upsert (500–1,000 rows per statement) reduces it to seconds.
  • Avoid seeding full city data in unit-test environments; use a fixture of 2–3 states and a factory. Reference data seeding on every test run is a classic suite-slowdown.

Failure modes to design against.

  • Re-seed duplication or truncation. If the seeder truncates, a redeploy can null out or orphan FKs from addresses to cities. Never run the vendor seeder against production without reading it first.
  • Migration collisions. A create_cities_table migration with the same timestamp/name as an existing one makes migrate non-deterministic across environments.
  • Encoding damage. If the pipeline or column collation isn't utf8mb4, accented municipality names corrupt silently and surface as "city not found" in forms — a support ticket, not an exception.
  • Legacy-code fatal on PHP 8. Removed Laravel helpers or dynamic properties will fatal at boot in production, not at install. The spike app is essential.
  • Silent data drift. No version stamp in the package means we can't tell which IBGE year is in the DB. Store the reference year in a config value or a 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.

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.
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums
rlacerda83/query-parser
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings