guiliredu/laravel-seed-migration-estados-cidades-brasil
Laravel package that seeds all Brazilian states and cities into your database via ready-made migrations. Quickly populate IBGE state and municipality data for forms, address lookups, and location filters without manual imports or external APIs.
composer require guiliredu/laravel-seed-migration-estados-cidades-brasil
On Laravel 5.5+ the service provider is auto-discovered. On older apps, register it manually in config/app.php:
Guiliredu\LaravelSeedMigrationEstadosCidadesBrasil\Providers\BrasilServiceProvider::class,
Run the migrations shipped with the package — they load automatically via loadMigrationsFrom, so you just need:
php artisan migrate
Then seed the data. Because there is no vendor:publish for the seeders in most versions, invoke them explicitly by class:
php artisan db:seed --class="Guiliredu\LaravelSeedMigrationEstadosCidadesBrasil\Seeders\EstadoSeeder"
php artisan db:seed --class="Guiliredu\LaravelSeedMigrationEstadosCidadesBrasil\Seeders\CidadeSeeder"
Tip: verify the actual class names by listing
vendor/guiliredu/laravel-seed-migration-estados-cidades-brasil/src/Seeders/— this package's namespaces have shifted between releases.
First use case: building a state/city dependent dropdown in a registration or checkout form. After migration you have all 27 UFs and ~5,570 municipalities ready to query — no third-party API needed.
Inspect the schema first:
php artisan tinker
>>> Schema::getColumnListing('estados');
>>> Schema::getColumnListing('cidades');
>>> DB::table('estados')->first();
Typical columns: estados (id, nome, uf) and cidades (id, nome, estado_id, codigo_ibge).
Wire the models yourself. The package ships tables, not always Eloquent models. Define thin models in app/Models:
class Estado extends Model {
protected $table = 'estados';
public function cidades() { return $this->hasMany(Cidade::class); }
}
class Cidade extends Model {
protected $table = 'cidades';
public function estado() { return $this->belongsTo(Estado::class); }
}
Dependent dropdown via API. Expose lightweight endpoints and cache aggressively — city lists rarely change:
Route::get('/estados', fn() => Cache::rememberForever('estados', fn() =>
Estado::orderBy('nome')->get(['id', 'nome', 'uf'])));
Route::get('/estados/{estado}/cidades', fn(Estado $estado) =>
$estado->cidades()->orderBy('nome')->get(['id', 'nome']));
Form validation. Use Rule::exists('cidades', 'id') combined with a check that cidade.estado_id matches the posted estado_id — the package does not enforce cross-field consistency.
Seed inside migrations or a deploy step. For fresh installs (CI, new environments), a wrapper seeder keeps deploys reproducible:
class BrasilSeeder extends Seeder {
public function run(): void {
$this->call([
\Guiliredu\...\Seeders\EstadoSeeder::class,
\Guiliredu\...\Seeders\CidadeSeeder::class,
]);
}
}
Then php artisan migrate --seed.
Integration with existing domain models. Add cidade_id to users, addresses, or orders, with a foreign key back to cidades.id. Because the seed table is data (not user data), it is safe to leave cidades in place for production reads.
Importing to a legacy DB. If you already have estados/cidades tables, don't run the package's migrations. Instead, publish or copy the seeder classes, point them at your table via $table on your own models, and run them once.
php artisan migrate automatically. If you already have a table named estados or cidades, the migration will fail mid-batch and leave later migrations unapplied. Either rename your tables or copy the seeder logic out and skip the package's migration path.max_execution_time or a MySQL max_allowed_packet limit. Run with --force, on a fresh connection, and consider chunking if you fork the seeder.ç, ã, é. Ensure the connection uses utf8mb4_unicode_ci (or utf8mb4_0900_ai_ci on MySQL 8); otherwise orderBy('nome') returns nonsensical ordering and SAO PAULO / SÃO PAULO don't match in LIKE searches.codigo_ibge is your stable key, not id. Auto-increment IDs are fine, but when reconciling with external systems (ERP, correios, IBGE datasets), join or look up on codigo_ibge. Do not assume id values are stable across re-seeds on different environments.$table->increments() etc.). Forking the migrations locally is generally cleaner than fighting the vendor.insertOrIgnore/updateOrCreate. Check the seeder run() method before re-seeding in production; wrap in a transaction and truncate first if needed.cidades references estados; always seed estado first and, if truncating, DB::statement('SET FOREIGN_KEY_CHECKS=0') (MySQL) or SET session_replication_role = replica (Postgres) around the truncate./cidades?search=paulo returns thousands on a wildcard. Always add ->limit(50) plus an index on (nome) or a full-text index, or filter by estado_id first.regiao), or mesoregion/microregion metadata missing from a given release, copy src/Seeders into your app, enrich the arrays, and stop depending on the package's seeders — the migration stays useful, but the data layer becomes yours.How can I help you explore Laravel packages today?