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

v2.3.0 2026-02-08 21:39 UTC

README

Latest Version on Packagist GitHub Tests Action Status GitHub Code Style Action Status Total Downloads

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.