Entity workflow¶
Small Forms is most useful as an explicit boundary between untrusted/external input and Entity Manager state.
Create an entity¶
Start with an entity constructed by its manager so relation metadata and the manager reference are valid:
$user = $userManager->newEntity();
$form = FormBuilder::createFromAttributes($user)
->fillFromArray($payload, $user);
The second fillFromArray() argument gives Small Forms the parent AbstractEntity when nested relation objects must be created.
Validate before hydration¶
use Small\Collection\Collection\StringCollection;
$messages = new StringCollection();
$form->validate($messages);
if ($messages->count() > 0) {
// Return/translate validation errors.
return;
}
By default validate() collects failures. Set the second argument to true to throw after collecting them:
The thrown exception is ValidationFailException; the caller-owned StringCollection still contains the field-level messages.
Hydrate and persist¶
Hydration writes matching form fields directly to reflected object properties, including non-public properties.
Core does not run this validation again during persistence. Database constraints and domain rules remain a separate integrity layer.
Input sources¶
The same form can consume several transport representations:
$form->fillFromArray($payload, $entity);
$form->fillFromJson($json);
$form->fillFromUrlEncodedString($body);
$form->fillFromObject($dto);
fillFromJson() first calls PHP json_validate() and throws InvalidInputDataException for invalid JSON.
Unknown keys in fillFromArray() are ignored:
Unknown-key ignoring is not authorization
Build a form containing only the fields writable by that operation. Do not rely on ignored keys as the security boundary for mass assignment.
Create and update contracts¶
A single entity can have different input contracts depending on the operation. Attribute metadata is convenient for the common contract; explicit forms are better when create/update permissions differ substantially.
$patchForm = FormBuilder::createInlineForm()
->addField('displayName', new StringType(), [new ValidateNotEmpty()])
->addField('enabled', new BooleanType());
Then hydrate only the allowed fields into the loaded entity.
Add rules or modifiers at runtime¶
$form->getField('email')
->addRule(new ValidateEmail(canBeNull: false))
->addModifier(new TrimModifier());
This is also how to use validators/modifiers that are not declared as PHP attributes.
Reusable form classes¶
For operation-specific contracts, derive from AbstractForm:
final class CreateUserForm extends AbstractForm
{
protected function build(): void
{
$this->addField(
'email',
new StringType(),
[new ValidateNotEmpty(), new ValidateEmail()],
[new TrimModifier()],
);
}
}
This avoids putting every application workflow rule on the persistent entity class.
Reading and serialization¶
$value = $form->getFieldValue('email'); // modifiers applied
$raw = $form->toArray(); // serialization of stored form state
toArray() has type-specific behavior for nested forms, dates and arrays. Review Behavior and caveats before using it as a generic normalized payload serializer.