ihangan/laravel-moldova-cuatm 问题修复 & 功能扩展

解决BUG、新增功能、兼容多环境部署,快速响应你的开发需求

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

ihangan/laravel-moldova-cuatm

Composer 安装命令:

composer require ihangan/laravel-moldova-cuatm

包简介

Moldova's CUATM administrative-territorial divisions (raioane, cities, towns and villages) as Eloquent models with multilingual names and coordinates.

README 文档

README

Moldova CUATM for Laravel

Moldova CUATM for Laravel

Latest version Tests PHPStan Total downloads License

Every administrative-territorial unit of the Republic of Moldova, as an Eloquent model you can query straight away. The data comes from CUATM, the official classifier maintained by the National Bureau of Statistics, so the codes and the hierarchy match what government systems use.

You get the 32 raioane, the municipalities, the sectors of Chișinău, every town and all ~1,600 villages, plus Gagauzia and the Stînga Nistrului units. Each one carries its official CUATM code, a parent link, names in Romanian, Russian and Ukrainian, and WGS84 coordinates.

I built this for a rental classifieds site that needed a real location tree (region → city → sector → village) instead of a free-text address field, and pulled it out into a package because the dataset is useful on its own.

Requirements

  • PHP 8.3 or higher
  • Laravel 12 or 13

Installation

composer require ihangan/laravel-moldova-cuatm

Publish and run the migration, then load the data:

php artisan vendor:publish --tag="moldova-cuatm-migrations"
php artisan migrate
php artisan cuatm:import

cuatm:import is idempotent, so you can run it again whenever a new CUATM edition ships without ending up with duplicates.

Usage

The model is Ihangan\MoldovaCuatm\Models\Location. It behaves like any other Eloquent model.

use Ihangan\MoldovaCuatm\Models\Location;
use Ihangan\MoldovaCuatm\Enums\LocationType;

// By the official CUATM code or by slug.
Location::whereCode('0111001')->first();
Location::where('slug', 'chisinau')->first();

// Every raion.
Location::ofType(LocationType::Raion)->get();

// Top-level units only.
Location::roots()->get();

Names

Names are translatable (backed by spatie/laravel-translatable). Romanian is always present; Russian and Ukrainian exist for most localities; English is filled in for the larger cities.

$chisinau = Location::where('slug', 'chisinau')->first();

$chisinau->name;                          // current locale
$chisinau->getTranslation('name', 'ru');  // "Кишинёв"
$chisinau->getTranslation('name', 'uk');  // "Кишинів"

Set a fallback once (for example in a service provider) so a missing locale returns Romanian instead of an empty string:

use Spatie\Translatable\Facades\Translatable;

Translatable::fallback(fallbackLocale: 'ro', fallbackAny: true);

Hierarchy

The tree is a self-referencing parent_id.

$village = Location::where('slug', 'dobrogea')->first();

$village->parent;       // the town it belongs to
$village->ancestors();  // [town, sector, municipality], nearest first

$sector = Location::where('slug', 'botanica')->first();
$sector->children;      // towns and villages under Botanica

Coordinates

$location = Location::where('slug', 'chisinau')->first();

[$location->lat, $location->lng]; // 47.005..., 28.857...

Facade

A small facade wraps the common lookups when you would rather not write the queries by hand:

use Ihangan\MoldovaCuatm\Facades\Cuatm;

Cuatm::findByCode('0111001');
Cuatm::findBySlug('chisinau');
Cuatm::roots();          // raioane, municipalities, Gagauzia, Transnistria
Cuatm::raioane();
Cuatm::childrenOf($raion);
Cuatm::tree();           // roots with their children eager-loaded

Cascading location picker

roots() and childrenOf() are all you need to build a "pick a region, then a locality below it" selector. The hierarchy isn't a fixed depth (a raion goes straight to its villages, while Chișinău goes municipality → sector → town → village), so the picker keeps offering another dropdown while the chosen unit still has children.

use Ihangan\MoldovaCuatm\Facades\Cuatm;
use Livewire\Component;

class LocationPicker extends Component
{
    /** @var array<int, int> the selected location id at each level */
    public array $path = [];

    public function selectLevel(int $level, ?int $id): void
    {
        $this->path = array_slice($this->path, 0, $level); // drop the deeper levels

        if ($id !== null) {
            $this->path[$level] = $id;
        }
    }

    public function render()
    {
        $levels = collect([Cuatm::roots()]);

        foreach ($this->path as $id) {
            $children = Cuatm::childrenOf($id);

            if ($children->isEmpty()) {
                break; // reached the bottom of the tree
            }

            $levels->push($children);
        }

        return view('livewire.location-picker', ['levels' => $levels]);
    }
}
{{-- resources/views/livewire/location-picker.blade.php --}}
<div class="space-y-3">
    @foreach ($levels as $level => $options)
        <select wire:change="selectLevel({{ $level }}, $event.target.value)">
            <option value="">—</option>
            @foreach ($options as $location)
                <option value="{{ $location->id }}" @selected(($path[$level] ?? null) === $location->id)>
                    {{ $location->name }}
                </option>
            @endforeach
        </select>
    @endforeach
</div>

The selected location is the last entry in $path. Outside Livewire the same two calls drive any UI: render Cuatm::roots() first, then Cuatm::childrenOf($id) each time a level is chosen.

Configuration

Publish the config file if you need to change the table name, the connection or the locales:

php artisan vendor:publish --tag="moldova-cuatm-config"
return [
    'table' => 'cuatm_locations',
    'connection' => null,
    'locales' => ['ro', 'ru', 'uk', 'en'],
    'fallback_locale' => 'ro',
];

The table is named cuatm_locations rather than locations so it does not clash with one your application may already have.

Data and updates

The dataset lives in database/data/cuatm.json and ships with the package.

  • Codes, hierarchy and Romanian names come from CUATM (Clasificatorul unităților administrativ-teritoriale ale Republicii Moldova), published by the National Bureau of Statistics.
  • Russian and Ukrainian names are Wikidata exonyms.
  • Coordinates are from public geodata.

CUATM changes rarely. When the Bureau publishes a new edition, replace the JSON file and run php artisan cuatm:import again.

Testing

composer test

Changelog

See CHANGELOG.md and the releases.

Contributing

See CONTRIBUTING.md.

Security

Found a security issue? Email igorhangan@gmail.com instead of using the issue tracker. See SECURITY.md.

Credits

  • Igor Hangan
  • The administrative data comes from the CUATM classifier published by the National Bureau of Statistics of Moldova, with Russian and Ukrainian names from Wikidata.

License

The MIT License. See LICENSE.md.

The administrative data is public information from the National Bureau of Statistics of Moldova; Wikidata content is CC0.

统计信息

  • 总下载量: 0
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 1
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2026-06-30

承接程序开发

PHP开发

VUE

Vue开发

前端开发

小程序开发

公众号开发

系统定制

数据库设计

云部署

网站建设

安全加固