ajangsupardi / laravel-postcode-my
Laravel package for seeding Malaysian address data (states, postcodes, locations) with postal codes from Pos Malaysia
Package info
github.com/ajangsupardi/laravel-postcode-my
pkg:composer/ajangsupardi/laravel-postcode-my
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
This package is auto-updated.
Last update: 2026-06-22 10:29:32 UTC
README
Laravel package for Malaysian address data (states, postcodes, locations) with postal codes — sourced from Pos Malaysia.
Data Source
This package uses postcode data from Pos Malaysia (api.pos.com.my).
Last updated: 2026-06-22 Data version: 1.0.0
The data is bundled as a static CSV file for instant seeding. To update data manually (requires ~30 minutes due to API limitations), see Update Data.
Features
- Postcode lookup — Query locations by postcode with full address hierarchy.
- Static data — Bundled CSV for instant seeding, no download required.
- Idempotent seeder — Safe to run multiple times without duplicate data.
- Hierarchical parsing — State → Postcode → Post Office → Location structure.
- Custom models — Extend default models or use your own.
- Laravel 11, 12, 13 support — Modern PHP 8.3+.
Requirements
- PHP ^8.3
- Laravel ^11.0 / ^12.0 / ^13.0
Installation
composer require ajangsupardi/laravel-postcode-my
Configuration (Optional)
Publish the config file:
php artisan vendor:publish --tag=postcode-config
This will create config/postcode.php where you can customize:
- Storage path — Where CSV files are stored
- Table prefix — Prefix for database table names
- Models — Override default models with your own
Usage
1. Run Migrations & Seeder
php artisan migrate php artisan postcode:seed
Or add to your DatabaseSeeder.php:
$this->call([ \Ajangsupardi\PostcodeMy\Database\Seeders\PostcodeSeeder::class, ]);
The seeder uses the bundled static CSV file for instant seeding.
2. Update Data (Optional)
If you need the latest postcode data from Pos Malaysia:
php artisan postcode:download php artisan postcode:seed
Note: The download command queries the Pos Malaysia API for each postcode (00000-99999). This process takes approximately 30 minutes due to API limitations.
After updating, consider bumping the data version in config/postcode.php:
'data_version' => '1.1.0', 'data_updated_at' => '2026-12-31',
3. Query Data
use Ajangsupardi\PostcodeMy\Models\Location; use Ajangsupardi\PostcodeMy\Models\Postcode; use Ajangsupardi\PostcodeMy\Models\State; // Find locations by postcode $locations = Location::whereHas('postcode', function ($query) { $query->where('postcode', '50000'); })->get(); // Find postcode with full hierarchy $postcode = Postcode::with('state', 'locations') ->where('postcode', '50000') ->first(); // Find all postcodes in a state $state = State::where('code', 'KL')->first(); $postcodes = $state->postcodes; // Search locations by name $locations = Location::name('Jalan')->get();
Database Schema
| Table | Columns | Description |
|---|---|---|
states |
id, name, code, timestamps | Malaysian states (Johor, Kedah, etc.) |
postcodes |
id, state_id, postcode, post_office, timestamps | Postcode areas |
locations |
id, postcode_id, name, timestamps | Specific locations/streets |
Hierarchical Structure
State (Negeri)
├── Postcode (Poskod)
│ ├── Post Office (Pejabat Pos)
│ └── Location (Lokasi)
Configuration Options
// config/postcode.php return [ 'data_version' => '1.0.0', 'data_updated_at' => '2026-06-22', 'storage_path' => storage_path('app/postcode'), 'table_prefix' => null, 'models' => [ 'state' => Ajangsupardi\PostcodeMy\Models\State::class, 'postcode' => Ajangsupardi\PostcodeMy\Models\Postcode::class, 'location' => Ajangsupardi\PostcodeMy\Models\Location::class, ], 'http' => [ 'timeout' => 60, 'connect_timeout' => 10, 'retry' => 3, 'retry_delay' => 1000, 'user_agent' => 'Mozilla/5.0 (compatible; LaravelPostcodeMy/1.0)', ], ];
Custom Models
You can extend the default models to add custom behavior:
namespace App\Models; use Ajangsupardi\PostcodeMy\Models\State as BaseState; class State extends BaseState { // Add your custom methods here }
Then update the config:
'models' => [ 'state' => App\Models\State::class, ],
License
The MIT License (MIT). Please see LICENSE for more information.