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

Geonames Laravel Package

farzai/geonames

Laravel package for working with GeoNames data such as countries, cities, timezones and currencies. Provides a simple, expressive API backed by a local database, so you can query geographic data offline without relying on external services or API limits. Easy to install and integrate.

View on GitHub
Deep Wiki
Context7

Getting Started

Requires PHP 8.3+. If you're on 2.x, read UPGRADING.md before touching composer.json — this is a rewrite, not a bump.

composer require farzai/geonames:^3.0
php artisan vendor:publish --tag=geonames-config
php artisan vendor:publish --tag=geonames-migrations
php artisan migrate

Data no longer ships with the package or is fetched at runtime. You explicitly pull the snapshots you need:

php artisan geonames:fetch          # interactive / default set
geonames fetch country              # or target a specific snapshot from the CLI

Both commands download from https://data.farzai.com/geonames/, verify the pinned sha256, and persist into your configured storage. Confirm the snapshots landed where you expect (check config/geonames.php) before writing queries — the most common "nothing works" report in v3 is a fetch that targeted a different directory than the app reads from.

First use case: seed your app's own lookup tables or validate user input against real geography.

use Farzai\Geonames\Facades\Geonames;

$fr = Geonames::country('FR');           // read-only model
$cities = Geonames::cities('FR');        // collection of read-only models
$paris = Geonames::city('2988507');      // by geoname id
$near = Geonames::nearest(48.8566, 2.3522, limit: 10);

Seeders shipped with the package help you stand up a full dataset for tests or CI:

php artisan db:seed --class="Farzai\\Geonames\\Database\\Seeders\\GeonamesSeeder"

Implementation Patterns

Treat snapshots as build artifacts, not app data. Fetch in a deployment step or a scheduled job, not on every request. Because the runtime is pure-read (no HTTP, no zip), you can safely call Geonames::* from anywhere — controllers, jobs, model accessors — without worrying about outbound latency or third-party outages.

Autocomplete / search. Geonames::search() is the workhorse for user-facing type-ahead. Scope it by country or admin level when you can, and debounce on the client. Combine with cities() when you need structured results rather than a flat list.

Validation rules. Use the bundled rules for address forms and onboarding flows instead of a brittle regex or a hardcoded list of countries:

$request->validate([
    'country'   => ['required', new CountryRule()],
    'admin1'    => ['nullable', new Admin1Rule($request->country)],
    'postal'    => ['nullable', new PostalCodeRule($request->country)],
]);

Check the rule classes shipped in src/Rules/ — they accept scoping arguments so dependent fields stay consistent.

Eloquent casts for normalized storage. The package's casts let you store a geoname id on your own model and hydrate the full read-only record on access. This keeps your schema lean while giving you $user->city->name, ->admin1, ->country without extra joins. The models are intentionally read-only — persist your own foreign keys, not the GeoNames rows themselves.

Proximity features. nearest() is fine for store locators, "events near me", and shipping-zone checks at low volume. For high-cardinality lookups, pre-compute distances into your own table; the package does not (and should not) own your spatial index.

Seeding strategy. For local dev and tests, run the seeder once and commit the resulting SQLite file, or run it in a CI setup step. For production, fetch + seed a single canonical dataset, and refresh on a schedule that matches the upstream snapshot cadence rather than re-downloading per deploy.

Integration with models. Add a country_code / geoname_id column to your domain tables and use the casts. Keep the package's read-only models behind a small application-level service if you want to swap providers later — the public API surface is stable but the underlying source (GeoNames CCN BY 4.0) may occasionally disagree with your business rules.

Gotchas and Tips

  • This is a breaking major. Downloaders, converters (including MongoDB), --feature-class, farzai/transport, symfony/http-client, symfony/console, psr/log, and ext-zip are all gone. If you depended on any of those — especially the MongoDB converter or the old downloader API — you must build an adapter yourself. UPGRADING.md is the only migration guide; there's no compatibility shim.
  • PHP 8.3+ is required. Don't try to deploy v3 to a fleet still on 8.1/8.2; composer will resolve but runtime behavior around typed constants and readonly classes assumes 8.3.
  • Fetch is explicit for a reason. v3 promises "no network at runtime" — which is a security and reliability win, but it means a fresh environment with no snapshots fails fast (and, ideally, loudly). Add a health check or boot-time assertion that the expected snapshots exist before serving traffic.
  • Snapshot pinning and sha256 hashes. Integrity is verified on fetch. If a fetch fails verification, the local snapshot is (or should be) left untouched — inspect the log and the target directory rather than re-running blindly. A corrupted half-written snapshot is the most likely cause of odd query results.
  • Config path mismatches bite. config/geonames.php controls both where fetches write and where queries read. If you move storage (e.g. to storage/app/geonames on a read-only filesystem), update both the config and your deploy step's write permissions. Don't assume storage_path() is writable on Lambda-style containers.
  • Seeder portability is tested, but check your version. SQLite, Postgres 16, MySQL 8.4, MariaDB 10.2/11.8 are exercised. If you're on older Postgres or MySQL 5.7, run the seeder in a staging environment first — large insert batches can hit max_allowed_packet or parameter limits.
  • Read-only models are read-only for a reason. Don't save() them; changes will either be ignored or throw. Persist references in your own tables and join on geoname_id or country codes.
  • Debugging "no results". The three usual causes, in order: (1) the snapshot for that country wasn't fetched, (2) the config path differs from the fetch target, (3) you're querying with a country code that isn't in the snapshot (check Geonames::country($code) returns non-null before chaining).
  • Attribution is a license obligation. GeoNames data is CC BY 4.0 — surface attribution in your UI or docs. See the README for the exact wording; don't strip it.
  • Extension points are narrower than 2.x. No transport or converter layer to hook into. If you need custom datasets or alternative backends, wrap the package behind your own repository interface rather than trying to extend internals — the 3.x internals are snapshot-shaped, not plugin-shaped.
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.
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle