laravel-postcode-my maintained by ajangsupardi
Laravel Postcode-My
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.