{"id":1494,"date":"2026-01-06T10:36:46","date_gmt":"2026-01-06T13:36:46","guid":{"rendered":"https:\/\/rjsites.com.br\/?p=1494"},"modified":"2026-01-06T10:36:46","modified_gmt":"2026-01-06T13:36:46","slug":"como-criar-estados-e-cidades-no-laravel-usando-a-api-do-ibge","status":"publish","type":"post","link":"https:\/\/rjsites.com.br\/index.php\/2026\/01\/06\/como-criar-estados-e-cidades-no-laravel-usando-a-api-do-ibge\/","title":{"rendered":"Como criar Estados e Cidades no Laravel usando a API do IBGE"},"content":{"rendered":"\n<h3 class=\"wp-block-heading\">Crie uma base confi\u00e1vel de estados e munic\u00edpios brasileiros no Laravel usando dados oficiais do IBGE.<\/h3>\n\n\n\n<p>Em muitos projetos Laravel, principalmente sistemas administrativos, ERPs e SaaS, surge a necessidade de trabalhar com <strong>Estados e Cidades do Brasil<\/strong>.<\/p>\n\n\n\n<p>O problema \u00e9 que:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>cadastrar isso manualmente \u00e9 invi\u00e1vel<\/li>\n\n\n\n<li>seeds est\u00e1ticos ficam desatualizados<\/li>\n\n\n\n<li>copiar dumps da internet n\u00e3o \u00e9 confi\u00e1vel<\/li>\n<\/ul>\n\n\n\n<p>Neste tutorial, vou mostrar como criar uma <strong>estrutura profissional de Estados e Cidades<\/strong> no Laravel, consumindo diretamente a <strong>API oficial e gratuita do IBGE<\/strong>, garantindo dados corretos e atualizados.<\/p>\n\n\n\n<p>Voc\u00ea vai aprender a:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>criar migra\u00e7\u00f5es bem estruturadas<\/li>\n\n\n\n<li>definir relacionamentos Eloquent<\/li>\n\n\n\n<li>criar um comando Artisan para importar os dados<\/li>\n\n\n\n<li>popular o banco com seguran\u00e7a e sem duplica\u00e7\u00f5es<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83d\udce6 O que vamos construir<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Tabela states<\/li>\n\n\n\n<li>Tabela cities<\/li>\n\n\n\n<li>Models com relacionamento<\/li>\n<\/ul>\n\n\n\n<p>Comando Artisan:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>php artisan ibge:import-locations<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">1\ufe0f\u20e3 Criando os models e migrations<\/h2>\n\n\n\n<p>Vamos come\u00e7ar criando os models j\u00e1 com suas migrations:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>php artisan make:model State -m\nphp artisan make:model City -m<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83d\udcc4 Migration da tabela <code>states<\/code><\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>use Illuminate\\Database\\Migrations\\Migration;\nuse Illuminate\\Database\\Schema\\Blueprint;\nuse Illuminate\\Support\\Facades\\Schema;\n\nreturn new class extends Migration {\n    public function up(): void\n    {\n        Schema::create('states', function (Blueprint $table) {\n            $table->id();\n            $table->string('uf', 2)->unique();              \/\/ SP, RJ...\n            $table->string('name')->index();                \/\/ S\u00e3o Paulo\n            $table->unsignedInteger('ibge_id')->unique();   \/\/ ID oficial do IBGE\n            $table->timestamps();\n        });\n    }\n\n    public function down(): void\n    {\n        Schema::dropIfExists('states');\n    }\n};<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">\ud83d\udca1 Por que essas colunas?<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>uf<\/code>: facilita exibi\u00e7\u00e3o e filtros<\/li>\n\n\n\n<li><code>ibge_id<\/code>: garante integridade com dados oficiais<\/li>\n\n\n\n<li><code>unique<\/code>: evita duplica\u00e7\u00f5es<\/li>\n\n\n\n<li><code>index<\/code>: melhora buscas por nome<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83d\udcc4 Migration da tabela <code>cities<\/code><\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>use Illuminate\\Database\\Migrations\\Migration;\nuse Illuminate\\Database\\Schema\\Blueprint;\nuse Illuminate\\Support\\Facades\\Schema;\n\nreturn new class extends Migration {\n    public function up(): void\n    {\n        Schema::create('cities', function (Blueprint $table) {\n            $table->id();\n            $table->foreignId('state_id')\n                ->constrained('states')\n                ->cascadeOnDelete();\n\n            $table->string('name')->index();\n            $table->unsignedInteger('ibge_id')->unique();\n            $table->timestamps();\n\n            \/\/ evita cidades duplicadas dentro do mesmo estado\n            $table->unique(&#91;'state_id', 'name']);\n        });\n    }\n\n    public function down(): void\n    {\n        Schema::dropIfExists('cities');\n    }\n};<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">\u25b6\ufe0f Executando as migrations<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>php artisan migrate<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">2\ufe0f\u20e3 Criando os models e relacionamentos<\/h2>\n\n\n\n<p>\ud83e\udde0 Model <code>State<\/code><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>namespace App\\Models;\n\nuse Illuminate\\Database\\Eloquent\\Model;\nuse Illuminate\\Database\\Eloquent\\Relations\\HasMany;\n\nclass State extends Model\n{\n    protected $fillable = &#91;'uf', 'name', 'ibge_id'];\n\n    public function cities(): HasMany\n    {\n        return $this->hasMany(City::class);\n    }\n}<\/code><\/pre>\n\n\n\n<p>\ud83e\udde0 Model <code>City<\/code><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>namespace App\\Models;\n\nuse Illuminate\\Database\\Eloquent\\Model;\nuse Illuminate\\Database\\Eloquent\\Relations\\BelongsTo;\n\nclass City extends Model\n{\n    protected $fillable = &#91;'state_id', 'name', 'ibge_id'];\n\n    public function state(): BelongsTo\n    {\n        return $this->belongsTo(State::class);\n    }\n}<\/code><\/pre>\n\n\n\n<p>Agora voc\u00ea j\u00e1 pode fazer:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$state->cities;\n$city->state;<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">3\ufe0f\u20e3 Criando o comando Artisan para importar do IBGE<\/h2>\n\n\n\n<p>Em vez de usar seed fixo, vamos criar um <strong>comando reutiliz\u00e1vel<\/strong>, que pode ser executado sempre que necess\u00e1rio.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>php artisan make:command IbgeImportLocations<\/code><\/pre>\n\n\n\n<p>\ud83d\udcc4 Comando <code>IbgeImportLocations<\/code><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>namespace App\\Console\\Commands;\n\nuse App\\Models\\City;\nuse App\\Models\\State;\nuse Illuminate\\Console\\Command;\nuse Illuminate\\Support\\Facades\\Http;\n\nclass IbgeImportLocations extends Command\n{\n    protected $signature = 'ibge:import-locations';\n    protected $description = 'Importa estados e cidades do IBGE para o banco local';\n\n    public function handle(): int\n    {\n        $this->info('Buscando estados no IBGE...');\n\n        $statesResponse = Http::timeout(60)\n            ->get('https:\/\/servicodados.ibge.gov.br\/api\/v1\/localidades\/estados');\n\n        if (! $statesResponse->successful()) {\n            $this->error('Falha ao consultar estados no IBGE.');\n            return self::FAILURE;\n        }\n\n        $states = collect($statesResponse->json())\n            ->sortBy('nome')\n            ->values();\n\n        foreach ($states as $s) {\n            $state = State::updateOrCreate(\n                &#91;'ibge_id' => $s&#91;'id']],\n                &#91;'uf' => $s&#91;'sigla'], 'name' => $s&#91;'nome']]\n            );\n\n            $this->info(\"Importando cidades de {$state->uf}...\");\n\n            $citiesResponse = Http::timeout(120)\n                ->get(\"https:\/\/servicodados.ibge.gov.br\/api\/v1\/localidades\/estados\/{$state->ibge_id}\/municipios\");\n\n            if (! $citiesResponse->successful()) {\n                $this->warn(\"Falha ao buscar cidades de {$state->uf}. Pulando...\");\n                continue;\n            }\n\n            foreach ($citiesResponse->json() as $c) {\n                City::updateOrCreate(\n                    &#91;'ibge_id' => $c&#91;'id']],\n                    &#91;\n                        'state_id' => $state->id,\n                        'name' => $c&#91;'nome'],\n                    ]\n                );\n            }\n        }\n\n        $this->info('Importa\u00e7\u00e3o conclu\u00edda com sucesso.');\n        return self::SUCCESS;\n    }\n}<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">4\ufe0f\u20e3 Executando a importa\u00e7\u00e3o<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>php artisan ibge:import-locations<\/code><\/pre>\n\n\n\n<p>Ao final voc\u00ea ter\u00e1:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>27 estados<\/li>\n\n\n\n<li>mais de 5.500 cidades<\/li>\n\n\n\n<li>dados oficiais<\/li>\n\n\n\n<li>sem duplica\u00e7\u00f5es<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\u2705 Vantagens dessa abordagem<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\ud83d\udccc Dados oficiais do IBGE<\/li>\n\n\n\n<li>\ud83d\udccc Comando reutiliz\u00e1vel<\/li>\n\n\n\n<li>\ud83d\udccc Evita seeds desatualizados<\/li>\n\n\n\n<li>\ud83d\udccc F\u00e1cil manuten\u00e7\u00e3o<\/li>\n\n\n\n<li>\ud83d\udccc Pronto para produ\u00e7\u00e3o<\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-list\"><\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83e\udde0 Conclus\u00e3o<\/h2>\n\n\n\n<p>Essa \u00e9 uma forma <strong>simples, robusta e profissional<\/strong> de manter Estados e Cidades no Laravel.<\/p>\n\n\n\n<p>Voc\u00ea pode usar essa estrutura em:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>formul\u00e1rios de endere\u00e7o<\/li>\n\n\n\n<li>filtros geogr\u00e1ficos<\/li>\n\n\n\n<li>cadastros de usu\u00e1rios<\/li>\n\n\n\n<li>sistemas administrativos<\/li>\n\n\n\n<li>SaaS multi-regi\u00e3o<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Crie uma base confi\u00e1vel de estados e munic\u00edpios brasileiros no Laravel usando dados oficiais do IBGE. Em muitos projetos Laravel, principalmente sistemas administrativos, ERPs e SaaS, surge a necessidade de trabalhar com Estados e Cidades do Brasil. O problema \u00e9 que: Neste tutorial, vou mostrar como criar uma estrutura profissional de Estados e Cidades no [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"site-sidebar-layout":"default","site-content-layout":"","ast-site-content-layout":"default","site-content-style":"default","site-sidebar-style":"default","ast-global-header-display":"","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"","ast-breadcrumbs-content":"","ast-featured-img":"","footer-sml-layout":"","theme-transparent-header-meta":"","adv-header-id-meta":"","stick-header-meta":"","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"set","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"footnotes":""},"categories":[1],"tags":[28,27],"class_list":["post-1494","post","type-post","status-publish","format-standard","hentry","category-sem-categoria","tag-laravel","tag-php"],"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/rjsites.com.br\/index.php\/wp-json\/wp\/v2\/posts\/1494","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/rjsites.com.br\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/rjsites.com.br\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/rjsites.com.br\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/rjsites.com.br\/index.php\/wp-json\/wp\/v2\/comments?post=1494"}],"version-history":[{"count":1,"href":"https:\/\/rjsites.com.br\/index.php\/wp-json\/wp\/v2\/posts\/1494\/revisions"}],"predecessor-version":[{"id":1495,"href":"https:\/\/rjsites.com.br\/index.php\/wp-json\/wp\/v2\/posts\/1494\/revisions\/1495"}],"wp:attachment":[{"href":"https:\/\/rjsites.com.br\/index.php\/wp-json\/wp\/v2\/media?parent=1494"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/rjsites.com.br\/index.php\/wp-json\/wp\/v2\/categories?post=1494"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/rjsites.com.br\/index.php\/wp-json\/wp\/v2\/tags?post=1494"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}