Validation in Extbase 

Extbase validates incoming request arguments automatically before your action method is called. If validation fails, the framework calls errorAction() instead of the intended action. Use #[IgnoreValidation] on a parameter to let an action receive an object even if it is invalid — for example to redisplay a form with its errors.

Validators can be Extbase built-ins, custom classes, or — since TYPO3 v14 — Symfony constraints.

Where validation fits into the Extbase request lifecycle 

The sequence for every request that carries arguments is:

  1. Property mapping — everything arriving from the request is a string or an array of strings. Property mapping converts these into typed PHP values and objects (see Extbase property mapper).
  2. Validation — the mapped values are checked against any #[Validate] attributes declared on the action parameter, on the domain model property, or both. Property validators run first; action parameter validators run afterwards. Either alone is sufficient — both can be combined on the same type.
  3. Action dispatch — if validation passes, the action method is called with the resolved arguments.
  4. Error handling — if validation fails, errorAction() is called instead. The default implementation redirects the user to the referring request (typically the action that rendered the form), carrying the validation errors so they can be displayed.

This means you declare what valid data looks like; Extbase decides when to run the checks and where to route the request on failure.

Where to declare validators 

Validators can be declared on action parameters, on domain model properties, or both.

On an action parameter — validates the value passed to an action before the action runs:

EXT:my_extension/Classes/Controller/ConferenceController.php
use TYPO3\CMS\Extbase\Attribute\Validate;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;

class ConferenceController extends ActionController
{
    public function createAction(
        #[Validate('NotEmpty')]
        #[Validate('StringLength', options: ['maximum' => 255])]
        string $title,
    ): ResponseInterface {
        // $title is guaranteed non-empty and at most 255 characters
    }
}
Copied!

On a domain model property — validates the property every time the model is used as an action argument:

EXT:my_extension/Classes/Domain/Model/Conference.php
use TYPO3\CMS\Extbase\Attribute\Validate;
use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;

class Conference extends AbstractEntity
{
    #[Validate('NotEmpty')]
    #[Validate('StringLength', options: ['maximum' => 255])]
    protected string $title = '';
}
Copied!

Placing validators on the model above means that every action that receives a Conference argument benefits from the same rules, without having to repeat the attribute on every parameter.

When a form submission fails validation, Extbase re-calls the originating action (typically newAction() or editAction()). If that action has the model as a typed parameter and declares #[IgnoreValidation] on it, the Form ViewHelper <f:form> view helper can read the submitted values from the object and Form.validationResults ViewHelper <f:form.validationResults> can display the errors inline — the object does not need to be valid for this to work.

Ignoring the validation result with #[IgnoreValidation] 

Sometimes you need to receive an object without running its validators, for example when displaying a "new" form that is pre-populated from a submitted but invalid object, or when an action intentionally accepts a partially filled model.

Place #[IgnoreValidation] on the parameter to tell Extbase to ignore the validation result for that argument and dispatch the action regardless:

EXT:my_extension/Classes/Controller/ConferenceController.php
use TYPO3\CMS\Extbase\Attribute\IgnoreValidation;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;

class ConferenceController extends ActionController
{
    public function newAction(
        #[IgnoreValidation]
        Conference $conference = null,
    ): ResponseInterface {
        $this->view->assign('conference', $conference ?? new Conference());
        return $this->htmlResponse();
    }
}
Copied!

Validation still runs — #[IgnoreValidation] does not skip it. It instructs Extbase to call the action even when the argument is invalid. This is what allows newAction() to receive a partially filled Conference back from a failed createAction() and hand it to the template so f:form can redisplay the submitted values with inline errors.

Without #[IgnoreValidation], the framework would see the invalid Conference, call errorAction(), which redirects back to newAction(), which would validate again — an infinite cycle.

Customising errorAction() 

The built-in errorAction() adds a generic flash message and redirects the user to the referring request. For most contact or registration forms this is sufficient.

To show a custom error flash message, override getErrorFlashMessage():

EXT:my_extension/Classes/Controller/ConferenceController.php
protected function getErrorFlashMessage(): bool|string
{
    return 'Please correct the errors in the form before saving.';
}
Copied!

Return false to suppress the flash message.

To completely change how validation errors are handled — for example to return a JSON response — override errorAction() itself:

EXT:my_extension/Classes/Controller/ConferenceController.php
use Psr\Http\Message\ResponseInterface;
use TYPO3\CMS\Extbase\Error\Result;

protected function errorAction(): ResponseInterface
{
    $errors = $this->arguments->validate();
    return $this->jsonResponse(json_encode([
        'errors' => $this->flattenErrors($errors),
    ]))->withStatus(422); // choose the status code appropriate for your API; 422 or 400 are common choices
}
Copied!

Displaying validation errors in Fluid templates 

Extbase makes validation errors available in Fluid via the f:form.validationResults view helper. Wrap form fields with it to show per-field error messages:

EXT:my_extension/Resources/Private/Templates/Conference/New.fluid.html
<f:form action="create" name="conference" object="{conference}">
    <f:form.validationResults for="conference.title">
        <f:for each="{validationResults.errors}" as="error">
            <p class="error">{error.message}</p>
        </f:for>
    </f:form.validationResults>
    <f:form.textfield property="title" />
    <f:form.submit value="Save" />
</f:form>
Copied!

The for attribute is the dot-notation path to the validated object or property. Leave it empty to access all errors in the current request.