anomaly/country-field_type
Country field type for the Anomaly Streams platform, adding a searchable country selector to Laravel-powered forms and entries. Store standardized country values for consistent display, filtering, and localization across CMS content, admin interfaces, and API-driven workflows.
This is not a general-purpose Laravel package — it is a PyroCMS 3 / Streams Platform field type addon. It only makes sense inside an application that already has anomaly/streams-platform installed (i.e. PyroCMS 3.x). If you are on plain Laravel, skip it and use symfony/intl's Countries::getNames() directly.
Minimal steps:
composer require anomaly/country-field_type
php artisan addon:install anomaly/country-field_type # or use CP → Addons → Install
php artisan addon:list # confirm it is registered
Then in the control panel: Content → Fields → Create, and pick the country field type from the type dropdown (slug is typically anomaly.field_type.country). Assign the field to a stream/namespace, or define it in an addon migration:
$this->fields->addField([
'name' => 'Country',
'slug' => 'country',
'type' => 'anomaly.field_type.country',
]);
Where to look first:
src/CountryFieldType.php — the getOptions() method tells you exactly where the country list comes from (hardcoded array, symfony/intl, or a separate countries package), and the form() method tells you which per-field settings exist (default_value, placeholder, possibly a countries allow-list).composer.json — dependency constraints and whether it pulls in symfony/intl or similar. This determines what you can call to turn a stored code back into a name.resources/lang/ — localized labels for the field settings.First real use case: a "Country" field on an address/contact stream, storing an ISO 3166-1 alpha-2 code (US, GB, DE) that you then render as a localized name at output time.
Store the code, render the name. The field type's value is the two-letter ISO code, which is locale-independent and safe to store, query, and export. Resolve the display name at render time:
use Symfony\Component\Intl\Countries;
Countries::getName($entry->country, app()->getLocale()); // "United States"
If the addon ships a presenter, $entry->country->label() or similar is worth checking first — read the presenter, don't guess.
Querying and filtering works on the raw code, which is the main reason to like this field type:
$entries->where('country', 'US')->get();
// or in a listing/table filter: options = ['US' => 'United States', ...]
Restricting the option set. For a region-specific site, either use the field's own settings (if a countries/allow-list setting exists in form()) or subclass the field type in your own addon, override getOptions(), and register it as your own slug. The latter is the cleaner extension point and survives upstream updates.
Seeding and imports. Always seed with codes, never names — names are locale-dependent and drift over time. A CSV import should validate against Countries::getNames()'s keys:
if (!array_key_exists($row['country'], Countries::getNames())) { /* reject */ }
Validation on save. The field type constrains the UI, but not API/import writes. Add a rule on the form request or a Streams validation rule:
'country' => ['required', 'string', Rules\In::class . ':' . implode(',', array_keys(Countries::getNames()))],
Grouping/aggregation. Group-by reports should map code → name in PHP after the query rather than storing the name; this keeps the DB schema stable if you switch list sources.
Multilingual sites. The stored code is the same across locales, so you get translated country names for free if your list source is symfony/intl. Do not create separate per-locale fields for country.
composer require drops the files in addons/, but the field type will not appear in the Fields type dropdown until addon:install has run (it registers the binding and runs the addon's migrations). If it is missing from the dropdown, run php artisan addon:reinstall anomaly/country-field_type and php artisan cache:clear.anomaly/country-field_type; the slug you use in migrations is anomaly.field_type.country. Getting this wrong in a migration produces a "field type not found" exception at seed time, not at boot.default_value only affects new entries. Changing it does not backfill existing records, and removing a country from the option list leaves previously stored codes orphaned — those records will render the raw code or an empty label depending on the presenter.'' rather than null when unset; empty() checks pass, but is_null() and whereNull() do not. Normalize at the boundary if your code depends on it.ksort() sorts by code (AT, BE, DE…) which is not alphabetical in the UI. If sort order matters, sort by value before returning options.composer.json and be prepared to vendor/fork it if a Laravel or PHP upgrade breaks it.composer.json constraints before upgrading Laravel. Field type addons of this vintage often have loose or absent laravel/framework constraints and may silently depend on symfony/intl being present transitively. If Countries::getName() throws a "class not found", the addon's list source is not Symfony Intl.php artisan tinker with the stream repository ($entries->where('country', 'US')->get()) plus the CP field editor; check the rendered input view if the select comes out empty.CountryFieldType in your own addon → a select field with hand-written options. Avoid editing addons/anomaly/country-field_type directly; it will be overwritten on the next addon:install or deploy.How can I help you explore Laravel packages today?