hasyirin/laravel-address
| Install | |
|---|---|
composer require hasyirin/laravel-address |
|
| Latest Version: | v4.0.5 |
| PHP: | ^8.4 |
| Laravel: | 12 · 13 |
| Older releases: | 10 · 11 |
| License: | MIT |
| Last Updated: | Jul 19, 2026 |
| Links: | GitHub · Packagist |
Laravel Address
A Laravel package for managing addresses with polymorphic relationships and hierarchical geographical data (countries, states, districts, subdistricts, and post offices).
Comes with built-in geographical data for Malaysia and a seeder command to populate reference tables.
Requirements
- PHP 8.4+
- Laravel 12 or 13
Installation
Install the package via Composer:
composer require hasyirin/laravel-address
Publish and run the migrations:
php artisan vendor:publish --tag="laravel-address-migrations"
php artisan migrate
Optionally publish the config file:
php artisan vendor:publish --tag="laravel-address-config"
Seeding Geographical Data
Seed countries, states, districts, subdistricts, and post offices:
php artisan address:seed
The command loads data from the package's data/ directory. To use your own data, place JSON files in your application's base_path('data') directory following the same structure.
Usage
Preparing Your Model
Add the InteractsWithAddresses trait to any model that should have addresses:
use Hasyirin\Address\Concerns\InteractsWithAddresses;
use Hasyirin\Address\Contracts\Addressable;
class User extends Model implements Addressable
{
use InteractsWithAddresses;
}
Creating Addresses
$user->address()->create([
'type' => 'primary',
'line_1' => '123 Main Street',
'line_2' => 'Suite 4B',
'line_3' => 'Taman Example',
'postcode' => '50000',
'country_id' => $country->id,
'state_id' => $state->id,
'post_office_id' => $postOffice->id,
'latitude' => 3.1390,
'longitude' => 101.6869,
'properties' => ['notes' => 'Front gate access'],
]);
Using the Factory
The Address model includes a factory for testing:
use Hasyirin\Address\Models\Address;
Address::factory()
->for($user, 'addressable')
->create([
'type' => 'primary',
'line_1' => 'No. 1, Jalan Test',
'postcode' => '40000',
]);
Retrieving Addresses
// Primary address (returns a default empty instance if none exists)
$user->address;
// All addresses
$user->addresses;
// Address by type
$user->getAddress('billing');
Querying by Type
The ofType scope accepts a string, array, or enum:
use Hasyirin\Address\Models\Address;
Address::ofType('shipping')->get();
Address::ofType(['billing', 'shipping'])->get();
Formatting
// Single-line comma-separated string
$address->formatted();
// "123 Main Street, Suite 4B, Taman Example, 50000, Kuala Lumpur, Selangor, Malaysia"
// Exclude state or country
$address->formatted(state: false);
$address->formatted(country: false);
// Uppercase
$address->formatted(capitalize: true);
HTML Rendering
// Multi-line with <p> tags
$address->render();
// Inline comma-separated
$address->render(inline: true);
// With options
$address->render(state: false, country: false, capitalize: true, margin: 1);
Copying an Address
Create an unsaved copy of an address (without the polymorphic relationship or type):
$copy = $address->copy();
$copy->type = 'billing';
$copy->addressable()->associate($anotherModel);
$copy->save();
Locality
Designate a "local" country, state, and district by setting their codes in config/address.php:
'locality' => [
'country' => 'MYS',
'state' => '14',
'district' => '02',
],
Resolve them anywhere via the static helper — it enforces the parent chain, so a state only counts as local if its country also matches the configured code:
Country::local(); // ?Country
State::local(); // ?State — matches state code AND parent country
District::local(); // ?District — matches district code AND parent state AND parent country
If any level is null in the config, the corresponding local() returns null.
Compose with the local() query scope:
District::query()->local()->where(...)->get();
The result is memoized per request via cache()->memo(). saved and deleted listeners on each model cascade-clear caches down the chain (Country save clears State and District caches too). For manual invalidation:
Country::clearLocalCache();
Configuration
// config/address.php
return [
// Codes used to resolve `Country::local()`, `State::local()`, `District::local()`
'locality' => [
'country' => null, // e.g. 'MYS'
'state' => null, // e.g. '14'
'district' => null, // e.g. '02'
],
// Override model classes
'models' => [
'address' => \Hasyirin\Address\Models\Address::class,
'country' => \Hasyirin\Address\Models\Country::class,
'state' => \Hasyirin\Address\Models\State::class,
'district' => \Hasyirin\Address\Models\District::class,
'subdistrict' => \Hasyirin\Address\Models\Subdistrict::class,
'post-office' => \Hasyirin\Address\Models\PostOffice::class,
],
// Override table names
'tables' => [
'addresses' => 'addresses',
'countries' => 'countries',
'states' => 'states',
'districts' => 'districts',
'subdistricts' => 'subdistricts',
'post_offices' => 'post_offices',
],
];
Data Structure
The package uses a hierarchical location model:
Country
└── State
├── District
│ └── Subdistrict
└── Post Office (with postcodes)
Each Address belongs to a Country, State, and optionally a PostOffice, and is polymorphically attached to any model via the addressable morph relationship.
Models
| Model | Key Fields |
|---|---|
Country |
code (ISO 3166-1 alpha-3), alpha_2, name |
State |
code, name, belongs to Country |
District |
code, name, belongs to State |
Subdistrict |
code, name, belongs to District |
PostOffice |
name, postcodes (JSON array), belongs to State |
Address |
type, line_1-line_3, postcode, latitude, longitude, properties (JSON) |
All models use soft deletes.
Extending Models
To use your own model classes, extend the package models and update the config:
use Hasyirin\Address\Models\Address as BaseAddress;
class Address extends BaseAddress
{
// your customizations
}
// config/address.php
'models' => [
'address' => \App\Models\Address::class,
// ...
],
Testing
composer test
Changelog
Please see CHANGELOG for more information on what has changed recently.
Credits
License
The MIT License (MIT). Please see License File for more information.
Related Packages
The simplest way to add country, state and city to Laravel application.
Powerful PHP database abstraction layer (DBAL) with many features for database s...
Laravel Serializable Closure provides an easy and secure way to serialize closur...
Version History
| Version | Released | PHP | Laravel | License |
|---|---|---|---|---|
| v4.0.5 | ^8.4 | ^12.0| | MIT | |
| v4.0.4 | ^8.4 | ^12.0| | MIT | |
| v4.0.3 | ^8.4 | ^12.0| | MIT | |
| v4.0.2 | ^8.4 | ^12.0| | MIT | |
| v4.0.1 | ^8.4 | ^12.0| | MIT | |
| v4.0.0 | ^8.4 | ^12.0| | MIT | |
| v3.0.0 | ^8.4 | ^11.0| | MIT | |
| v2.5.0 | ^8.4 | ^11.0| | MIT | |
| v2.4.2 | ^8.4 | ^11.0| | MIT | |
| v2.4.1 | ^8.4 | ^11.0| | MIT | |
| v2.4.0 | ^8.4 | ^11.0| | MIT | |
| v2.3.0 | ^8.4 | ^11.0| | MIT | |
| v2.2.0 | ^8.4 | ^11.0| | MIT | |
| v2.1.0 | ^8.4 | ^11.0| | MIT | |
| v2.0.0 | ^8.4 | ^11.0| | MIT | |
| v1.0.0 | ^8.4 | ^10.0| | MIT |