Repository navigation
Description
javaabu/meta (Javaabu/meta#3, #9) defines meta fields with stencil fields:
MetaFields::for(Post::class, [
TextInput::make('subtitle')->maxLength(255)->translatable(),
Checkbox::make('featured'),
TextInput::make('views')->integer(),
]);These fields then go into the model's own form (post-types' post form, for example) with their state nested under meta:
- input names
meta[subtitle] - state and validation keys
meta.subtitle - the value is read with
data_get($post, 'meta.subtitle') - it's saved through
fill(['meta' => [...]])
HasMeta takes care of the model side. What stencil is missing:
1. Nest a container's fields under a state path
Group::make()->statePath('meta')->schema(MetaFields::fields($post))The fields inside would get:
getStatePath()/getInputName()/getId():meta.subtitle/meta[subtitle]/meta-subtitle- validation rules and attributes keyed
meta.subtitle - hydration, old input,
dehydrate()andfillModel()(root attributemeta) all using the nested path
This is Filament's statePath() on layout components. Right now a field's path is fixed by make($name), and there's no public way to rename or re-path a field someone else created. A public Field::name() setter (or withName()) would also unblock this, but statePath() is the cleaner API. Conditions inside the group could keep taking absolute paths (visibleWhen('meta.featured', true)).
2. Translatable fields whose record isn't a translatable model
Translatable meta values are stored per locale by javaabu/meta, not as attributes of a Translatable model. So Translatable::supports($post, 'meta.subtitle') is false, and ->translatable() falls back to a plain field.
A hook would cover this: something on the form or record that says a state path is translatable. For example, a contract the record can implement:
interface ResolvesTranslatableFields
{
public function isTranslatableField(string $state_path): bool;
}Stencil would then expand that field with Translatable::inputs() and expand its rules itself (Translatable::rules() also checks supports()).
Nested paths also need bracketed input names: meta[subtitle_dv], not meta.subtitle_dv, because PHP turns dots in input names into underscores. The state and dehydrated keys would be meta.subtitle_dv (or meta.subtitle.dv; meta can take either, so pick whatever is natural for stencil).
3. Small public getters
TextInput::isInteger(), so meta can store->integer()inputs in its integer column. Right now onlyisNumeric()is public.Field::isDeclaredTranslatable(), meaning->translatable()was called (evaluated), whether or not translatable is installed. Meta uses it to store per-locale values.
Until these land, javaabu/meta keeps the definitions, storage, query scopes and types working, and labels its form integration blocked. I'll switch to these APIs as soon as they're on main.
Activity
n4ff4h commentedon Nov 5, 2026
Update from javaabu/meta: I've narrowed what meta needs from this issue.
Javaabu\Meta\Support\Translatable\TranslatableMetaRecord), so->translatable()works with stencil as it is today. To find out whether a field was declared translatable, meta asks a copy of it in a probe form. A publicisDeclaredTranslatable()would still be nicer, but it's optional.TextInput::isInteger()): still wanted. Without it,TextInput::make('views')->integer()is stored as a float unless the app adds->metaType('integer'). Meta already callsisInteger()when the method exists.statePath()on containers): nice to have. Without it, meta fields render in their ownMetaForm(or->embedded()next to the record's form), with input names that aren't prefixed. With it,MetaFieldscould also return fields nested undermeta.for the record's own form.Nothing in meta is blocked on this issue now.
n4ff4h commentedon Nov 5, 2026
Done in 3e3ab27 (CI green on 8881bfc).
statePath()on any component nests the fields inside it.Group::make()->statePath('meta')->schema([...])gives:getStatePath()→meta.subtitle,getInputName()→meta[subtitle],getId()→meta-subtitle, and the node keymeta.subtitle;data_get($record, 'meta.subtitle')and from old input;meta.subtitle;validate()/dehydrate()→['meta' => [...]], andfillModel()fills the root attributemeta.Paths nest (
meta.seo.title). Look fields up by their full path ($form->getField('meta.subtitle'));getName()is stillsubtitle. On a field,->statePath('x')replaces the path its name gives. Conditions take absolute paths (visibleWhen('meta.featured', true)).Javaabu\Stencil\Contracts\ResolvesTranslatableFields(isTranslatableField(string $state_path): bool). It works on the record or on a form class, and is asked instead ofTranslatable::supports()(translatable must still be installed and enabled). Then:Translatable::inputs($state_path, $record).nullable.data_get($record, 'meta.subtitle_dv').meta.subtitle_dv), and input names are bracketed (meta[subtitle_dv]).name(bracketed) andstatePath(dot).TextInput::isInteger()andField::isDeclaredTranslatable(), which is true when->translatable()was called and its condition holds, whether or not the field expands.Docs: layout.md#nesting-state and translatable.md#records-that-arent-translatable-models.
Use stencil's isInteger(), isDeclaredTranslatable() and statePath()