hasyirin / laravel-address
This is my package laravel-address
Fund package maintenance!
Hasyirin Fakhriy
Installs: 55
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 1
Forks: 0
Open Issues: 0
pkg:composer/hasyirin/laravel-address
Requires
- php: ^8.4
- illuminate/contracts: ^11.0||^12.0
- spatie/laravel-package-tools: ^1.16
Requires (Dev)
- larastan/larastan: ^3.0
- laravel/pint: ^1.14
- nunomaduro/collision: ^8.1.1
- orchestra/testbench: ^9.0.0||^10.0
- pestphp/pest: ^3.0||^4.0
- pestphp/pest-plugin-arch: ^3.0||^4.0
- pestphp/pest-plugin-laravel: ^3.0||^4.0
- phpstan/extension-installer: ^1.3||^2.0
- phpstan/phpstan-deprecation-rules: ^1.1||^2.0
- phpstan/phpstan-phpunit: ^1.3||^2.0
This package is auto-updated.
Last update: 2026-02-08 21:45:48 UTC
README
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 11 or 12
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();
Configuration
// config/address.php return [ // Mark a country/state as "local" during seeding 'locality' => [ 'country' => null, // e.g. 'MYS' 'state' => null, // e.g. '01' ], // 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, local |
State |
code, name, local, 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.