Benutzerdefinierte Validierungsgrundlagen in Symfony 4/5 mit Beispielen

In Symfony meiner Meinung nach eine sehr praktische Funktion für die Entitätsvalidierung. Insbesondere die Verwendung des Anmerkungsformats zum Konfigurieren von Validierungsregeln besticht mich wirklich. Bei den allermeisten Aufgaben decken schlüsselfertige Lösungen Standardfälle ab. Wie Sie wissen, ist die Validierung jedoch eine heikle Angelegenheit, und Sie wissen nie, welche Einschränkungen Sie diesmal auferlegen müssen. Auf der anderen Seite hilft eine flexiblere und durchdachte Validierung immer dabei, Benutzerfehler zu vermeiden.


Unter dem Schnitt lade ich Sie ein, zu sehen, wie einfach es ist, Ihre Einschränkungen zu schreiben und die Vergleiche zweier verfügbarer Felder am Beispiel der grundlegenden Validierung und Validierung zu erweitern. Der Artikel kann für diejenigen von Interesse sein, die mit der Validierung in Symfony noch wenig vertraut sind oder die Möglichkeit umgangen haben, ihre eigenen Validatoren zu schreiben.


In diesem Artikel werden wir den klassischen Kontext der Validierung berücksichtigen. Es gibt eine bestimmte Entität, deren Daten aus dem entsprechenden Formular ausgefüllt werden, während die Validierung Einschränkungen für jeden Aspekt der eingegebenen Daten auferlegt. Es ist erwähnenswert, dass Validatoren ohne Einschränkungen zur Steuerung der internen Logik, der API-Validierung usw. verwendet werden können.


Wir stellen sofort fest, dass die vorgeschlagenen Beispiele nicht die einzig möglichen Lösungen sind und keine maximale Optimalität oder umfassende Verwendung beanspruchen, sondern zu Demonstrationszwecken angeboten werden. Andererseits werden Teile der Beispiele aus Kampfprojekten übernommen und vereinfacht, und ich hoffe, dass sie hilfreich sein können, um die Einfachheit des Schreibens benutzerdefinierter Validierungen ohne Unterbrechung der Realität zu veranschaulichen.


Grundlegende Validierung


— Constraint ConstraintValidator. Constraint, , , ConstraintValidator . "hh:mm", . Constraint . .


namespace App\Custom\Constraints;

use Symfony\Component\Validator\Constraint;

/**
 * @Annotation
 * @Target({"PROPERTY"})
 */
class TextTime extends Constraint
{
    public $message = '  ';
}

Target : . ,


public function getTargets()
{
    //   self::CLASS_CONSTRAINT
    return self::PROPERTY_CONSTRAINT;
}

message .


, .


public function validatedBy()
{
    return \get_class($this).'Validator';
}

, , ConstraintValidator. ConstraintValidator validate.


namespace App\Custom\Constraints;

use Symfony\Component\Validator\ConstraintValidator;

class TextTimeValidator extends ConstraintValidator
{
    /**
     *    
     *
     * @param mixed $value  
     * @param Constraint $constraint   
     */
    public function validate($value, Constraint $constraint)
    {
        $time = explode(':', $value);

        $hours = (int) $time[0];
        $minutes = (int) $time[1];

        if ($hours >= 24 || $hours < 0)
            $this->fail($constraint);
        else if ((int) $time[1] > 60 || $minutes < 0)
            $this->fail($constraint);
    }

    private function fail(Constraint $constraint) 
    {
        $this->context->buildViolation($constraint->message)
            ->addViolation();
    }
}

, , , .


...
    /**
     * @Assert\NotBlank()
     * @Assert\Regex(
     *     pattern="/\d{2}:\d{2}/",
     *     message=" "
     * )
     * @CustomAssert\TextTime()
     * @ORM\Column(type="string", length=5)
     */
    private $timeFrom;
...

, , . , , , message .


. , Symfony Time. : . , "hh:mm:ss". , .



, , - . , , : "hh:mm", "hh:mm"-"hh:mm". , , .


, AbstractComparsion. AbstractComparsion Constraint — .


namespace App\Custom\Constraints;

use Symfony\Component\Validator\Constraints\AbstractComparison;

/**
 * @Annotation
 * @Target({"PROPERTY"})
 */
class TextTimeInterval extends AbstractComparison
{   
    public $message = '       {{ compared_value }}.';
}


namespace App\Custom\Constraints;

use Symfony\Component\Validator\Constraints\AbstractComparisonValidator;

class TextTimeIntervalValidator extends AbstractComparisonValidator
{
    /**
     *       
     *
     * @param mixed $timeFrom   
     * @param mixed $timeTo   
     *
     * @return  true   , false 
     */
    protected function compareValues($timeFrom, $timeTo)
    {
        $compareResult = true;

        $from = explode(':', $timeFrom);
        $to = explode(':', $timeTo);

        try {
            if ((int) $from[0] > (int) $to[0])
                $compareResult = false;
            else if (((int) $from[0] == (int) $to[0]) && ((int) $from[1] > (int) $to[1])) {
                $compareResult = false;
            }
        } catch (\Exception $exception) {
            $compareResult = false;
        }

        return $compareResult;
    }
}

Nur in diesem Fall (eine der möglichen Implementierungen) überschreiben wir die Funktion compareValues, die beim Validierungserfolg true / false zurückgibt und von der Funktion validate () im AbstractComparisonValidator verarbeitet wird.


Dies wird im Wesentlichen wie folgt beschrieben


...
 /**
     * @Assert\NotBlank()
     * @Assert\Regex(
     *     pattern="/\d{2}:\d{2}/",
     *     message=" "
     * )
     * @CustomAssert\TextTime()
     * @CustomAssert\TextTimeInterval(
     *     propertyPath="timeTo",
     *     message="       "
     * )
     * @ORM\Column(type="string", length=5)
     */
    private $timeFrom;

    /**
     * @Assert\NotBlank()
     * @Assert\Regex(
     *     pattern="/\d{2}:\d{2}/",
     *     message=" "
     * )
     * @CustomAssert\TextTime()
     * @ORM\Column(type="string", length=5)
     */
    private $timeTo;
...

Hoffentlich haben diese kleine Analyse und Beispiele gezeigt, wie einfach es ist, benutzerdefinierte Validatoren zu verwenden. Da wir für den Anfang natürlich die einfachsten Validatoren in Betracht gezogen haben, um die Präsentation nicht mit Themenbeschreibungen zu überladen, haben wir keine außergewöhnlichen Beispiele angegeben. In zukünftigen Artikeln plane ich, komplexere Validatoren und spezielle Fälle zu berücksichtigen.


All Articles