Magento 2 Experten — Hyvä Theme, Tailwind CSS & SEO aus einer Hand ›

Formular-Validierung in Symfony

Formular-Validierung in Symfony

~15 Min. Lesezeit Zuletzt aktualisiert am 8. August 2026

Aktuell akzeptiert unser Projekt-Formular JEDEN Wert – auch einen leeren Namen. Symfonys Validator-Komponente löst das über deklarative Constraints, die wir GENAU dort platzieren, wo sie logisch hingehören: an der Datenklasse selbst.

Eine dedizierte Datenklasse statt Array

Bisher gab $form->getData() ein Array zurück – für Validierung brauchen wir eine ECHTE PHP-Klasse mit typisierten Eigenschaften, an die sich Constraints als Attribute anheften lassen:

src/Form/Data/ProjectData.php
<?php

declare(strict_types=1);

namespace App\Form\Data;

use Symfony\Component\Validator\Constraints as Assert;

class ProjectData
{
    #[Assert\NotBlank(message: 'Bitte geben Sie einen Projektnamen ein.')]
    #[Assert\Length(
        min: 3,
        max: 100,
        minMessage: 'Der Projektname muss mindestens {{ limit }} Zeichen lang sein.',
        maxMessage: 'Der Projektname darf höchstens {{ limit }} Zeichen lang sein.',
    )]
    public string $name = '';

    #[Assert\Length(max: 1000, maxMessage: 'Die Beschreibung darf höchstens {{ limit }} Zeichen lang sein.')]
    public ?string $beschreibung = null;
}

Wir platzieren diese Klasse bewusst unter src/Form/Data/ statt src/Entity/ – in Block 4 wird ProjectData durch die echte Project-Entity ersetzt, aber das Validierungsprinzip bleibt IDENTISCH.

Den Form Type mit data_class verbinden

src/Form/ProjectType.php
use App\Form\Data\ProjectData;
use Symfony\Component\OptionsResolver\OptionsResolver;

class ProjectType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder
            ->add('name', TextType::class, ['label' => 'Projektname'])
            ->add('beschreibung', TextareaType::class, ['label' => 'Beschreibung', 'required' => false])
        ;
    }

    public function configureOptions(OptionsResolver $resolver): void
    {
        $resolver->setDefaults(['data_class' => ProjectData::class]);
    }
}

data_class teilt dem Formular mit, WELCHE Klasse es befüllen soll – $form->getData() gibt jetzt ein ProjectData-OBJEKT zurück statt eines Arrays, UND isValid() prüft automatisch ALLE Constraints, die an dieser Klasse definiert sind.

Häufige Constraints im Überblick

ConstraintPrüft
#[Assert\NotBlank]Darf nicht leer sein.
#[Assert\Length(min: ..., max: ...)]Mindest-/Maximallänge für Strings.
#[Assert\Email]Muss eine gültige E-Mail-Adresse sein (Kapitel 28 nutzt das für die Registrierung).
#[Assert\Range(min: ..., max: ...)]Numerischer Wert innerhalb eines Bereichs.
#[Assert\Choice(choices: [...])]Wert muss aus einer festen Liste stammen (Kapitel 17 nutzt das für den Task-Status).
#[Assert\GreaterThan('today')]Für Datumswerte – nützlich für das Fälligkeitsdatum unserer Task-Entity.

Validierungsfehler werden automatisch angezeigt

Da {{ form(form) }} (Kapitel 15) Fehler bereits automatisch mit rendert, brauchen wir am Template NICHTS zu ändern – schlägt die Validierung fehl, erscheint die konfigurierte message AUTOMATISCH unter dem betroffenen Feld.

Eigene Validierungslogik: Callback-Constraint

Für Regeln, die sich nicht mit einem eingebauten Constraint abdecken lassen (z. B. "Fälligkeitsdatum darf nicht auf ein Wochenende fallen"), gibt es #[Assert\Callback]:

use Symfony\Component\Validator\Context\ExecutionContextInterface;

#[Assert\Callback]
public function validiere(ExecutionContextInterface $context): void
{
    if (str_contains($this->name, 'TODO')) {
        $context->buildViolation('Der Projektname darf nicht "TODO" enthalten.')
            ->atPath('name')
            ->addViolation()
        ;
    }
}

Tipp: Faustregel: eingebaute Constraints (NotBlank, Length, ...) IMMER bevorzugen, wenn sie die Regel abdecken – nur für WIRKLICH projektspezifische Logik, die sich nicht als einzelnes Feld isolieren lässt, zu #[Assert\Callback] greifen.