Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Filament Thailand Location Picker Laravel Package

baleethai/filament-thailand-location-picker

View on GitHub
Deep Wiki
Context7

Getting Started

Install the package and publish its assets:

composer require baleethai/filament-thailand-location-picker
php artisan vendor:publish --tag="thailand-plugin-config"
php artisan vendor:publish --tag="thailand-plugin-translations"   # new in v1.1.0
php artisan migrate --seed   # or the package's dedicated location seeder

Three things to look at first:

  1. config/thailand-plugin.php — the locale key (name_th vs name_en data column) and, as of v1.1.0, enable_geo (show/hide the Region/Geo select).
  2. lang/vendor/thailand-plugin/{en,th}.json — the new UI label/placeholder translations. Skim these to see exactly which strings you can override.
  3. The select components themselves (ProvinceSelect, DistrictSelect, SubdistrictSelect, ZipCodeSelect, plus the ThailandLocation composite field) — these are what you'll wire into forms.

First use case: an address block on a Customer or Order resource form. Drop the composite field into the schema, save the four IDs, and you have a working Region → Province → District → Subdistrict → Zip Code cascade in one line.

Implementation Patterns

Composite field for the common case. Use ThailandLocation when you want the whole cascade with default behaviour; use the individual *Select fields when you need to interleave your own fields or pre-fill from existing data.

use BaleeThai\FilamentThailandLocationPicker\Forms\ThailandLocation;

ThailandLocation::make('address')
    ->label(__('customer.address'))
    ->required(),

Flat columns vs. relations. The simplest integration stores province_id, district_id, subdistrict_id, zip_code as integer/string columns on the parent model. If you need reporting or joins, add belongsTo relations (province(), district(), subdistrict()) and eager-load them in table queries.

Locale is two independent axes — set both deliberately.

// config/thailand-plugin.php
'locale' => 'name_th',   // which data column is displayed/returned

// UI labels follow the app locale, not the config:
App::setLocale('th');    // Region/Province/District labels switch to Thai

This split is the most useful thing about v1.1.0: a Thai-facing admin can see Thai labels while an English-facing storefront shows name_en data, or vice versa, without fighting a single global setting.

Disabling Geo per form. When an address form doesn't need the region step (or the region is implied by context — e.g. a single-region warehouse), turn it off locally:

ThailandLocation::make('address')
    ->geoEnabled(false);   // ProvinceSelect then lists all provinces unfiltered

Keep config('thailand-plugin.enable_geo') as the app-wide default and use ->geoEnabled() only where the form genuinely differs. Mixing both aggressively makes behaviour hard to reason about during review.

Translations. Publish once, then edit the JSON files under lang/vendor/thailand-plugin/. Override only the keys you care about — Laravel's JSON translation falls back per-key, so a sparse file still works for the rest.

{ "Province": "จังหวัด", "Zip Code": "รหัสไปรษณีย์" }

Display in tables/infolists. Don't render raw IDs. Either eager-load the relation and use ->formatStateUsing(fn ($state, $record) => $record->subdistrict?->{$column}), or add a model accessor that respects config('thailand-plugin.locale') so table and form stay consistent.

Gotchas and Tips

  • Two locales, one config key name. thailand-plugin.locale does not affect the new UI translations. If labels stay English in a Thai app, check App::getLocale() and the published JSON, not the config file.
  • Stale region_id when Geo is off. With enable_geo => false or ->geoEnabled(false), the Region input disappears from the form but the column still exists. Either ->dehydrated(false) the region field, null it in a mutateFormDataBeforeSave, or scope the province list — otherwise old records keep a region that no longer matches what the user sees.
  • Config and translation caching. After changing enable_geo or editing published JSON, run php artisan optimize:clear (or at minimum config:clear / cache:clear). Cached config is the usual reason a toggle "doesn't work".
  • Publishing overwrites. Re-running vendor:publish --tag="thailand-plugin-translations" after an upgrade will clobber your edits unless you pass --force knowingly. Commit the published files to version control.
  • Zip codes are not unique to a subdistrict. Several subdistricts share a postal code, and a subdistrict can map to multiple codes in edge cases. Never use zip_code as the key for the cascade — drive the selects from IDs and treat zip as display/validation only.
  • Cascade resets. Changing a province must clear district/subdistrict/zip state. If you're composing individual *Select fields yourself you own that reset logic (->live() + afterStateUpdated); the composite ThailandLocation field handles it for you — prefer it unless you need custom ordering.
  • Seeding and upgrades. Location data ships with the package. On minor upgrades, re-run the seeder (or the package's data migration) so new subdistricts appear; existing IDs are intended to remain stable, but verify against a staging DB before rolling to production.
  • N+1 in tables. Listing 50 orders with province/district columns without ->with(['province','district']) is a classic 100-query page. Eager-load, or denormalise the display name onto the parent row at save time.
  • Extension points. If the built-in cascade doesn't fit, the individual *Select classes are the seam: subclass or compose them with your own options() / searchable() / preload() configuration rather than forking the package.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator
equipc/laravel-email-database-log
adedsec/nova-translated-labels