ajangsupardi / laravel-postcode-id
Laravel package for seeding Indonesian address data (provinces, regencies, districts, villages) with postal codes from Pos Indonesia
Package info
github.com/ajangsupardi/laravel-postcode-id
pkg:composer/ajangsupardi/laravel-postcode-id
Requires
- php: ^8.3
- illuminate/console: ^11.0|^12.0|^13.0
- illuminate/database: ^11.0|^12.0|^13.0
- illuminate/http: ^11.0|^12.0|^13.0
- illuminate/support: ^11.0|^12.0|^13.0
Requires (Dev)
- orchestra/testbench: ^9.0|^10.0
- phpunit/phpunit: ^11.0
README
Laravel package for Indonesian address data with postal codes — downloaded fresh from Pos Indonesia.
Features
- Postal code lookup — Query villages by postal code with full address hierarchy
- Fresh data — Downloads directly from Pos Indonesia, not static dumps
- Idempotent seeders — Safe to run multiple times without duplicates
- Hierarchical parsing — Province → Regency → District → Village with name normalization
- Custom models — Extend default models or use your own
- Laravel 11, 12, 13 — Modern PHP 8.3+
Installation
composer require ajangsupardi/laravel-postcode-id
Publish Configuration (optional)
php artisan vendor:publish --tag=postcode-config
Publish Migrations (optional)
php artisan vendor:publish --tag=postcode-migrations
Note: Migrations are automatically loaded by the service provider, so publishing is only needed if you want to modify the table structure.
Usage
1. Download Postcode Data
php artisan postcode:download
This command will download all postcode data from Pos Indonesia and save it as a CSV file.
2. Run Migrations & Seeder
php artisan migrate php artisan postcode:seed
Or add it to your DatabaseSeeder.php:
use Ajangsupardi\PostcodeId\Database\Seeders\PostcodeSeeder; public function run(): void { $this->call([ PostcodeSeeder::class, ]); }
3. Auto-download & Seed
If you want to combine download and seed in a single step, you can call the download command before the seeder:
use Illuminate\Support\Facades\Artisan; $storagePath = config('postcode.storage_path'); if (! file_exists($storagePath.'/kodepos.csv')) { Artisan::call('postcode:download'); }
Postal Code Lookup
The main feature — find any Indonesian address by postal code:
use Ajangsupardi\PostcodeId\Models\Village; // Village by postal code $village = Village::where('postal_code', '60111')->first(); // With full address hierarchy $village = Village::with('district.regency.province') ->where('postal_code', '60111') ->first(); // Result: Gubeng → Kota Surabaya → Jawa Timur
// Search by village name $villages = Village::where('name', 'LIKE', '%Gubeng%') ->with('district.regency') ->get();
Database Tables
This package creates 4 tables:
| Table | Description |
|---|---|
provinces |
Province data (id, name, code) |
regencies |
Regency/city data (id, province_id, name) |
districts |
District/sub-district data (id, regency_id, name) |
villages |
Village data (id, district_id, name, postal_code) |
Configuration
Edit config/postcode.php:
return [ // CSV file storage path 'storage_path' => storage_path('app/postcode'), // Database table prefix (null = no prefix) 'table_prefix' => null, // Custom models 'models' => [ 'province' => Ajangsupardi\PostcodeId\Models\Province::class, 'regency' => Ajangsupardi\PostcodeId\Models\Regency::class, 'district' => Ajangsupardi\PostcodeId\Models\District::class, 'village' => Ajangsupardi\PostcodeId\Models\Village::class, ], // HTTP client settings 'http' => [ 'timeout' => 60, 'connect_timeout' => 10, 'retry' => 3, 'retry_delay' => 1000, 'user_agent' => 'Mozilla/5.0 (compatible; LaravelPostcodeId/1.0)', ], ];
Custom Models
You can extend the default models to add columns or relationships:
namespace App\Models; use Ajangsupardi\PostcodeId\Models\Province as BaseProvince; class Province extends BaseProvince { protected $fillable = ['name', 'code', 'your_custom_field']; // Add custom relationships or methods }
Then update the configuration:
'models' => [ 'province' => App\Models\Province::class, ],
Table Prefix
If you want to use a prefix for your tables:
'table_prefix' => 'postcode_',
This will create tables: postcode_provinces, postcode_regencies, postcode_districts, postcode_villages.
Example Queries
use Ajangsupardi\PostcodeId\Models\Province; use Ajangsupardi\PostcodeId\Models\Village; // Get all provinces $provinces = Province::all(); // Get regencies in East Java $regencies = Province::where('name', 'Jawa Timur') ->first() ->regencies; // Find a village by postal code $village = Village::where('postal_code', '60111')->first(); // Get full address hierarchy from village to province $village = Village::with('district.regency.province') ->where('postal_code', '60111') ->first();
Requirements
- PHP ^8.3
- Laravel ^11.0 / ^12.0 / ^13.0
Changelog
v1.1.6
- Fix progress bar not displaying in terminal during
postcode:seed
v1.1.5
- Fix
postcode:seedhanging on PostgreSQL — remove transaction wrapper that caused deadlock
v1.1.4
- Fix
postcode:seedhanging indefinitely — useArtisan::call()instead ofCommand::call()to correctly invoke seeder
v1.1.3
- Add progress bar to all seeders for visual feedback during bulk operations
v1.1.2
- Fix
postcode:downloadfailing on transient network errors — retry now handles cURL connection exceptions
v1.1.1
- Fix
VillageSeederhanging/timing out on PostgreSQL — foreign key triggers are temporarily disabled during bulk insert
v1.1.0
- Added
postcode:seedartisan command to fix shell escaping bug on Linux
v1.0.0
- Initial release — download, parse, and seed Indonesian address data with postal codes
License
MIT License. See LICENSE for more information.