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

Bangladeshgeocode Laravel Package

lemonpatwari/bangladeshgeocode

Laravel/PHP package providing Bangladesh geocode data—divisions, districts, upazilas, unions, and villages—with migrations, seeders, models, and helpers for building cascading location dropdowns and address forms.

View on GitHub
Deep Wiki
Context7

Getting Started

This package gives you the administrative geography of Bangladesh — Division → District → Upazila → Union — as seedable tables and (typically) Eloquent models, so you don't hand-maintain ~8 divisions, 64 districts, ~495 upazilas and ~4,500 unions.

Minimal path to a working feature:

composer require lemonpatwari/bangladeshgeocode
php artisan vendor:publish --provider="Lemonpatwari\BangladeshGeocode\BangladeshGeocodeServiceProvider"
php artisan migrate
php artisan db:seed --class="BangladeshGeocodeSeeder"   # verify the exact class name in the README

Because the package is small and young, confirm the publish tags and seeder class name by reading vendor/lemonpatwari/bangladeshgeocode/src/ and the README.md before you run anything in a shared database — that directory is where you should look first, not the docs. Check in order:

  1. config/ — table name prefixes, whether models are configurable, any enabled switches.
  2. database/migrations/ — column names (name, bn_name, division_id, etc.) and whether foreign keys are declared.
  3. database/seeders/ — how much data ships and in what format (JSON/PHP array), which tells you how long seeding takes.
  4. src/Models/ — relationships (districts(), upazilas(), unions()), and whether coordinates exist.

First use case: an address form with four dependent dropdowns. Load divisions server-side, then fetch districts/upazilas/unions via small JSON endpoints as the user selects. That's 90% of what this package is used for, and it validates the whole install in one screen.

Implementation Patterns

Reference the IDs, not the names. Store union_id (or the deepest level you collect) on your addresses/users/orders table and derive division/district/upazila through the relationships. Storing four denormalised name strings means every future spelling correction in the seed data becomes a data-migration for you.

Schema::table('addresses', function (Blueprint $table) {
    $table->foreignId('union_id')->nullable()->constrained('unions');
});

Cascading selects via a single controller. Expose one endpoint per level rather than four controllers:

Route::get('/geo/districts/{division}', fn (Division $division) =>
    $division->districts()->orderBy('name')->get(['id', 'name'])
);

Keep the payload to id + name. For Select2/Choices.js, add ->pluck('name', 'id') server-side so the front-end does no mapping.

Validate with exists, not custom rules. 'union_id' => ['required', 'integer', 'exists:unions,id'] is enough; the FK constraint is your second line of defence. Don't validate the parent IDs independently — derive them from the selected child to avoid inconsistent tuples (upazila A with union from upazila B).

Cache the top level aggressively, never the leaves. Divisions and districts are effectively static; unions can change. Cache::rememberForever('geo.divisions', ...) for the first dropdown, and query unions live. If you cache unions, key by upazila_id and set a TTL.

Seed once per environment, not per deploy. Put the geo seeder in a dedicated deploy step (php artisan db:seed --class=...) or a isGeoSeeded() guard in DatabaseSeeder, and use RefreshDatabase + the package seeder in feature tests so factories always have valid IDs to pick from.

Reporting. For "orders per division", join through rather than loading models: ->join('unions', ...)->join('upazilas', ...) and group by divisions.name. Model-level withCount chains get slow once you're scanning a large orders table.

Extension point: if you need coordinates or postcodes, add them in your own migration on top of the package's tables rather than editing vendor files — that keeps upgrades clean. If the models are published/configurable, subclass and bind your own in a service provider.

Gotchas and Tips

  • Seeding is slow in naive installs. Thousands of rows via Eloquent create() in a loop can take minutes. Check whether the seeder uses insert() or upsert(); if not, wrap it in a transaction or chunk it. Locally this is annoying; in CI it's a timeout.
  • Data freshness is on you. Union boundaries are reorganised periodically. There's no auto-update. Note the package version (last release 2026-03-05) in your composer.lock and treat a bump as a data migration requiring review, not a routine composer update.
  • Name transliteration varies widely. Dhaka/Dacca, Chattogram/Chittagong, Barishal/Barisal, Jhalokati/Jhalokathi. If you offer name search, normalise both sides and check whether the package stores Bangla names in a separate bn_name column — users search in Bangla. Set the column collation to utf8mb4_unicode_ci.
  • Don't filter names with LIKE 'x%'. "Cox's Bazar" and "Chapainawabganj" break intuition. Use LIKE '%term%' on the union level with a LIMIT, or a proper search index.
  • Verify whether lat/long exists. Many Bangladesh geo packages are name-hierarchy only. If your feature needs maps, confirm before you build around it — otherwise you'll be enriching the table yourself.
  • Foreign key order matters. If you truncate and reseed, disable FK checks or delete children first (unions → upazilas → districts → divisions), or the reseed fails midway and leaves you with orphans.
  • Low adoption is a real risk (12 stars, no dependents). Pin the version in composer.json ("lemonpatwari/bangladeshgeocode": "^1.0" with a committed lockfile), keep the migrations in version control rather than relying on vendor, and be prepared to fork — it's a small, self-contained dataset, which makes forking cheap.
  • Debugging "empty dropdown": log the actual HTTP response before blaming the front-end. Nine times in ten it's an unpublished migration (table missing → 500) or a seeder that never ran (200 with []). php artisan tinker with Division::count() distinguishes the two in one command.
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.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource