creasi/laravel-nusa

A Laravel package that aim to provide Indonesia' Administrative Data

Fund package maintenance!
creasico

v0.1.9 2024-10-06 05:55 UTC

README

Version License Actions Status

Creasi Nusa

Simple library aims to provide Indonesia Administrative Region Data based including the coordinates and postal codes, that easily integrated with our laravel project.

Requirements

  • PHP >=v8.1 with php-sqlite3 extension
  • Laravel >=10.0

Why?

Why don't just use existsing laravolt/indonesia, you may ask? That packages have been around for quite sometimes and already 've been used by hundreds of people, indeed. But, we need a package that ready-to-use once its installed.

I've been using edwardsamuel/Wilayah-Administratif-Indonesia for a while and put some contributions there, but it seems no longer maintained since 2018. More over its built for python not PHP.

That's why we choose cahyadsn/wilayah it has robust and strong database in terms of legality, but its not actually a package that can be installed as dependency. By that said, it has some work to-do.

We also found that w3appdev/kodepos provides better database structures that can easily mapped with databases from cahyadsn/wilayah in single query.

Our takes for the words "easily integrated" and "ready-to-use once its installed" means we shouldn't dealing with the data migration and seeding, hence Indonesia isn't a small country, right? running seeder for such amount of data can takes quite some times to proceed let alone the app seeder.

Why PHP >=8.1 and Laravel >=10.0, you may ask? Because, why not!

Installation

composer require creasi/laravel-nusa

That's all

Roadmaps

  • Basic Models
    • Provinces, includes laltitude, longitude and coordinates
    • Regencies, includes laltitude, longitude and coordinates
    • Districts
    • Vilages, include postal_code
  • Routing

Usage

Thankfully Laravel provides us convenience way to have some sort of "relations" regardless of the database engine. So we can have this administrative data shipped in sqlite data and once we install it, then all we need is use it from our project with convenience of eloquent models.

ReSTful API

  • Get all provinces

    GET {APP_URL}/nusa/provinces

    • Query Params :

    • Example :

      • GET http://localhost:8000/nusa/provinces
        {
          "data": [
            {
              "code": 33,
              "name": "Jawa Tengah",
              "latitude": -6.9934809206806,
              "longitude": 110.42024335421,
              "coordinates": [...],
              "postal_codes": [...],
            },
            { ... }
          ],
          "links": {
            "first": "http://localhost:8000/nusa/provinces?page=1",
            "last": "http://localhost:8000/nusa/provinces?page=3",
            "prev": null,
            "next": "http://localhost:8000/nusa/provinces?page=2",
          },
          "meta": {
            "current_page": 1,
            "from": 1,
            "last_page": 3,
            "links": [
              {
                "url": null,
                "label": "« Previous",
                "active": false,
              },
              {
                "url": "http://localhost:8000/nusa/provinces?page=1",
                "label": "1",
                "active": true,
              },
              { ... },
              {
                "url": "http://localhost/nusa/provinces?page=2",
                "label": "Next »",
                "active": false,
              },
            ],
            "path": "http://localhost:8000/nusa/provinces",
            "per_page": 15,
            "to": 15,
            "total": 34
          }
        }
  • Show a province

    GET {APP_URL}/nusa/provinces/{province}

    • Route Param : {province} province code
    • Example :
      • GET http://localhost:8000/nusa/provinces/33
        {
          "data": {
            "code": 33,
            "name": "Jawa Tengah",
            "latitude": -6.9934809206806,
            "longitude": 110.42024335421,
            "coordinates": [...],
            "postal_codes": [...],
          },
          "meta": {}
        }
  • Get all regencies in a province

    GET {APP_URL}/nusa/provinces/{province}/regencies

    • Route Param : {province} province code
    • Example :
      • GET http://localhost:8000/nusa/provinces/33/regencies
        {
          "data": [
            {
              "code": 3375,
              "province_code": 33,
              "name": "Kota Pekalongan",
              "latitude": -6.8969497174987,
              "longitude": 109.66208089654,
              "coordinates": [...],
              "postal_codes": [...],
            },
            { ... }
          ],
          "links": {
            "first": "http://localhost:8000/nusa/provinces/33/regencies?page=1",
            "last": "http://localhost:8000/nusa/provinces/33/regencies?page=3",
            "prev": null,
            "next": "http://localhost:8000/nusa/provinces/33/regencies?page=2",
          },
          "meta": {
            "current_page": 1,
            "from": 1,
            "last_page": 3,
            "links": [
              {
                "url": null,
                "label": "« Previous",
                "active": false,
              },
              {
                "url": "http://localhost:8000/nusa/provinces/33/regencies?page=1",
                "label": "1",
                "active": true,
              },
              { ... },
              {
                "url": "http://localhost/nusa/provinces/33/regencies?page=2",
                "label": "Next »",
                "active": false,
              },
            ],
            "path": "http://localhost:8000/nusa/provinces/33/regencies",
            "per_page": 15,
            "to": 15,
            "total": 35
          }
        }
  • Get all districts in a province

    GET {APP_URL}/nusa/provinces/{province}/districts

    • Route Param : {province} province code
    • Example :
      • GET http://localhost:8000/nusa/provinces/33/districts
        {
          "data": [
            {
              "code": 330101,
              "regency_code": 3301,
              "province_code": 33,
              "name": "Kedungreja",
              "postal_codes": [
                53263
              ]
            },
            { ... }
          ],
          "links": {
            "first": "http://localhost:8000/nusa/provinces/33/districts?page=1",
            "last": "http://localhost:8000/nusa/provinces/33/districts?page=39",
            "prev": null,
            "next": "http://localhost/nusa/provinces/33/districts?page=2",
          },
          "meta": {
            "current_page": 1,
            "from": 1,
            "last_page": 39,
            "links": [
              {
                "url": null,
                "label": "« Previous",
                "active": false,
              },
              {
                "url": "http://localhost:8000/nusa/provinces/33/districts?page=1",
                "label": "1",
                "active": true,
              },
              { ... },
              {
                "url": "http://localhost/nusa/provinces/33/districts?page=2",
                "label": "Next »",
                "active": false,
              },
            ],
            "path": "http://localhost:8000/nusa/provinces/33/districts",
            "per_page": 15,
            "to": 15,
            "total": 576
          }
        }
  • Get all villages in a province

    GET {APP_URL}/nusa/provinces/{province}/villages

    • Route Param : {province} province code
    • Example :
      • GET http://localhost:8000/nusa/provinces/33/villages
        {
          "data": [
            {
              "code": 3301012001,
              "district_code": 330101,
              "regency_code": 3301,
              "province_code": 33,
              "name": "Tambakreja",
              "postal_code": 53263,
            },
            { ... }
          ],
          "links": {
            "first": "http://localhost:8000/nusa/provinces/33/villages?page=1",
            "last": "http://localhost:8000/nusa/provinces/33/villages?page=571",
            "prev": null,
            "next": "http://localhost:8000/nusa/provinces/33/villages?page=2",
          },
          "meta": {
            "current_page": 1,
            "from": 1,
            "last_page": 571,
            "links": [
              {
                "url": null,
                "label": "« Previous",
                "active": false,
              },
              {
                "url": "http://localhost:8000/nusa/provinces/33/villages?page=1",
                "label": "1",
                "active": true,
              },
              { ... },
              {
                "url": "http://localhost:8000/nusa/provinces/33/villages?page=2",
                "label": "Next »",
                "active": false,
              },
            ],
            "path": "http://localhost:8000/nusa/provinces/33/villages",
            "per_page": 15,
            "to": 15,
            "total": 8562
          }
        }
  • Get all regencies

    GET {APP_URL}/nusa/regencies

    • Query Params :

    • Example :

      • GET http://localhost:8000/nusa/regencies
        {
          "data": [
            {
              "code": 3375,
              "province_code": 33,
              "name": "Kota Pekalongan",
              "latitude": -6.8969497174987,
              "longitude": 109.66208089654,
              "coordinates": [...],
              "postal_codes": [...],
            },
            { ... }
          ],
          "links": {
            "first": "http://localhost:8000/nusa/regencies?page=1",
            "last": "http://localhost:8000/nusa/regencies?page=35",
            "prev": null,
            "next": "http://localhost:8000/nusa/regencies?page=2",
          },
          "meta": {
            "current_page": 1,
            "from": 1,
            "last_page": 35,
            "links": [
              {
                "url": null,
                "label": "« Previous",
                "active": false,
              },
              {
                "url": "http://localhost:8000/nusa/regencies?page=1",
                "label": "1",
                "active": true,
              },
              { ... },
              {
                "url": "http://localhost/nusa/regencies?page=2",
                "label": "Next »",
                "active": false,
              },
            ],
            "path": "http://localhost:8000/nusa/regencies",
            "per_page": 15,
            "to": 15,
            "total": 514
          }
        }
  • Show a regency

    GET {APP_URL}/nusa/regencies/{regency}

    • Route Param : {regency} regency code
    • Example :
      • GET http://localhost:8000/nusa/regencies/3375
        {
          "data": {
            "code": 3375,
            "province_code": 33,
            "name": "Kota Pekalongan",
            "latitude": -6.8969497174987,
            "longitude": 109.66208089654,
            "coordinates": [...],
            "postal_codes": [...],
          },
          "meta": {}
        }
  • Get all districts in a regency

    GET {APP_URL}/nusa/regencies/{regency}/districts

    • Route Param : {regency} regency code
    • Example :
      • GET http://localhost:8000/nusa/regencies/3375/districts
        {
          "data": [
            {
              "code": 337501,
              "regency_code": 3375,
              "province_code": 33,
              "name": "Pekalongan Barat",
              "postal_codes": [
                51111
                51112
                51113
                51116
                51117
                51151
              ]
            },
            { ... }
          ],
          "links": {
            "first": "http://localhost:8000/nusa/regencies/3375/districts?page=1",
            "last": "http://localhost:8000/nusa/regencies/3375/districts?page=1",
            "prev": null,
            "next": null,
          },
          "meta": {
            "current_page": 1,
            "from": 1,
            "last_page": 1,
            "links": [
              {
                "url": null,
                "label": "« Previous",
                "active": false,
              },
              {
                "url": "http://localhost:8000/nusa/regencies/3375/districts?page=1",
                "label": "1",
                "active": true,
              },
              { ... },
              {
                "url": null,
                "label": "Next »",
                "active": false,
              },
            ],
            "path": "http://localhost:8000/nusa/regencies/3375/districts",
            "per_page": 15,
            "to": 4,
            "total": 4
          }
        }
  • Get all villages in a regency

    GET {APP_URL}/nusa/regencies/{regency}/villages

    • Route Param : {regency} regency code
    • Example :
      • GET http://localhost:8000/nusa/regencies/3375/villages
        {
          "data": [
            {
              "code": 3375011002,
              "district_code": 337501,
              "regency_code": 3375,
              "province_code": 33,
              "name": "Medono",
              "postal_code": 51111,
            },
            { ... }
          ],
          "links": {
            "first": "http://localhost:8000/nusa/regencies/3375/villages?page=1",
            "last": "http://localhost:8000/nusa/regencies/3375/villages?page=2",
            "prev": null,
            "next": "http://localhost:8000/nusa/regencies/3375/villages?page=2",
          },
          "meta": {
            "current_page": 1,
            "from": 1,
            "last_page": 2,
            "links": [
              {
                "url": null,
                "label": "« Previous",
                "active": false,
              },
              {
                "url": "http://localhost:8000/nusa/regencies/3375/villages?page=1",
                "label": "1",
                "active": true,
              },
              { ... },
              {
                "url": "http://localhost:8000/nusa/regencies/3375/villages?page=2",
                "label": "Next »",
                "active": false,
              },
            ],
            "path": "http://localhost:8000/nusa/regencies/3375/villages",
            "per_page": 15,
            "to": 15,
            "total": 27
          }
        }
  • Get all districts

    GET {APP_URL}/nusa/districts

    • Query Params :

    • Example :

      • GET http://localhost:8000/nusa/districts
        {
          "data": [
            {
              "code": 110101,
              "regency_code": 1101,
              "province_code": 11,
              "name": "Bakongan",
              "postal_codes": [
                23773
              ],
            },
            { ... }
          ],
          "links": {
            "first": "http://localhost:8000/nusa/districts?page=1",
            "last": "http://localhost:8000/nusa/districts?page=485",
            "prev": null,
            "next": "http://localhost:8000/nusa/districts?page=2",
          },
          "meta": {
            "current_page": 1,
            "from": 1,
            "last_page": 485,
            "links": [
              {
                "url": null,
                "label": "« Previous",
                "active": false,
              },
              {
                "url": "http://localhost:8000/nusa/districts?page=1",
                "label": "1",
                "active": true,
              },
              { ... },
              {
                "url": "http://localhost:8000/nusa/districts?page=2",
                "label": "Next »",
                "active": false,
              },
            ],
            "path": "http://localhost:8000/nusa/districts",
            "per_page": 15,
            "to": 15,
            "total": 7266
          }
        }
  • Show a district

    GET {APP_URL}/nusa/districts/{district}

    • Route Param : {district} district code
    • Example :
      • GET http://localhost:8000/nusa/districts/337503
        {
          "data": {
            "code": 337503,
            "regency_code": 3375,
            "province_code": 33,
            "name": "Pekalongan Utara",
            "postal_codes": [
              51141
              51143
              51146
              51147
              51148
              51149
            ],
          },
          "meta": {}
        }
  • Get all villages in a district

    GET {APP_URL}/nusa/districts/{district}/villages

    • Route Param : {district} district code
    • Example :
      • GET http://localhost:8000/nusa/districts/337503/villages
        {
          "data": [
            {
              "code": 3375031002,
              "district_code": 337503,
              "regency_code": 3375,
              "province_code": 33,
              "name": "Krapyak",
              "postal_code": 51147,
            },
            { ... }
          ],
          "links": {
            "first": "http://localhost:8000/nusa/districts/337503/villages?page=1",
            "last": "http://localhost:8000/nusa/districts/337503/villages?page=1",
            "prev": null,
            "next": null,
          },
          "meta": {
            "current_page": 1,
            "from": 1,
            "last_page": 1,
            "links": [
              {
                "url": null,
                "label": "« Previous",
                "active": false,
              },
              {
                "url": "http://localhost:8000/nusa/districts/337503/villages?page=1",
                "label": "1",
                "active": true,
              },
              { ... },
              {
                "url": null,
                "label": "Next »",
                "active": false,
              },
            ],
            "path": "http://localhost:8000/nusa/districts/337503/villages",
            "per_page": 15,
            "to": 7,
            "total": 7
          }
        }
  • Get all villages

    GET {APP_URL}/nusa/villages

    • Query Params :

    • Example :

      • GET http://localhost:8000/nusa/villages
        {
          "data": [
            {
              "code": 1101012001,
              "district_code": 110101,
              "regency_code": 1101,
              "province_code": 11,
              "name": "Keude Bakongan",
              "postal_code": 23773,
            },
            { ... }
          ],
          "links": {
            "first": "http://localhost:8000/nusa/villages?page=1",
            "last": "http://localhost:8000/nusa/villages?page=5565",
            "prev": null,
            "next": "http://localhost:8000/nusa/villages?page=2",
          },
          "meta": {
            "current_page": 1,
            "from": 1,
            "last_page": 5565,
            "links": [
              {
                "url": null,
                "label": "« Previous",
                "active": false,
              },
              {
                "url": "http://localhost:8000/nusa/villages?page=1",
                "label": "1",
                "active": true,
              },
              { ... },
              {
                "url": "http://localhost:8000/nusa/villages?page=2",
                "label": "Next »",
                "active": false,
              },
            ],
            "path": "http://localhost:8000/nusa/villages",
            "per_page": 15,
            "to": 15,
            "total": 83467
          }
        }
  • Get a village

    GET {APP_URL}/nusa/villages/{village}

    • Route Param : {village} village code
    • Example :
      • GET http://localhost:8000/nusa/villages/3375031006
        {
          "data": {
            "code": 3375031006,
            "district_code": 337503,
            "regency_code": 3375,
            "province_code": 33,
            "name": "Padukuhan Kraton",
            "postal_code": 51146,
          },
          "meta": {}
        }

Models

This library comes with 4 primary models as follows :

  • Creasi\Nusa\Models\Province
  • Creasi\Nusa\Models\Regency
  • Creasi\Nusa\Models\District
  • Creasi\Nusa\Models\Village

Every models comes with similar interfaces, which mean every model has code and name field in it, you can also use search() scope method to query model either by code or name. e.g:

use Creasi\Nusa\Models\Province;

$province = Province::search(33)->first();

// or

$province = Province::search('Jawa Tengah')->first();

Please note that only Province and Regency that comes with latitude, longitude and coordinates data, while Village comes with postal_code. That's due to what cahyadsn/wilayah provide us.

In that regard we expect that Province, Regency and District should have access of any postal_codes that available within those area, and we believe that might be helpful in some cases. And there you go

// Retrieve distict list of postal codes available in the province
$province->postal_codes;

Base on our experiences developing huge variety of products, the most use cases we need such a data is to fill up address form. But the requirement is might be vary on every single project. For that reason we also provide the bare minimun of Address model that use your default db connection and can easily extended to comply with project's requirement.

In that case you might wanna use our WithAddresses or WithAdress trait to your exitsting model, like so

use Creasi\Nusa\Contracts\HasAddresses;
use Creasi\Nusa\Models\Concerns\WithAddresses;

class User extends Model implements HasAddresses
{
    use WithAddresses;
}

To be able to use Address model, all you need is to publish the migration, like so

php artisan vendor:publish --tag creasi-migrations

Then simply run artisan migrate to apply the additional migrations.

Databases

The database structure documentation please consult to database/README.md.

Customization

By default, nusa will add another database.connections config to your project and use it as main database for all nusa's models, and you can customize it anyway.

  1. Publish nusa's config by running the following commands

    ./artisan vendor:publish --tag creasi-nusa-config
  2. Add new database.connections with key of your creasi.nusa.connection, say you have

    // config/nusa.php
    return [
        'connection' => 'indonesia',
    ];

    So, you'll need

    // config/database.php
    return [
        'connections' => [
            'indonesia' => [
                // ...
            ]
        ],
    ];

In term of extending Address model, please a look at creasi.nusa.addressable config if you wanna use your own implementation of Address model.

Notes

As of now, only connection name and table names are available to customize, also we only test it using sqlite driver. Let us know if you had any issue using another database drivers.

Contributing

  1. Clone the repo and cd into it

    git clone --recurse-submodules git@github.com:creasico/laravel-nusa.git
  2. Install dependencies

    composer install
  3. Create new database, by default we use the following configuration :

    • dbname : nusantara
    • dbhost : 127.0.0.1
    • dbuser : root
    • dbpass : secret
    mysql -e 'create database nusantara;'
  4. Last but not least, run db:import command

    composer db:import

If you were using different configuration you can edit this file but please don't submit your changes.

Once you've done with your meaningful contributions, run the following command to ensure everythings is works as expected.

composer test

Notes

Credits

License

This library is open-sourced software licensed under MIT license.