Behavior and caveats¶
This page records implementation details that matter when Small Forms 2.3.0 is used as an Entity Manager input layer.
Validation sees stored values¶
Field::validate() validates the field's stored value. Field modifiers are not applied first.
That means validation order is conceptually:
- input is stored;
- validation rules inspect the stored value;
- modifiers are applied later when
getFieldValue()/hydrate()reads it.
Type-provided validators are designed around that flow. For custom rules/modifiers, do not assume normalization happened before validation unless your form explicitly stores normalized input.
Modifiers are read-time transformations¶
Field::getValue() copies the stored value into a local variable and applies modifiers to that copy. The underlying stored form value remains unchanged.
This makes repeated reads deterministic for pure modifiers and keeps the transport representation available internally, but it also means toArray() is not equivalent to “normalized form data”.
toArray() is type-specific¶
Current behavior:
nullstaysnull;- nested
AbstractFormvalues recurse throughtoArray(); - date/time fields call
format()on the stored date object; ArrayTypeserializes each item by calling itstoArray()method;- other fields return the stored value as-is.
Consequently, toArray() is most reliable for nested form/object arrays. For scalar ArrayType fields, prefer getFieldValue() or your own payload mapping if you need a normalized scalar array.
Date values¶
fillFromArray() has a direct conversion branch for DateTimeType. DateTimeImmutableType is converted through its field modifier when the value is read/hydrated.
When using toArray() on a date field, ensure the stored value is already a date object because serialization calls format() directly.
Unknown properties¶
Unknown input keys are ignored. Unknown/unresolvable object property types are also skipped when FormBuilder::createFromAdapter() catches UnknownTypeException.
For ORM entities, explicit Small Forms type attributes make the form contract visible and avoid accidental omission.
Attribute support is class-specific¶
Implementing ValidationRuleInterface or ModifierInterface is not enough to make a class legal PHP attribute syntax. The class must also carry PHP's #[Attribute] declaration.
All built-in validators and modifiers carry this metadata and support property targets. Custom implementations intended for attribute-driven forms must declare #[Attribute] themselves.
Entity construction¶
Never instantiate an Entity Manager AbstractEntity directly for form hydration; its constructor requires a manager. Create the root with its manager and pass it to fillFromArray() when relation input is involved:
$entity = $manager->newEntity();
$form = FormBuilder::createFromAttributes($entity)
->fillFromArray($payload, $entity);
Small Forms can then use relation-manager factories for nested entity creation.
Persistence is separate¶
A successful form validation is not a substitute for:
- database constraints;
- transaction boundaries;
- authorization;
- concurrency/race handling;
- Entity Manager lifecycle hooks;
- application/domain invariants that require authoritative state.
Validate the transport contract first, hydrate only authorized fields, then persist inside the appropriate Entity Manager transaction/persistence workflow.