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

Gb T 2260 Laravel Package

medz/gb-t-2260

medz/gb-t-2260 gives PHP and Laravel apps offline access to China's GB/T 2260 administrative division codes, mapping provinces, cities, districts and counties to standard codes. Look up regions by code or name, build cascading address selectors, and seed region tables. Handy for registration forms,...

View on GitHub
Deep Wiki
Context7

Getting Started

This is a data-only package, not a Laravel service provider. There is no config to publish, no migrations, no facades — it ships the GB/T 2260 (中华人民共和国行政区划代码) dataset as PHP arrays/objects you can read.

composer require medz/gb-t-2260

First steps:

  1. Open src/ and read the single class that exposes the dataset (typically GbT2260::provinces(), ::cities($provinceCode), ::areas($cityCode), or a tree() method — verify the exact method names in the source, since the package is not documented beyond the README).
  2. Skim GB/T 2260 to understand the 6-digit code layout: PPCCDD where PP = province, CC = city, DD = district/county. 110000 = Beijing, 110100 = Beijing city proper, 110101 = Dongcheng district.
  3. First use case: populate an address form's cascading <select> elements.
use Medz\GbT2260\GbT2260;

$provinces = GbT2260::provinces();
// [['code' => '110000', 'name' => '北京市'], ...]

$cities = GbT2260::cities('440000'); // Guangdong
$areas  = GbT2260::areas('440100');  // Guangzhou

If your only need is a static dropdown, this package is enough. If you need province/city/district lookup by code inside a request, wrap it once (see below) rather than calling it repeatedly.

Implementation Patterns

Cascading address selector. Return the three levels as JSON from three thin controller actions or one ?parent=code action. Cache the arrays per level; the underlying data never changes at runtime.

Route::get('/regions/{parent?}', function (?string $parent = null) {
    return cache()->rememberForever("regions:$parent", fn () =>
        $parent === null
            ? GbT2260::provinces()
            : (str_ends_with($parent, '0000')
                ? GbT2260::cities($parent)
                : GbT2260::areas($parent))
    );
});

Denormalized read model. The package data is static and small (~3k rows). Seed your own regions table once via a custom Artisan command, then use Eloquent relationships (Province hasMany City hasMany District). This gives you joins, indexes, and whereIn filtering that the package itself cannot.

// database/seeders/RegionSeeder.php
foreach (GbT2260::provinces() as $p) {
    Province::create($p);
    foreach (GbT2260::cities($p['code']) as $c) {
        City::create($c + ['province_code' => $p['code']]);
        foreach (GbT2260::areas($c['code']) as $a) {
            District::create($a + ['city_code' => $c['code']]);
        }
    }
}

Validation rule. Register a custom rule so user-submitted codes cannot drift from the canonical set:

class ValidRegionCode implements ValidationRule {
    public function validate(string $attr, mixed $value, Closure $fail): void {
        if (! GbT2260::exists((string) $value)) {
            $fail('The :attribute is not a valid GB/T 2260 code.');
        }
    }
}

Address storage. Store the three codes (province_code, city_code, district_code) plus a free-text street field. Never store the names — they change on administrative reshuffles (see below). Resolve names on read via a lookup map built once per request.

Building a tree for a single API response. If your frontend prefers a nested structure, flatten once and memoise:

$tree = cache()->rememberForever('gb2260.tree', function () {
    return collect(GbT2260::provinces())->map(fn ($p) => [
        ...$p,
        'children' => collect(GbT2260::cities($p['code']))->map(fn ($c) => [
            ...$c,
            'children' => GbT2260::areas($c['code']),
        ])->all(),
    ])->all();
});

Wrapping as a repository. Because the package is a bare static accessor, wrap it in an injectable RegionRepository so the rest of your app never couples to Medz\GbT2260\GbT2260 directly. This is your extension point if you ever swap to a fresher dataset (e.g., scraped from the National Bureau of Statistics) without a project-wide find/replace.

Gotchas and Tips

  • The data is frozen at ~2020. The last release was 2020-04-13, and administrative divisions are consolidated, split, and renamed frequently (e.g., county-level city upgrades, urban district mergers). Codes that were valid in the package may be retired or reassigned today. Treat the package as a snapshot, not a live source of truth, and refresh the dataset yourself if accuracy matters — do not assume the package will be updated.
  • Treat codes as strings, always. 110000 is fine as an int, but county codes like 110101 and town-level codes with leading zeros will bite you if you ever cast to int or store in an INT column. Use CHAR(6) in MySQL and string in PHP.
  • Municipality codes have a synthetic middle level. For 北京/上海/天津/重庆, the provincial code xx0000 maps to a "city" xx0100 labelled 市辖区, and the actual districts hang off that. Your cascading UI must not stop at province for these four — otherwise users in Beijing see no districts.
  • 省直辖县级行政区划 (659xxx, 419xxx, 429xxx, 469xxx) break the strict 3-level assumption: a "city" node may contain county-level cities directly. If you build a tree with rigid Province→City→District semantics, you will misclassify these. Handle them as a "no intermediate city" branch, or display the city level as the district level for these parents.
  • Taiwan, Hong Kong, Macau are included as province-level codes (710000, 810000, 820000) but typically with no children in this dataset. Handle empty child arrays in your UI (hide the city select instead of showing an empty dropdown).
  • No service provider, no config, no facade. Don't add it to config/app.php providers — there's nothing to boot. Autoloading is enough. If you're on Laravel 11+ and looking for a providers entry, there is none to add.
  • No caching built in. Each call reconstructs its array from PHP files. On hot endpoints, memoise at the app level (as shown above) or seed to the database. Repeated ::provinces() calls in a single request are cheap but not free.
  • Debugging a "missing" district. Before assuming a bug in the package, look up the code in the NBS 统计用区划代码 (or a current mirror). If the code exists in NBS but not the package, you've hit the 2020 staleness — the region was created after the snapshot, not missed by a bug.
  • Extension points are effectively "none" beyond forking. The value of this package is its data file, not its API. If you need town-level (xxxyyy) codes, historical codes, or pinyin search, this package does not have it — plan to vendor your own dataset and keep the repository abstraction in your app layer.
  • 106 stars, 0 dependents, last release 2020. Do not build a long-lived dependency on its API surface. Copy the data into a seeder and drop the package from composer.json once seeded — that's the cleanest way to insulate your app from abandonment.
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.
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle