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

Tomato Locations Module Laravel Package

tomatophp/tomato-locations-module

Tomato Locations is a ready-to-use module for TomatoPHP that adds full location management to your Laravel admin panel, letting you manage countries, cities, areas, and related data with a clean Filament interface, migrations, and seeders out of the box.

View on GitHub
Deep Wiki
Context7

Getting Started

The tomatophp/tomato-locations-module is a TomatoPHP admin module that gives you a ready-made geographic hierarchy (country → city → area) with Filament resources, migrations, and a seeded dataset. It is designed to sit on top of tomato-admin/Filament, so the first thing to confirm is that your app already has a Filament admin panel wired up — otherwise you will install it and see no UI.

Minimal setup:

composer require tomatophp/tomato-locations-module
php artisan vendor:publish --provider="TomatoPHP\TomatoLocations\TomatoLocationsServiceProvider"
php artisan migrate
php artisan tomato-locations:install   # publishes config + seeds countries/cities

Look first at:

  1. config/tomato-locations.php — toggle which entities are exposed, API flags, and table/route prefixes.
  2. database/migrations/*_create_*_tables.php — understand the countries, cities, and areas schema before you attach foreign keys from your own models.
  3. The Filament resources under src/Filament/ (or src/Resources/ on older Filament versions) — this is where the CRUD behavior lives, and where you will subclass or override if you need custom fields.

Your first realistic use case is attaching an address to an existing model (e.g. an Order or Store). Once migrations are run, you get country_id, city_id, and area_id foreign keys you can reuse:

Schema::table('stores', function (Blueprint $table) {
    $table->foreignId('country_id')->nullable()->constrained();
    $table->foreignId('city_id')->nullable()->constrained();
    $table->foreignId('area_id')->nullable()->constrained();
});

Implementation Patterns

Cascade select in Filament forms. The module ships with dependent selects (country → city → area). Reuse the same pattern in your own resources rather than rebuilding it:

Select::make('country_id')
    ->options(Country::pluck('name', 'id'))
    ->reactive()
    ->afterStateUpdated(fn ($set) => $set('city_id', null));

Select::make('city_id')
    ->options(fn ($get) => City::where('country_id', $get('country_id'))->pluck('name', 'id'))
    ->reactive()
    ->afterStateUpdated(fn ($set) => $set('area_id', null));

Select::make('area_id')
    ->options(fn ($get) => Area::where('city_id', $get('city_id'))->pluck('name', 'id'));

API consumption. If the module exposes read-only endpoints (check config('tomato-locations.api')), you can serve the taxonomy to a frontend/mobile app without standing up your own controller. Typical usage: load countries once, lazily fetch cities/areas on selection using the parent id. This keeps payloads small and avoids shipping thousands of rows to the client.

Seeding strategy. The packaged seeder loads a large geographic dataset. In multi-tenant or partially-populated apps, do not run the full seed on every deploy. Either seed once and disable in subsequent environments, or import a scoped subset (e.g. only countries your business operates in) via a custom command that calls the module's models directly.

Localization. Country/city names come from the seed data and are usually English-only. If you need multi-language labels, add a translation table keyed on the location id and resolve through an accessor rather than editing the package tables — this survives module upgrades.

Validation. For address entry, validate that the selected area_id actually belongs to the selected city_id (and city_id to country_id). A Rule::exists('areas', 'id')->where('city_id', $cityId) rule prevents mismatched combinations that the form's cascade normally prevents but API clients can bypass.

Gotchas and Tips

  • v1.0.0 has no documented breaking changes yet, but it is the first stable tag. Pin to ^1.0 and read the changelog link for patch releases — early releases of TomatoPHP modules frequently rename config keys and Filament resource namespaces.
  • The seeder is heavy. A full country/city/area seed can run into hundreds of thousands of rows. Run it with --force knowingly; on small VPS setups it can time out mid-run. Wrap it in a queued job or chunked command if you deploy to constrained hosting.
  • Table/route prefixes collide with existing apps. If you already have a countries or cities table, the migration will fail. Publish the migrations, rename the tables via config, or guard with Schema::hasTable before running.
  • Filament version mismatch is the most common install failure. Tomato modules are version-locked to specific Filament majors. Check composer.json constraints against your installed Filament before composer require; a mismatch typically manifests as resource classes silently not registering, not a hard error.
  • The tomato-locations:install command is idempotent-ish but not fully. Re-running it re-seeds. If you have edited seed data manually, back it up or run migrations only.
  • Debugging "empty dropdowns": ninety-percent of the time the issue is the seeder never ran (countries is empty), or the reactive city_id select is not re-evaluated because the parent field lacks ->reactive(). Check php artisan tinker → Country::count() first.
  • Extension points: extend the base models (Country, City, Area) with your own subclasses in App\Models, then swap the bindings in a service provider — this lets you add relationships, scopes, or casts without forking the package. For UI changes, override the Filament resource rather than editing vendor files.
  • API caching: responses for countries/cities are effectively static. Cache them (Cache::remember) rather than hitting the DB on every form load — the taxonomies change rarely and the queries are simple but frequent.
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.
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums
rlacerda83/query-parser
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
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