aliziodev / laravel-indonesia-regions
Laravel package for Indonesia regions selection with postal codes | Package Laravel untuk pemilihan wilayah Indonesia lengkap dengan kode pos
Package info
github.com/aliziodev/laravel-indonesia-regions
pkg:composer/aliziodev/laravel-indonesia-regions
Fund package maintenance!
Requires
- php: ^8.2|^8.3
- illuminate/database: ^11.0|^12.0
- illuminate/routing: ^11.0|^12.0
- illuminate/support: ^11.0|^12.0
Requires (Dev)
- laravel/pint: ^1.24
- orchestra/testbench: ^9.0|^10.0
- pestphp/pest: ^3.8|^4.0
- pestphp/pest-plugin-laravel: ^3.0|^4.0
README
Package Laravel untuk data wilayah Indonesia lengkap dengan kode pos. Package ini menyediakan data provinsi, kota/kabupaten, kecamatan, dan desa/kelurahan.
Package ini dipertahankan sebagai package kompatibilitas untuk integrasi lama. Dataset package ini disinkronkan dari repo upstream aliziodev/laravel-wilayah dan disimpan sebagai file PHP agar install dan update tetap lintas database.
Fitur
- Data wilayah Indonesia lengkap dan terupdate (sesuai Kepmendagri No 300.2.2-2138 Tahun 2025)
- Kode pos untuk setiap desa/kelurahan
- Cache system untuk performa optimal
- Facade untuk penggunaan yang mudah
- Support untuk Laravel 11.x dan 12.x
- Pencarian wilayah
- Pencarian dengan alamat lengkap
- Pencarian full text hingga level desa
- Hirarki/Info wilayah
- Format untuk dropdown/select
- Pagination support
- Format nama negara terbatas ke
IndonesiaatauID - Sync dataset via command setelah
composer update - Pencarian case-insensitive termasuk di PostgreSQL
- Endpoint API bawaan yang siap dipakai
Instalasi
composer require aliziodev/laravel-indonesia-regions
Kemudian jalankan command instalasi:
php artisan indonesia-regions:install
Untuk menyinkronkan dataset terbaru setelah package diupdate:
php artisan indonesia-regions:sync
Opsional, publish konfigurasi cache package:
php artisan vendor:publish --tag=indonesia-regions-config
Sumber Data
Data package ini digenerate dari repo upstream berikut:
https://github.com/aliziodev/laravel-wilayah
Saat workflow sync berjalan, package akan mengambil dataset dari repo upstream tersebut, menyalinnya ke folder data/, lalu aplikasi pengguna cukup menjalankan php artisan indonesia-regions:sync untuk melakukan upsert ke database.
API Bawaan
Package ini menyediakan endpoint API bawaan tanpa perlu membuat controller sendiri. Secara default route akan aktif dengan prefix:
/api/indonesia-regions
Konfigurasi tersedia di config/indonesia-regions.php:
'api' => [ 'enabled' => true, 'prefix' => 'api/indonesia-regions', 'middleware' => ['api'], 'responder' => null, ],
Jika aplikasi Anda memiliki wrapper response sendiri, Anda bisa mengganti responder bawaan package:
'api' => [ 'enabled' => true, 'prefix' => 'api/indonesia-regions', 'middleware' => ['api'], 'responder' => App\Support\Api\RegionApiResponder::class, ],
Class tersebut harus mengimplementasikan Aliziodev\IndonesiaRegions\Contracts\ApiResponderInterface.
Contoh implementasi:
<?php namespace App\Support\Api; use Aliziodev\IndonesiaRegions\Contracts\ApiResponderInterface; use Illuminate\Http\JsonResponse; class RegionApiResponder implements ApiResponderInterface { public function respond(mixed $data, int $status = 200): JsonResponse { return response()->json([ 'success' => true, 'data' => $data, ], $status); } }
Contoh jika aplikasi Anda sudah punya helper seperti ApiResponse:
<?php namespace App\Support\Api; use Aliziodev\IndonesiaRegions\Contracts\ApiResponderInterface; use Illuminate\Http\JsonResponse; class RegionApiResponder implements ApiResponderInterface { public function respond(mixed $data, int $status = 200): JsonResponse { return ApiResponse::success($data, status: $status); } }
Endpoint yang tersedia:
GET /api/indonesia-regionsGET /api/indonesia-regions/cascadeGET /api/indonesia-regions/selectGET /api/indonesia-regions/search?term=bakonganGET /api/indonesia-regions/search/address?term=bakonganGET /api/indonesia-regions/search/full-text?term=acehGET /api/indonesia-regions/postal-code/23773GET /api/indonesia-regions/11.01.01.2001GET /api/indonesia-regions/11.01.01.2001/infoGET /api/indonesia-regions/11.01.01.2001/select-contextGET /api/indonesia-regions/11.01.01.2001/full-address?country_name=ID
Contoh endpoint options untuk frontend:
GET /api/indonesia-regions/select
GET /api/indonesia-regions/select?parent_code=11
GET /api/indonesia-regions/select?format=map
Response default:
[
{ "value": "11", "label": "ACEH" }
]
Response format=map:
{
"11": "ACEH"
}
Untuk frontend yang ingin langsung mendapat semua options bertingkat sekaligus, gunakan endpoint cascade:
GET /api/indonesia-regions/cascade
GET /api/indonesia-regions/cascade?region_code=11.01.01.2001
GET /api/indonesia-regions/cascade?region_code=11.01.01.2001&country_name=ID
Response:
{
"selected": {
"province": { "value": "11", "label": "Aceh" },
"city": { "value": "11.01", "label": "Kab. Aceh Selatan" },
"district": { "value": "11.01.01", "label": "Bakongan" },
"village": { "value": "11.01.01.2001", "label": "Keude Bakongan", "postal_code": "23773" }
},
"options": {
"provinces": [
{ "value": "11", "label": "ACEH" }
],
"cities": [
{ "value": "11.01", "label": "KAB. ACEH SELATAN" }
],
"districts": [
{ "value": "11.01.01", "label": "BAKONGAN" }
],
"villages": [
{ "value": "11.01.01.2001", "label": "KEUDE BAKONGAN" }
]
},
"full_address": "Keude Bakongan, Bakongan, Kab. Aceh Selatan, Aceh, ID, 23773"
}
Contoh helper untuk prefill cascading select dari region_code yang sudah tersimpan:
GET /api/indonesia-regions/11.01.01.2001/select-context
GET /api/indonesia-regions/11.01.01.2001/select-context?country_name=ID
Response:
{
"selected": {
"province": { "value": "11", "label": "Aceh" },
"city": { "value": "11.01", "label": "Kab. Aceh Selatan" },
"district": { "value": "11.01.01", "label": "Bakongan" },
"village": { "value": "11.01.01.2001", "label": "Keude Bakongan", "postal_code": "23773" }
},
"full_address": "Keude Bakongan, Bakongan, Kab. Aceh Selatan, Aceh, Indonesia, 23773"
}
Contoh Lengkap API dengan Wilayah Bandung
Contoh kode wilayah yang digunakan:
32: Jawa Barat32.73: Kota Bandung32.73.02: Coblong32.73.02.1001: Dago
Ambil daftar kota/kabupaten di Jawa Barat:
GET /api/indonesia-regions?parent_code=32
Ambil options kota/kabupaten untuk dropdown:
GET /api/indonesia-regions/select?parent_code=32
Atau ambil seluruh context cascade sekaligus:
GET /api/indonesia-regions/cascade?region_code=32.73.02.1001
Response:
[
{ "value": "32.73", "label": "KOTA BANDUNG" },
{ "value": "32.04", "label": "KAB. BANDUNG" }
]
Cari wilayah dengan kata kunci Bandung:
GET /api/indonesia-regions/search?term=bandung
Cari hanya level kota/kabupaten:
GET /api/indonesia-regions/search?term=bandung&type=city
Cari full text, hasil selalu village:
GET /api/indonesia-regions/search/full-text?term=bandung GET /api/indonesia-regions/search/full-text?term=coblong GET /api/indonesia-regions/search/full-text?term=dago&country_name=ID
Contoh response:
[
{
"code": "32.73.02.1001",
"province": "Jawa Barat",
"city": "Kota Bandung",
"district": "Coblong",
"village": "Dago",
"postal_code": "40135",
"full_address": "Dago, Coblong, Kota Bandung, Jawa Barat, ID, 40135"
}
]
Ambil detail region berdasarkan kode:
GET /api/indonesia-regions/32.73 GET /api/indonesia-regions/32.73.02.1001
Ambil hierarchy lengkap berdasarkan region_code:
GET /api/indonesia-regions/32.73.02.1001/info GET /api/indonesia-regions/32.73.02.1001/info?country_name=ID
Contoh response:
{
"province": {
"code": "32",
"name": "Jawa Barat"
},
"city": {
"code": "32.73",
"name": "Kota Bandung"
},
"district": {
"code": "32.73.02",
"name": "Coblong"
},
"village": {
"code": "32.73.02.1001",
"name": "Dago",
"postal_code": "40135"
},
"full_address": "Dago, Coblong, Kota Bandung, Jawa Barat, Indonesia, 40135"
}
Ambil alamat lengkap saja:
GET /api/indonesia-regions/32.73.02.1001/full-address GET /api/indonesia-regions/32.73.02.1001/full-address?country_name=ID
Response:
{
"full_address": "Dago, Coblong, Kota Bandung, Jawa Barat, ID, 40135"
}
Ambil region dari kode pos:
GET /api/indonesia-regions/postal-code/40135
Prefill cascading select dari region_code yang sudah tersimpan:
GET /api/indonesia-regions/32.73.02.1001/select-context
Response:
{
"selected": {
"province": { "value": "32", "label": "Jawa Barat" },
"city": { "value": "32.73", "label": "Kota Bandung" },
"district": { "value": "32.73.02", "label": "Coblong" },
"village": { "value": "32.73.02.1001", "label": "Dago", "postal_code": "40135" }
},
"full_address": "Dago, Coblong, Kota Bandung, Jawa Barat, Indonesia, 40135"
}
Gaya Penulisan Parameter
Package ini mendukung dua gaya penulisan parameter:
1. Regular Parameters (Traditional)
// Parameter harus sesuai urutan $cities = Indonesia::getRegions('11', ['code', 'name'], 15);
2. Named Parameters
// Urutan tidak penting, lebih jelas dan mudah dibaca $cities = Indonesia::getRegions( parentCode: '11', columns: ['code', 'name'], perPage: 15 );
Kedua gaya penulisan akan memberikan hasil yang sama. Named parameters (PHP 8.0+) memiliki beberapa keunggulan:
- Lebih mudah dibaca dan dipahami
- Mengurangi kesalahan dalam urutan parameter
- Memungkinkan untuk melewati parameter opsional di tengah
- Self-documenting code
- IDE support yang lebih baik Pilih gaya penulisan yang sesuai dengan kebutuhan dan versi PHP yang Anda gunakan.
Penggunaan
Mengambil Data Wilayah (getRegions)
use Aliziodev\IndonesiaRegions\Facades\Indonesia; // Mengambil semua provinsi $provinces = Indonesia::getRegions(); // Response: [ { "code": "11", "name": "ACEH", }, { "code": "12", "name": "SUMATERA UTARA", } ] // Mengambil kota/kabupaten di Aceh dengan pagination $cities = Indonesia::getRegions('11', ['code', 'name'], 15); // Response dengan pagination: { "current_page": 1, "data": [ { "code": "11.01", "name": "KAB. ACEH SELATAN", }, // ... more cities ], "first_page_url": "http://example.com/api?page=1", "from": 1, "last_page": 23, "last_page_url": "http://example.com/api?page=23", "next_page_url": "http://example.com/api?page=2", "path": "http://example.com/api", "per_page": 15, "prev_page_url": null, "to": 15, "total": 343 } // postal_code hanya muncul untuk desa/kelurahan
Perlu diingat bahwa jika parameter columns tidak diisi (null), maka akan menggunakan default columns yaitu ['code', 'name', 'postal_code']. Untuk mengambil semua kolom, Anda perlu secara eksplisit menentukan columns: ['*'].
Pencarian (search)
use Aliziodev\IndonesiaRegions\Facades\Indonesia; // Pencarian umum $results = Indonesia::search('Bakongan'); // Search bersifat case-insensitive, termasuk di PostgreSQL $results = Indonesia::search('bakongan'); $results = Indonesia::search('BAKONGAN'); // Response: [ { "code": "11.01.01", "name": "BAKONGAN" }, { "code": "11.01.01.2001", "name": "KEUDE BAKONGAN", "postal_code": "23773" // postal_code hanya muncul untuk desa/kelurahan } ] // Pencarian dengan tipe spesifik $villages = Indonesia::search('Bakongan', 'village'); // Pencarian dengan kolom tambahan $results = Indonesia::search('Bakongan', null, null, ['code', 'name']); // Pencarian lengkap dengan named parameters (PHP 8.0+) $results = Indonesia::search( term: 'Bakongan', type: 'village', perPage: 15, columns: ['code', 'name'] ); // Response dengan pagination sama dengan format getRegions()
Pencarian dengan Alamat Lengkap (searchWithAddress)
use Aliziodev\IndonesiaRegions\Facades\Indonesia; // Pencarian umum dengan alamat lengkap $results = Indonesia::searchWithAddress('Bakongan'); // Response: [ { "code": "11.01.01", "name": "BAKONGAN", "full_address": "BAKONGAN, KAB. ACEH SELATAN, ACEH" }, { "code": "11.01.01.2001", "name": "KEUDE BAKONGAN", "postal_code": "23773", "full_address": "KEUDE BAKONGAN, BAKONGAN, KAB. ACEH SELATAN, ACEH, 23773" } ] // Pencarian dengan tipe spesifik $villages = Indonesia::searchWithAddress('Bakongan', 'village'); // Pencarian dengan kolom tambahan $results = Indonesia::searchWithAddress('Bakongan', null, null, ['code', 'name']); // Pencarian lengkap dengan named parameters (PHP 8.0+) $results = Indonesia::searchWithAddress( term: 'Bakongan', type: 'village', perPage: 15, columns: ['code', 'name'] ); // Response dengan pagination sama dengan format getRegions() // Menggunakan nama negara singkat $results = Indonesia::searchWithAddress('Bakongan', null, null, null, 'ID');
Pencarian Full Text (searchWithFullText)
use Aliziodev\IndonesiaRegions\Facades\Indonesia; // Pencarian full text dengan default limit (15) $results = Indonesia::searchWithFullText('Bakongan'); // Pencarian dengan limit kustom $results = Indonesia::searchWithFullText('Bakongan', 25); // Pencarian dengan nama negara singkat $results = Indonesia::searchWithFullText( term: 'Bakongan', limit: 25, countryName: 'ID' ); // Response: [ { "code": "11.01.01.2001", "province": "ACEH", "city": "KAB. ACEH SELATAN", "district": "BAKONGAN", "village": "KEUDE BAKONGAN", "postal_code": "23773", "full_address": "KEUDE BAKONGAN, BAKONGAN, KAB. ACEH SELATAN, ACEH, ID, 23773" } // ... more results ]
Metode ini selalu mengembalikan hasil pada level village. Kata kunci dapat berasal dari nama village, district, city, province, atau postal code, tetapi hasil akhirnya tetap berupa data village lengkap dengan hirarkinya.
Nilai countryName yang didukung hanya:
Indonesia(default)ID
Mencari Berdasarkan Kode (findByCode)
// Regular parameters $region = Indonesia::findByCode('11.01.01.2001'); $region = Indonesia::findByCode('11.01.01.2001', ['code', 'name']); // Named parameters (PHP 8.0+) $region = Indonesia::findByCode( code: '11.01.01.2001', columns: ['code', 'name'] ); // Response: { "code": "11.01.01.2001", "name": "KEUDE BAKONGAN", "postal_code": "23773", }
Format Dropdown/Select (getForSelect)
$provinces = Indonesia::getForSelect(); // Response: { "11": "ACEH", "12": "SUMATERA UTARA", "13": "SUMATERA BARAT" // ... more provinces } // Get cities for select $cities = Indonesia::getForSelect('11'); // Response: { "11.01": "KAB. ACEH SELATAN", "11.02": "KAB. ACEH TENGGARA", // ... more cities }
Informasi Detail Wilayah (getRegionInfo)
$info = Indonesia::getRegionInfo('11.01.01.2001'); // Response: { "province": { "code": "11", "name": "ACEH" }, "city": { "code": "11.01", "name": "KAB. ACEH SELATAN" }, "district": { "code": "11.01.01", "name": "BAKONGAN" }, "village": { "code": "11.01.01.2001", "name": "KEUDE BAKONGAN", "postal_code": "23773" }, "full_address": "KEUDE BAKONGAN, BAKONGAN, KAB. ACEH SELATAN, ACEH, 23773" } // Menggunakan nama negara singkat $info = Indonesia::getRegionInfo('11.01.01.2001', null, 'ID'); // Response akan menggunakan nama negara yang dipilih "KEUDE BAKONGAN, BAKONGAN, KAB. ACEH SELATAN, ACEH, ID, 23773"
Alamat Lengkap (getFullAddress)
$address = Indonesia::getFullAddress('11.01.01.2001'); // Response: "KEUDE BAKONGAN, BAKONGAN, KAB. ACEH SELATAN, ACEH, 23773" // Menggunakan nama negara singkat $address = Indonesia::getFullAddress('11.01.01.2001', 'ID'); // Response akan menggunakan nama negara yang dipilih "KEUDE BAKONGAN, BAKONGAN, KAB. ACEH SELATAN, ACEH, ID, 23773"
Pencarian Kode Pos (findByPostalCode)
$region = Indonesia::findByPostalCode('23773'); // Response: { "code": "11.01.01.2001", "name": "KEUDE BAKONGAN", "postal_code": "23773" }
Validasi Kode (validateCode)
$isValid = Indonesia::validateCode('11.01.01.2001'); // true $isValid = Indonesia::validateCode('11.99'); // false $isValid = Indonesia::validateCode('11.12345'); // false
Mendapatkan Tipe Wilayah (getRegionType)
$type = Indonesia::getRegionType('11.01.01.2001'); // 'village' $type = Indonesia::getRegionType('11.01.01'); // 'district' $type = Indonesia::getRegionType('11.01'); // 'city' $type = Indonesia::getRegionType('11'); // 'province'
Cache Management
php artisan indonesia-regions:clear-cache
Pengaturan cache dapat dikustomisasi melalui config/indonesia-regions.php.
Struktur Kode Wilayah
- Provinsi: 2 digit (contoh: 11)
- Kota/Kabupaten: 5 digit (contoh: 11.01)
- Kecamatan: 8 digit (contoh: 11.01.01)
- Desa/Kelurahan: 13 digit (contoh: 11.01.01.2001)
Kolom Database
- code : Kode wilayah (primary key)
- name : Nama wilayah
- postal_code : Kode pos (untuk desa/kelurahan)
- status : Status wilayah aktif/tidak aktif (optional)
- search_text : Kolom internal untuk optimasi
searchWithFullText()pada level village
Method Parameters
getRegions
parentCode(string|null) : Kode wilayah parent (opsional)columns(array|null) : Kolom yang akan diambil (default: ['code', 'name', 'postal_code'])perPage(int|null) : Jumlah data per halaman untuk pagination (opsional)
search
term(string) : Kata kunci pencariantype(string|null) : Tipe wilayah ('province'|'city'|'district'|'village')perPage(int|null) : Jumlah data per halaman untuk pagination (opsional)
searchWithAddress
term(string) : Kata kunci pencariantype(string|null) : Tipe wilayah ('province'|'city'|'district'|'village')perPage(int|null) : Jumlah data per halaman untuk pagination (opsional)columns(array|null) : Kolom yang akan diambilcountryName(string|null) : Nama negara untuk alamat lengkap. Nilai yang didukung hanyaIndonesiaatauID(default:Indonesia)
searchWithFullText
term(string) : Kata kunci pencarianlimit(int|null) : Batas jumlah hasil pencarian (default: 15)countryName(string|null) : Nama negara untuk alamat lengkap. Nilai yang didukung hanyaIndonesiaatauID(default:Indonesia)- Catatan: hasil selalu dikembalikan pada level
village
findByCode
code(string) : Kode wilayahcolumns(array|null) : Kolom yang akan diambil (default: ['*'])
getForSelect
parentCode(string|null) : Kode wilayah parent (opsional)
getRegionInfo
code(string) : Kode wilayahcolumns(array|null) : Kolom yang akan diambil (default: ['code', 'name', 'postal_code'])countryName(string|null) : Nama negara untuk alamat lengkap. Nilai yang didukung hanyaIndonesiaatauID(default:Indonesia)
getFullAddress
villageCode(string) : Kode desa/kelurahancountryName(string|null) : Nama negara untuk alamat lengkap. Nilai yang didukung hanyaIndonesiaatauID(default:Indonesia)
findByPostalCode
postalCode(string) : Kode pos
validateCode
code(string) : Kode wilayah yang akan divalidasi
getRegionType
code(string) : Kode wilayah
Ucapan Terima Kasih
Data wilayah pada package ini pada dasarnya berasal dari cahyadsn/wilayah. Data tersebut kemudian diolah dan dipelihara lebih lanjut melalui repo upstream aliziodev/laravel-wilayah, lalu disinkronkan ke package ini.
Kontribusi
Silakan buat issue atau pull request untuk kontribusi.
Lisensi
Package ini di bawah lisensi MIT.