coolsam/laravel-mongodb-world
A Laravel package that brings world geography data to MongoDB, offering models and seeders for countries, states, and cities with handy lookup helpers for building location-aware applications.
This package provides world reference data (countries, states/regions, cities) backed by MongoDB for Laravel applications. It is a data package rather than a UI package: you install it, seed the collections, then query the models directly from your own application code.
Install — note the package was renamed in v1.0.1:
composer require coolsam/laravel-mongo-world
If you were previously on coolsam/world, swap the requirement and run composer update; see Gotchas for namespace implications.
You need a working MongoDB connection configured in config/database.php under the mongodb driver (typically via mongodb/laravel-mongodb). The package assumes MongoDB is your store — it will not work against MySQL/Postgres without modification.
First steps, in order:
php artisan vendor:publish --tag=mongo-world-config
php artisan world:seed — check php artisan list after install, since the exact command name is the first thing to confirm in the package README.mongosh and check the collections exist and are non-empty before debugging your application code.First use case: a country/state dropdown in a signup form, resolved server-side:
use CoolSam\World\Models\Country;
$countries = Country::orderBy('name')->get(['id', 'name', 'iso2']);
Confirm the actual namespace and model names from the package source (vendor/coolsam/laravel-mongo-world/src/Models) — that directory is the authoritative reference and will answer most questions faster than the docs.
Reference data as a read-mostly store. Treat the world collections as static lookup data. Do not write to them from application code; seed them once per environment. If you need per-tenant overrides (custom regions, enabled-country lists), keep those in your own tables and join in application logic rather than mutating the package collections.
Dropdown endpoint pattern. Avoid shipping the full city dataset to the browser. Expose cascading endpoints:
// routes/api.php
Route::get('/geo/countries', fn () => Country::orderBy('name')->get(['id','name','iso2']));
Route::get('/geo/countries/{country}/states', fn (Country $country) => $country->states()->orderBy('name')->get(['id','name']));
Route::get('/geo/states/{state}/cities', fn (State $state) => $state->cities()->orderBy('name')->get(['id','name']));
Model route binding works normally if the models extend the MongoDB Eloquent base and use _id as the key — verify the key type (ObjectId vs string) before exposing IDs in URLs.
Caching. Country/state lists are ideal cache candidates because they never change between seeds:
$countries = Cache::rememberForever('geo.countries', fn () => Country::orderBy('name')->get()->toArray());
Remember to bust the cache in your deploy/seed pipeline, not per-request.
Search. For city autocomplete, a case-insensitive prefix query on an indexed field is usually sufficient:
City::where('name', 'regex', '^'.preg_quote($term, '/'))->limit(10)->get();
Ensure the index exists on the field you filter and sort by; without it, city queries degrade quickly at full dataset size.
Validation integration. A common integration is a custom validation rule that checks a submitted city/state pair actually belongs together, rather than trusting the client:
Validator::extend('geo_pair', fn ($attr, $value, $params, $validator) =>
City::where('_id', $value)->where('state_id', $params[0])->exists()
);
Seeding in CI. Keep the seed step idempotent and part of your test bootstrap so feature tests that touch geo data run against a known dataset. Prefer a truncated-then-reseed strategy over incremental inserts.
The v1.0.1 rename is the headline risk. coolsam/world → coolsam/laravel-mongo-world changes the Composer package name. Consequences to check on upgrade:
composer.json and any composer.lock pins; composer require coolsam/world will now resolve to the old name and may fail or resolve a stale version.vendor/coolsam/laravel-mongo-world/composer.json under autoload.psr-4 and grep your codebase for use CoolSam\World\ before assuming. This is the single most likely source of a post-upgrade fatal error.config/app.php (if not using auto-discovery) need the same audit.replace or provide entry, or pinned via a VCS repository, adjust accordingly.Package renames fragment documentation. Blog posts and Stack Overflow answers referencing coolsam/world still apply functionally, but install commands in them are stale. Filter accordingly.
MongoDB is a hard dependency. This will not drop into a MySQL-only project without work. If your app is relational, the pragmatic move is to export the seeded collections to a relational schema once and skip the runtime dependency entirely.
IDs are not integers. ObjectId values serialize as objects or 24-char hex strings depending on driver version. Cast explicitly at the API boundary, or you will ship {"$oid": "..."} to a JavaScript client expecting a string.
Relationship performance. Eager-load with with('states') for country lists; lazy loading per row against a remote MongoDB connection produces the classic N+1 latency spike. Conversely, do not eager-load deeply nested cities for a country-level request.
Connection config quirks. The package typically reads its own connection setting rather than the default. If queries return empty results, confirm both DB_CONNECTION and the package's config value point at the same database — a silent mismatch against an empty database looks identical to a broken install.
Index before seeding. Create indexes on name, iso2/iso3, and the parent reference fields after bulk insert. Creating them beforehand makes large seeds dramatically slower; creating them never makes queries slow forever.
Extension points. The models are ordinary Eloquent models, so override via a subclass rather than editing vendor files if you need extra scopes or accessors. If the package binds an interface in its service provider, prefer rebinding that to a subclass in your own AppServiceProvider — vendor edits are lost on the next composer update, which is exactly what just happened with the rename.
How can I help you explore Laravel packages today?