Installation:
composer require hoangphi/vietnam-maps
php artisan vietnam-map:install
This runs the default installation, publishing config and migrations, then imports the dataset into vietnam_maps_provinces, vietnam_maps_districts, and vietnam_maps_wards tables.
First Use Case: Fetch all provinces with their districts:
use HoangPhi\VietnamMap\Models\Province;
use HoangPhi\VietnamMap\Models\District;
$provinces = Province::with('districts')->get();
foreach ($provinces as $province) {
echo $province->name;
foreach ($province->districts as $district) {
echo " - {$district->name}";
}
}
config/vietnam-maps.php (customize table/column names).app/Models/HoangPhi/VietnamMap/ (Province, District, Ward).php artisan list (check vietnam-map: commands).Data Retrieval:
// Get all wards in a district
$wards = District::find($districtId)->wards;
$provinces = Province::with(['districts.wards'])->get();
Customizing Data:
database/migrations/{datetime}_create_vietnam_maps_table.php (e.g., population, postal_code).php artisan vendor:publish --tag=vietnam-map-models
Then modify app/Models/HoangPhi/VietnamMap/Province.php (e.g., add scopes):
public function scopeInSouthernRegion($query) {
return $query->whereIn('name', ['Ho Chi Minh', 'Binh Duong', 'Dong Nai']);
}
API Integration:
php artisan make:resource ProvinceResource
// app/Http/Resources/ProvinceResource.php
public function toArray($request) {
return [
'id' => $this->gso_id,
'name' => $this->name,
'districts' => DistrictResource::collection($this->districts),
];
}
Frontend Integration:
@foreach($provinces as $province)
<select name="province">
<option value="{{ $province->gso_id }}">{{ $province->name }}</option>
</select>
@foreach($province->districts as $district)
<option value="{{ $district->gso_id }}">-- {{ $district->name }}</option>
@endforeach
@endforeach
// Fetch districts via AJAX when province changes
$('#province').change(function() {
$.get(`/api/provinces/${this.value}/districts`, function(districts) {
$('#district').html(districts.map(d => `<option value="${d.id}">${d.name}</option>`).join(''));
});
});
Validation:
gso_id for validation (e.g., in Laravel Forms):
use HoangPhi\VietnamMap\Rules\ValidProvince;
$request->validate([
'province_id' => ['required', new ValidProvince],
]);
Migration Conflicts:
gso_id (General Statistics Office ID) remains unique and matches the original dataset. Avoid renaming it to something ambiguous like id.Data Overwrites:
vietnam-map:install command drops and recreates tables by default. Use --force carefully in production.--no-drop (if available) or manually handle data merges.Performance with Large Datasets:
with()) is critical for nested queries (e.g., provinces.districts.wards). Avoid N+1 queries:
// Bad: N+1 query
foreach (Province::all() as $province) {
$province->districts; // Loads districts one by one
}
// Good: Eager load
Province::with('districts')->get();
Model Binding Issues:
gso_id as the primary key for Provinces/Districts/Wards. If you override the key, ensure your routes and bindings align:
// In routes/web.php
Route::get('/provinces/{province:gso_id}', ...);
Verify Data Integrity:
$missingDistricts = District::whereNull('province_id')->get();
php artisan tinker
>>> $province = \HoangPhi\VietnamMap\Models\Province::find(1);
>>> $province->districts->count();
Log Queries:
config/logging.php or use a package like barryvdh/laravel-debugbar to inspect SQL.Extend the Dataset:
latitude, longitude) via migrations:
Schema::table('vietnam_maps_districts', function (Blueprint $table) {
$table->decimal('latitude', 10, 8)->nullable();
$table->decimal('longitude', 11, 8)->nullable();
});
Caching:
$provinces = Cache::remember('vietnam.provinces', now()->addHours(1), function() {
return Province::with('districts.wards')->get();
});
Localization:
localizations table and use Laravel’s localization features:
// Example: Add a `localized_names` JSON column
$province->localized_names = ['en' => 'Hanoi', 'vi' => 'HÃ Ná»™i'];
Testing:
// database/seeds/VietnamMapsSeeder.php
public function run() {
Province::factory()->count(10)->create()->each(function ($province) {
$province->districts()->saveMany(District::factory()->count(rand(5, 20))->make());
});
}
$province = Mockery::mock(\HoangPhi\VietnamMap\Models\Province::class);
$province->shouldReceive('districts')->andReturn(collect([$districtMock]));
Custom Commands:
vietnam-map:update command to pull latest data):
// app/Console/Commands/UpdateVietnamMaps.php
public function handle() {
$this->call('vietnam-map:install', ['--force' => true]);
}
How can I help you explore Laravel packages today?