Describe the bug
Saving a shipping zone whose type is "Limit to Countries" always fails validation with:
The selected countries is invalid.
This happens for every newly created zone and whenever you add a country that isn't already persisted on the record. It makes the "Limit to Countries" zone type effectively unusable.
Steps to reproduce
- Register the
ShippingPlugin and open Shipping → Zones in the Lunar admin panel.
- Create/edit a shipping zone and set Type to "Limit to Countries".
- Select one or more countries.
- Save.
Expected behaviour
The zone saves and the selected countries are synced to the country_shipping_zone pivot.
Actual behaviour
Validation fails on the countries field with "The selected countries is invalid." (Laravel's in rule message), so the form never saves.
Root cause
In src/Filament/Resources/ShippingZoneResource.php, getCountriesFormComponent() declares both ->options(...) and a ->getOptionLabelsUsing(...) callback:
Forms\Components\Select::make('countries')
->options(Country::get()->pluck('name', 'id'))
->multiple()
->required()
// ...
->getOptionLabelsUsing(static function (Model $record): array {
$record->loadMissing('countries');
return $record->countries
->pluck('name', 'id')
->toArray();
})
Filament builds the field's in validation rule from getInValidationRuleValues() (Filament\Forms\Components\Select), which — when getOptionLabelsUsing is set — derives the valid values from getOptionLabels(withDefaults: false), i.e. from that callback rather than from options().
Because the callback returns only $record->countries (the countries already persisted on the record), any selected country that isn't already saved is not in the allow-list:
if (count(array_diff($state, array_keys($optionLabels)))) {
return []; // -> Rule::in([]) -> always fails
}
getInValidationRule() then turns that empty array into Rule::in([]), which rejects every value. For a new zone $record->countries is empty, so it fails for all selections.
The same pattern is present on the singular country field via getOptionLabelsUsing, but that field only renders for the states/postcodes types and single-select validation uses getOptionLabelUsing (singular, unset), so it isn't affected.
Suggested fix
The static ->options(Country::get()->pluck('name', 'id')) already provides labels for every country, so the getOptionLabelsUsing callback on the countries field is redundant for display and harmful for validation. Either remove it, or have it resolve labels for the selected values rather than the persisted relationship, e.g.:
->getOptionLabelsUsing(fn (array $values): array =>
Country::query()->whereKey($values)->pluck('name', 'id')->all()
)
Environment
- lunarphp/table-rate-shipping: 1.5.0-beta.6
- lunarphp/lunar + lunarphp/core: 1.5.0-beta.6
- filament/filament: v4.11.7
- Laravel: 12.x / 13.x
- PHP: 8.4
Describe the bug
Saving a shipping zone whose type is "Limit to Countries" always fails validation with:
This happens for every newly created zone and whenever you add a country that isn't already persisted on the record. It makes the "Limit to Countries" zone type effectively unusable.
Steps to reproduce
ShippingPluginand open Shipping → Zones in the Lunar admin panel.Expected behaviour
The zone saves and the selected countries are synced to the
country_shipping_zonepivot.Actual behaviour
Validation fails on the
countriesfield with "The selected countries is invalid." (Laravel'sinrule message), so the form never saves.Root cause
In
src/Filament/Resources/ShippingZoneResource.php,getCountriesFormComponent()declares both->options(...)and a->getOptionLabelsUsing(...)callback:Filament builds the field's
invalidation rule fromgetInValidationRuleValues()(Filament\Forms\Components\Select), which — whengetOptionLabelsUsingis set — derives the valid values fromgetOptionLabels(withDefaults: false), i.e. from that callback rather than fromoptions().Because the callback returns only
$record->countries(the countries already persisted on the record), any selected country that isn't already saved is not in the allow-list:getInValidationRule()then turns that empty array intoRule::in([]), which rejects every value. For a new zone$record->countriesis empty, so it fails for all selections.The same pattern is present on the singular
countryfield viagetOptionLabelsUsing, but that field only renders for thestates/postcodestypes and single-select validation usesgetOptionLabelUsing(singular, unset), so it isn't affected.Suggested fix
The static
->options(Country::get()->pluck('name', 'id'))already provides labels for every country, so thegetOptionLabelsUsingcallback on thecountriesfield is redundant for display and harmful for validation. Either remove it, or have it resolve labels for the selected values rather than the persisted relationship, e.g.:Environment