Skip to content

Entity attributes and type resolution

Small Forms can derive an inline form from an object:

use Small\Forms\Form\FormBuilder;

$form = FormBuilder::createFromAttributes($entity);

Internally this creates an AnnotationAdapter and inspects the object's properties.

Explicit form metadata on an entity

A property can carry ORM metadata and Small Forms metadata side by side:

use Small\Forms\Form\Field\Type\StringType;
use Small\Forms\Modifier\EmptyStringToNullModifier;
use Small\Forms\ValidationRule\ValidateEmail;
use Small\SwooleEntityManager\Entity\Attribute\Field;
use Small\SwooleEntityManager\Entity\Enum\FieldValueType;

#[Field(type: FieldValueType::string)]
#[StringType]
#[EmptyStringToNullModifier]
#[ValidateEmail]
private ?string $email = null;

The explicit Small Forms type has priority over automatic type resolution.

Type-resolution order

AbstractObjectAdapter::getType() resolves each property in this order:

  1. the first property attribute whose object extends AbstractType;
  2. exact native bool, int, float, array or string types;
  3. the initialized property value when it is a Small Collection or array;
  4. the initialized property value when it implements DateTimeInterface;
  5. another initialized object value, mapped to SubFormType;
  6. otherwise UnknownTypeException.

FormBuilder::createFromAdapter() catches UnknownTypeException and skips that property.

Use explicit types on ORM entities

Nullable scalar types such as ?string stringify as ?string through reflection and therefore do not match the adapter's exact scalar cases. Nullable relations that currently contain null also cannot be inferred from their value. Put an explicit Small Forms type attribute on writable ORM properties instead of relying on inference.

For example:

#[Field(type: FieldValueType::string)]
#[StringType]
private ?string $title = null;

Without #[StringType], that nullable property can be omitted from the generated form.

Relation properties

Use explicit nested form types for relations:

#[ToOne(AddressManager::class, ['addressId' => 'id'])]
#[SubFormType(AddressEntity::class)]
private ?AddressEntity $address = null;

For arrays/nested lists:

#[ArrayType(new SubFormType(LineEntity::class))]
private array $lines = [];

Entity Manager to-many relations normally use EntityCollection; see Relations and nested forms before relying on automatic hydration of the stock collection.

Validation and modifier attributes

AnnotationAdapter collects property attributes implementing:

  • ValidationRuleInterface into the field's validation rules;
  • ModifierInterface into the field's modifiers.

All built-in Small Forms 2.3 validators and modifiers are declared as PHP attributes with property-target support. This includes the complete catalog of 75 validation rules and 38 modifiers documented in the validation-rule reference and modifier reference.

That means rules and transformations can be expressed directly on an ORM entity property:

use Small\Forms\Form\Field\Type\StringType;
use Small\Forms\Modifier\SubStrModifier;
use Small\Forms\Modifier\ToLowerModifier;
use Small\Forms\Modifier\TrimModifier;
use Small\Forms\ValidationRule\ValidateBeginWith;
use Small\Forms\ValidationRule\ValidateNumberCharsAtLeast;
use Small\SwooleEntityManager\Entity\Attribute\Field;
use Small\SwooleEntityManager\Entity\Enum\FieldValueType;

#[Field(type: FieldValueType::string)]
#[StringType]
#[TrimModifier]
#[ToLowerModifier]
#[SubStrModifier(0, 64)]
#[ValidateBeginWith('usr-')]
#[ValidateNumberCharsAtLeast(8)]
private string $externalId = '';

The same classes remain usable programmatically through addField(), addRule() and addModifier(). ValidateCallback is technically an attribute class too, but its Closure constructor argument makes normal programmatic construction the practical choice.

ORM attributes such as Field, PrimaryKey, ToOne and ToMany are ignored by the Small Forms rule/modifier collectors because they do not implement those interfaces.

The adapter also verifies that attribute classes exist. Attribute-driven forms therefore surface invalid/missing attribute classes early.

Symfony Validator dependency

The current AnnotationAdapter checks for Symfony\Component\Validator\Validation in its constructor. Install the package when using object/attribute forms:

composer require symfony/validator

Small Forms does not translate Symfony Assert\* constraints into Small Forms validators. The dependency is currently a runtime requirement of AnnotationAdapter, not an alternate validation engine.