laravel-admin-ext/china-distpicker
Laravel Admin extension that adds a cascading China province, city, and district picker to forms. It provides an easy-to-use regional selector for Chinese addresses, with built-in province, city, and district data and seamless integration into laravel-admin.
china-distpicker adds a three-column cascading region selector (省 / 市 / 区) to laravel-admin forms. It is a thin wrapper: the PHP side gives you a distpicker form field bound to three model columns, and the front end loads the distpicker JS bundle that filters city options by province and district by city. There is no server-side region logic at all — the package's job is the widget and the dataset.
Prerequisites and install:
# requires encore/laravel-admin 1.x already installed and bootstrapped
composer require laravel-admin-ext/china-distpicker
# publish the JS asset/data bundle if the release ships a publishable tag
php artisan vendor:publish --tag=laravel-admin-china-distpicker
php artisan admin:import ... # not needed; no menu item
Then clear caches (php artisan cache:clear, php artisan view:clear) and hard-refresh the browser.
Where to look first, in order:
README.md — the only real documentation; it is short, so read it fully.src/Distpicker.php — the field class. This tells you the exact method signature, whether it accepts a label, and whether it supports default().resources/views/ — the Blade template. This reveals the DOM ids/classes and which asset path is expected, which is what you debug when the selects render empty.public/vendor/laravel-admin-ext/china-distpicker/ (path may vary by version).First use case: an orders or shops table with province, city, district columns (all string, nullable or not, per your validation):
// app/Admin/Controllers/ShopController.php
protected function form()
{
$form = new Form(new Shop());
$form->text('name', '名称');
$form->distpicker(['province', 'city', 'district'], '所在地区');
$form->text('address', '详细地址');
return $form;
}
That's the whole happy path: one line replaces three separate select fields and the dependent-option wiring you would otherwise write by hand.
Keep the array order fixed. The field maps positionally to [province, city, district]. Renaming or reordering the array without renaming the columns is the most common cause of "values saved to the wrong column."
Edit forms work for free, if you name things consistently. The field hydrates from the model's attributes, so province/city/district in the array must match real DB columns and the model's $fillable. Any mismatch and the update silently drops the value.
Validation belongs outside the field. Treat the widget as UI-only and validate on the request:
// FormRequest or controller
'province' => 'required|string|max:64',
'city' => 'required|string|max:64',
'district' => 'required|string|max:64',
If you want a real integrity check ("南山区 must belong to 深圳市"), write a custom rule against the same dataset the JS uses. The package does not verify that the posted trio is a valid cascade, and a hand-crafted POST can submit any three strings.
Compose with the rest of the address. Regional cascade plus a free-text detail field is the standard pattern; put the detail field immediately after the distpicker so the visual order matches the stored order.
Reuse across resources. Wrap the three lines in a small trait or helper (AddressFormFields::attach($form)) rather than copy-pasting column names into every controller — you will change naming or validation once and want it in one place.
Conditional and read-only variants. Standard laravel-admin field methods (->rules(), ->help(), ->when(), ->hide(), ->readonly()) apply to the field, but verify each on your version — see the gotcha on rules() below.
Display side. $show->field('province') prints the raw stored string. If you store codes rather than names, add ->using(fn ($v) => RegionMap::name($v)) so grids and detail pages stay readable.
Imports and APIs. Because storage is just three strings, seeding/importing rows needs no special handling — only vocabulary consistency (names vs. codes, 省 vs. 广东省). Normalize before insert or your grids will mix formats.
If you are not on laravel-admin 1.x: do not try to install this. On OpenAdmin, Filament or Nova, port the Blade template and register it as a custom field, or use a generic two-to-three-level cascading select component. The JS library at the core is replaceable; the PHP wrapper is ~50 lines.
composer.json constraints, and re-test rather than fighting --ignore-platform-reqs in CI.public/vendor/.... Re-publish with --force, then hard-refresh.rules() on multi-column fields is the sharp edge. In laravel-admin, a field that spans multiple columns typically applies its rules to one column name only. Do not assume ->rules('required') validates all three — put the rules in your FormRequest/controller and add a test that posts a blank province.saving callback that skips the trio when unchanged, or make the columns part of a required contract.resources/views/ before assuming it is safe; overriding the view via ->view('admin.fields.distpicker') with unique ids is the fix.select names, confirm the JS asset 200s in the network tab, confirm the model's $fillable includes all three columns, then check the DB write in ->saved().china-area-data, element-china-area-data). All three are low-effort compared to replacing the whole admin panel.How can I help you explore Laravel packages today?