Looking to hire Laravel developers? Try LaraJobs

laravel-postcode-id maintained by ajangsupardi

Description
Laravel package for seeding Indonesian address data (provinces, regencies, districts, villages) with postal codes from Pos Indonesia
Author
Last update
2026/07/21 12:22 (dev-master)
License
Links
Downloads
18

Comments
comments powered by Disqus

Laravel Postcode ID

Latest Stable Version License Total Downloads

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:seed hanging on PostgreSQL — remove transaction wrapper that caused deadlock

v1.1.4

  • Fix postcode:seed hanging indefinitely — use Artisan::call() instead of Command::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:download failing on transient network errors — retry now handles cURL connection exceptions

v1.1.1

  • Fix VillageSeeder hanging/timing out on PostgreSQL — foreign key triggers are temporarily disabled during bulk insert

v1.1.0

  • Added postcode:seed artisan 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.