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

Location Module Laravel Package

visiosoft/location-module

Laravel location module for managing countries, regions, and cities in Visiosoft applications. Provides geographic models and migration-ready structures for adding hierarchical location data and selectors, helping organize and reference location records across your project.

View on GitHub
Deep Wiki
Context7

Getting Started

Prerequisites: a Laravel app running visiosoft/streams-platform (PyroCMS 2.x, OpenClassify, or a custom Streams install). This is not a standalone Laravel package — it depends on Streams' addon system, field types, and repository contracts. If you only need country/city data in vanilla Laravel, look at laravel-world / squire instead.

Install:

composer require visiosoft/location-module
php artisan addon:install visiosoft.module.location   # runs migrations + seeders

If addon:install isn't available, register Visiosoft\LocationModule\LocationModuleServiceProvider in config/app.php, then:

php artisan migrate
php artisan db:seed --class="Visiosoft\LocationModule\Country\CountrySeeder"

Where to look first:

  • src/LocationModuleServiceProvider.php — bindings for repositories and the addon registration.
  • src/Country/, src/State/, src/City/ (or src/Locations/) — each contains a *Model, *Repository + Contract/*RepositoryInterface, and a *Seeder. This trio is the pattern everywhere in the module.
  • migrations/ — confirms column names and FK order before you write joins.
  • resources/config/ and resources/lang/ — table names and any labels.

First use case: a cascading country → state → city select on a listing form, and filtering ads by location afterward.

use Visiosoft\LocationModule\Country\Contract\CountryRepositoryInterface;

public function create(CountryRepositoryInterface $countries)
{
    return view('listing.create', ['countries' => $countries->all()]);
}

Implementation Patterns

Resolve repositories, not models. The module binds *RepositoryInterface in its service provider and uses them for caching, pagination, and finders. Injecting CountryModel directly bypasses that logic.

public function __construct(
    private StateRepositoryInterface $states,
    private CityRepositoryInterface $cities,
) {}

Relationships are standard Eloquent. Country::find($id)->states (hasMany), State::find($id)->cities (hasMany). Use ->with('states') when you need them together.

Cascading select endpoints. Keep payloads small ({id, name}) and cache per parent:

Route::get('/api/countries/{country}/states', fn ($country) =>
    Cache::remember("states.$country", 3600, fn () =>
        app(StateRepositoryInterface::class)->findByCountry($country)
    )
);

Route::get('/api/states/{state}/cities', fn ($state) =>
    app(CityRepositoryInterface::class)->findByState($state)
);

Front-end: fetch states on country change, fetch cities on state change, clear downstream selects.

Filtering listings by location. Filter at the most specific level the user chose, with a fall-back to the parent:

Ad::query()
  ->when($request->city_id,  fn ($q) => $q->where('city_id', $request->city_id))
  ->when($request->state_id && ! $request->city_id,
        fn ($q) => $q->where('state_id', $request->state_id))
  ->when($request->country_id && ! $request->state_id,
        fn ($q) => $q->where('country_id', $request->country_id))
  ->paginate();

Seed once, in deployment only. Country/state/city datasets are large. Use targeted seeders in migrations and a tiny fixture set in tests (RefreshDatabase + a handful of hand-created countries), never the full seeder.

Extending via the service provider. To add ISO codes, currency, timezone, or a translated name:

$this->app->bind(CountryRepositoryInterface::class, MyCountryRepository::class);
// or extend the model

Streams field type integration. When used as a Streams field type, assign it to a form and read ->getValue() — it returns the resolved model/id depending on config. Prefer the field type over hand-rolled selects inside Streams forms.

Gotchas and Tips

  • Not standalone. Pulling this into plain Laravel means also installing streams-platform and its providers/config. Validate that cost before choosing it over a static location package.
  • Seeder memory. City dumps are tens of MB. Run with ini_set('memory_limit', '-1'), use chunked inserts, and never leave --seed in a routine migrate deploy step.
  • migrate --seed trap. It re-runs all seeders on every deploy; on the city table this is slow and can duplicate rows if the seeder isn't idempotent.
  • Migration order / FKs. states must migrate after countries, cities after states. On MySQL 5.7, check index length on name columns (utf8mb4 → 191 chars) or the index creation fails.
  • Table name collisions. Confirm actual table names in the migrations before writing joins. If another package already owns countries, you'll get duplicate-table errors; publish and rename the migrations.
  • Deletes and FKs. If city.state_id is restrictOnDelete, deleting a state fails silently. Add ->onDelete('cascade') in your own migrations where appropriate.
  • Caching invalidation. Repositories that cache all()/findByCountry won't notice edits or imports. Cache::forget("states.$countryId") after any write, or call Cache::flush() post-import.
  • i18n is not included. Names are typically English-only. Add a locations_translations table plus an accessor if you need multilingual labels.
  • No currency/timezone/ISO data unless you extend it — don't assume country.code or country.currency exists; inspect the migration first.
  • Debugging: php artisan addon:list | grep -i location, then dump(app(CountryRepositoryInterface::class)) to confirm the binding resolved. Check storage/logs/laravel.log for seeder FK failures.
  • Maintenance risk. 0 stars, 0 dependents, last release 2024-03-13. Pin an exact tag, fork on first issue, and treat upgrades as manual reviews. Extension points are the service-provider bindings, published migrations/config, and subclassed models — use those rather than editing vendor/.
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.
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons