anomaly/state-field_type
A field type for the Anomaly Streams platform that adds a state selector to your forms, letting users choose a US state from a predefined list and save the selection to your stream entries.
anomaly/state-field_type is a micro-addon for the Streams Platform (PyroCMS 3.x): a single field type that renders a dropdown of US states.
composer require anomaly/state-field_type
php artisan addon:install anomaly.field_type.state
If addon:install can't resolve the slug, check addon.json — the slug usually mirrors the composer name (anomaly.field_type.state) and the namespace follows the same convention.
Look here first, in this order:
addon.json — the type (field_type) and any require block telling you the Streams version it was built against. This is the single best signal of whether it will work in your install.src/StateFieldType.php — the whole behaviour of the package is essentially $name, $slug, $inputView, getOptions() and getConfig(). Read all of it; it's short.resources/views/input.twig — confirms whether it renders a plain <select> or wraps a Streams select partial (affects how you style and how it behaves with required).resources/config/config.php and getConfig() — tells you which per-field options (default state, placeholder, "top options") actually exist.First use case: add a state field to an existing address/contact stream in the admin (/admin/fields/create), pick the "State" type, assign it to the stream, then confirm the value round-trips on the entry form before writing any code against it. Do this on a scratch stream — with zero dependents and no maintenance signal, you want a failure to be cheap and obvious.
Declaring the field in a migration (preferred over admin clicks, so the field is reproducible):
// streams/addons/{vendor}/{addon}/migrations/2024_01_01_120000_create_addresses_stream.php
public function up()
{
$this->stream->create(['slug' => 'addresses', 'name' => 'Addresses']);
$this->stream->addField('state', [
'type' => 'anomaly.field_type.state',
'config' => [
'required' => true,
// confirm keys against getConfig() in the source
],
]);
}
Form builder (when you're building forms in PHP rather than stream-assigned fields):
$builder->add('state', 'anomaly.field_type.state', [
'label' => 'State / Province',
'required' => true,
]);
Reading the value. The entry attribute returns the stored scalar; the field type object is reached via field():
$entry->state; // "CA" (or "California" — depends on the addon's storage format)
$entry->field('state')->getValue(); // same value, explicit
{{ entry.state }} // Twig
Validate against the same list the dropdown offers, or you'll get values in the DB that the UI can never produce:
$states = $entry->field('state')->getOptions(); // usually abbreviation => name
$request->validate([
'state' => ['required', Rule::in(array_keys($states))],
]);
This is the pattern worth standardising on: one field type instance, one option list, used for the input, the request rule, and any API serialisation. If getOptions() is public (it almost certainly is), you can inject the field type or a small StateOptions wrapper rather than duplicating a states array.
Table/listing columns are free — the field type's outputView handles display in $builder->column('state'). Override the output view only if you need a badge or a full-name label instead of the stored abbreviation.
Reuse outside Streams (an API resource, a CSV import) is the one place this package stops paying for itself. The dropdown is the only thing it adds; if you need the list in a console command, import a static data set rather than booting a field type.
anomaly.field_type.select with the 50 states as options config gives you 95% of the value with zero third-party risk. Keep that as your plan B, not your first move.CA) vs full name (California) is decided by getOptions() / the input view's value attribute. Check it before anyone enters data; switching later is a data migration, not a config change.string(2) from an earlier abbreviation-based migration.select with a chained/conditional option set — don't try to bend this one.php artisan addon:install anomaly.field_type.state, then php artisan view:clear and php artisan cache:clear. If the type doesn't appear in the field-type dropdown, the addon isn't registered — check addon.json's namespace matches the directory under streams/addons/.php artisan addon:publish anomaly.field_type.state, then edit the published Twig. Same for config.php. Anything you patch in vendor/ is lost on the next composer update.StateFieldType, override getOptions() and $inputView, register it in your own addon, and switch the field's type to yours. That keeps the migration history intact.getRules(). If the dropdown has a placeholder like -- Select -- and the field isn't required, you'll store an empty string rather than null — normalise on save if downstream code uses whereNotNull.How can I help you explore Laravel packages today?