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 Plugin Laravel Package

rainlab/location-plugin

RainLab Location is an October CMS plugin for managing countries and states, with a ready-to-use location database, an address form widget, and backend settings for adding or editing country and state records in your site.

Deep Wiki
Context7

Getting Started

This is not a Laravel package — it's an October CMS plugin (rainlab/location-plugin) that ships with two models, two database tables, and a few form widgets. Composer-installing it into a stock Laravel app won't do anything useful. Skip this assessment if you're not on October CMS.

Install path (October only):

# From the project root, via marketplace
php artisan plugin:install RainLab.Location

# Or drop the folder and migrate + seed
git clone https://github.com/rainlab/location-plugin plugins/rainlab/location
php artisan october:migrate

Where to look first:

  1. models/Country.php and models/State.php — the entire public surface is here (listCountries, getDefault, scopeIsEnabled, formatAddress).
  2. updates/ — migrations + seeder for ~250 countries and thousands of states (US, CA, AU, IN, etc.). This is why the plugin exists; nobody wants to seed ISO 3166 by hand.
  3. formwidgets/Country.php and formwidgets/State.php — the backend field types you'll reference in fields.yaml.
  4. Settings → Location tab — default country, default state, address format template.

First use case — a backend address form on your own model:

# models/customer/fields.yaml
fields:
    country:
        label: Country
        type: dropdown
        # or use the plugin's own widget for the full ISO list + default handling
    state:
        label: State
        type: dropdown
        dependsOn: country

Then hydrate in the controller:

public function formExtendFields($widget) {
    $widget->getField('country')->options = \RainLab\Location\Models\Country::listCountries();
}

Or reference the built-in form widget directly (type: countrySelect / type: stateSelect) to get dependent state loading for free.

Implementation Patterns

Pattern 1 — Dependent country/state dropdowns in a backend form. The plugin's stateSelect widget reads the parent country field and AJAXes state options. Reference it by alias:

country:
    label: Country
    type: countrySelect
    default: 'US'
state:
    label: State/Province
    type: stateSelect
    dependsOn: country
    span: right

Pattern 2 — Frontend partials for public forms. The plugin ships partials you can extend. Typical frontend implementation uses onChange handlers:

{{ form_select('country', Country::listCountries(), input('country')) }}
<select name="state" id="state"
        data-request="onLoadStates"
        data-request-data="country: '#country'">
</select>
// In a CMS component
public function onLoadStates() {
    $countryId = post('country');
    return ['#state' => $this->renderPartial('@_states', [
        'states' => \RainLab\Location\Models\State::listStates($countryId),
    ])];
}

Pattern 3 — Store the ISO code, not the row id. Most integrations (including RainLab.User) key off the two-letter code. Country::whereCode('US')->first() is the lookup idiom. Joining to rainlab_location_countries.code is stable across reseeds; joining on id is not guaranteed by anything in the schema.

Pattern 4 — Formatted address rendering. Set the template in Settings → Location → Address Format (e.g. {{ street }}\n{{ city }}, {{ state }} {{ zip }}), then render with the plugin helper so it respects the admin setting:

echo \RainLab\Location\Models\Country::formatAddress($model->toArray());

This is the single most valuable reason to install the plugin if you support multiple regions — the format string lives in admin, not in code.

Pattern 5 — Enable/disable countries per storefront. Use Country::where('is_enabled', true)->get() (or the built-in scopeIsEnabled) and drive your dropdowns from that, so a merchandising decision (hide shipping to X) doesn't need a deploy.

Gotchas and Tips

  • Seed data is the product; the code is thin. There are no helpers for geocoding, distance, timezone, or currency. If you need those, you'll add them — the plugin is a vocabulary and a table, not a geo toolkit.
  • The code column semantics differ. Countries use ISO 3166-1 alpha-2 (US, GB). States use a mix of ISO 3166-2 subdivision codes, postal abbreviations, or nothing at all depending on the country. Don't assume state.code == 'CA' means California everywhere; validate against country_id.
  • is_enabled and is_default are separate flags. Setting a default country that is later disabled leaves you with no default. Country::getDefault() returns null in that case — always null-check before using it to prefill a form.
  • formatAddress() needs the full attribute set. It silently drops placeholders whose keys are missing from the array. If your address renders as , , you're passing state_id where the template expects state (a name), or vice versa. Inspect the format string in Settings first.
  • Migrations re-seed on october:fresh, not on october:migrate. In production, updating the plugin can introduce new countries/states but won't remove old ones — plan for orphans when you switch lookups to code-based.
  • October version drift. This plugin tracks October 3.x. On October 2.x you may need a pinned older tag; the form-widget API changed (type: aliases, dependsOn semantics). Check version.yaml against your core.
  • Not autoloaded in vanilla Laravel. If you found this while searching Packagist for a location package, stop — use grimzy/laravel-mysql-spatial or symfony/intl (which gives you Countries::getName('US') and States for free with zero October coupling).
  • Extension point: every table and model is a standard October model, so you can extend with Country::extend(function($model){ $model->addDynamicProperty('shipping_zone'); }) in a boot() method in your own plugin rather than forking. Forking makes upstream october:migrate upgrades painful.
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