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

World Laravel Package

thecoder/world

Laravel package providing ready-to-use world data—countries, states, cities, currencies, languages, and timezones—with migrations, models, and helpers for building location-aware applications quickly.

View on GitHub
Deep Wiki
Context7

Getting Started

  1. Confirm the package identity before writing code. The repository is mbpcoder/world (maintainer @MBP165), but the vendor name in your brief is thecoder/world. Check the name field in the repo's composer.json (or the Packagist page) and install that exact string:

    composer require thecoder/world
    

    Also check the require block for the supported Laravel/PHP range. The v2.5.x line has moved from v2.1.0 to v2.5.1 in a short window — read the changelog between those tags, not just the latest release notes.

  2. Read these three things first, in order:

    • README.md — install/publish/seed commands, if documented.
    • composer.json — psr-4 namespace and service provider class (you'll need both for vendor:publish).
    • database/ and src/Models/ — the actual table names, columns, and relationships. Treat the source as the spec; small packages often have thin documentation.
  3. Check your DB driver against the release notes. v2.5.1 is largely a SQLite correctness release (migration fix, tableName property usage, SQLite test coverage). If you develop or run CI on SQLite, pin >=2.5.1 — earlier 2.x migrations were broken under SQLite. If you only run MySQL/Postgres, this is low-risk but still the cheapest upgrade you'll make.

  4. Publish and migrate:

    php artisan vendor:publish --provider="TheCoder\World\WorldServiceProvider"
    php artisan migrate
    

    If no provider is auto-discovered, add it to bootstrap/providers.php (Laravel 11+) or config/app.php.

  5. Seed the reference data. Expect a dedicated seeder or artisan command for countries/states/cities. Run it explicitly rather than relying on DatabaseSeeder, so production and local environments are handled deliberately.

  6. First use case: a cascading country → state → city address form, plus normalized foreign keys (country_id, state_id, city_id) on your users, orders, or addresses tables instead of free-text location strings.

Implementation Patterns

Store IDs, not names. Add nullable foreign keys to your domain tables and join to the package tables for display:

Schema::table('addresses', function (Blueprint $table) {
    $table->foreignId('country_id')->nullable()->constrained('countries')->nullOnDelete();
    $table->foreignId('state_id')->nullable()->constrained('states')->nullOnDelete();
    $table->foreignId('city_id')->nullable()->constrained('cities')->nullOnDelete();
});

Verify the actual table names from the migrations first — v2.5.1 explicitly moved table naming onto a tableName property in the locations migration, which is a strong hint names are meant to be overridable (possibly prefixed, e.g. world_countries). Resolve them from config at runtime rather than hardcoding the constraint target if you can.

Wrap the package models behind your own layer. Don't scatter TheCoder\World\Models\City through controllers and Blade. A thin repository or query service gives you one place to swap in a different dataset later, which matters for a low-traffic package:

class LocationRepository
{
    public function countries(): Collection
    {
        return Cache::rememberForever('world.countries.v1', fn () =>
            Country::orderBy('name')->get(['id', 'name', 'iso2'])
        );
    }

    public function statesFor(int $countryId): Collection
    {
        return Cache::rememberForever("world.states.{$countryId}", fn () =>
            State::where('country_id', $countryId)->orderBy('name')->get(['id', 'name'])
        ));
    }
}

Countries and states are effectively static — cache them aggressively. Cities are large enough that you should cache per-state rather than globally.

Build JSON endpoints for dependent selects. Controllers return {id, name} pairs; Alpine.js/Livewire or a small fetch handler swaps the options. Keep the payload minimal (no full rows) since states/cities can be thousands of records.

Route::get('/locations/states/{country}', fn (Country $country) =>
    StateResource::collection($country->states()->orderBy('name')->get())
);

Validate the hierarchy, not just existence. exists:cities,id alone lets a user submit a city from the wrong state. Use a rule or withValidator closure that checks city->state_id === $request->state_id and state->country_id === $request->country_id.

Seed idempotently and out-of-band. If the package seeder uses plain insert(), re-running it will duplicate rows. Wrap it in your own command that truncates/upserts, and run it as a deploy step or a queued job — not inside a request or a test suite.

Normalize for search. If you add a search box, index a lowercase ASCII column (Str::ascii($name)) so "São Paulo" and "Sao Paulo" both match; LIKE '%term%' on the raw name will frustrate users.

Map to ISO codes at the boundary. For shipping, tax, or payment providers that expect US/BR rather than an internal integer, store or look up iso2/iso3 on the country row and convert once in an adapter, not in every call site.

Gotchas and Tips

  • Vendor/namespace mismatch is the first trap. Confirm the Composer name and the PSR-4 namespace from composer.json rather than guessing from the repo URL; the provider string in vendor:publish must match exactly. Nothing in the v2.5.1 notes resolves this ambiguity.
  • Pin >=2.5.1 and read the range between v2.1.0 and v2.5.1. The release notes only cover three PRs, but the tag jump implies four minor releases of unreviewed change. composer update in a locked project is the safe path.
  • SQLite was broken before v2.5.1. If your test suite runs on the default-in-memory SQLite connection, older migrations failed. Upgrade rather than patching vendor migrations by hand — the fix is upstream and the tests to prove it now ship with the package.
  • City seeding is the real cost. A global city dataset is typically 100k+ rows. Expect slow imports, high memory, and possible max_execution_time or packet-size failures. Chunk inserts, disable FK checks during the load (SET FOREIGN_KEY_CHECKS=0 on MySQL), and seed only the countries you actually serve in non-production environments.
  • Migration collisions. If you already have countries/states/cities tables, publishing the package migrations will clash. The new tableName property is your escape hatch — override it before installing rather than editing vendor files afterward. Check timestamps too; consider publishing with --tag selectively.
  • Test suite bloat. Seeding the full dataset in RefreshDatabase will make tests crawl. Seed a minimal fixture (two countries, a few states/cities) via factories or a --class flag, and assert against those. The package's own tests are not a substitute for a lean fixture set.
  • Cache invalidation after re-seeding. If you rememberForever lists, a data refresh silently serves stale rows. Version your cache keys (world.countries.v2) or flush a tagged namespace as part of the seed command.
  • Config-first customization. Look for config/world.php — table names, model bindings, and connection may be overridable. If a model needs an accessor (e.g. full_name), prefer extending the package model and rebinding via config over editing vendor files.
  • Data provenance and licensing. Check whether the dataset is derived from GeoNames or similar and what attribution is required. Also check how current the data is — country/region boundaries change, and an unmaintained dataset drifts.
  • Maintenance risk is real, but slightly improved. The addition of "comprehensive unit tests" and a second-contributor-style PR flow is a mild positive signal. Still: ~24 stars, few dependents, single maintainer. Pin the version in composer.json, keep the data in your own tables (not just vendor migrations), and be prepared to fork or replace the seeder. Your abstraction layer is the insurance policy.
  • Debugging checklist: php artisan tinker → Country::count(); if zero, the seeder didn't run. If relationships return empty, verify the actual FK column names (country_id vs country_code) in the migrations — the README may not match. If you're on SQLite and migrate still fails, confirm you actually resolved >=2.5.1 and aren't holding a stale lock file.
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.
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin
skyraptor/chatter
edlugz/sasapay
abbasudo/artisan-route