Dasar-dasar Validasi Kustom di Symfony 4/5 dengan Contoh

Dalam Symfony, menurut saya, fungsionalitas yang sangat nyaman untuk validasi entitas. Secara khusus, penggunaan format anotasi untuk mengonfigurasi aturan validasi benar-benar menyuap saya. Dalam sebagian besar tugas, solusi turnkey mencakup kasus standar. Tetapi, seperti yang Anda tahu, validasi adalah masalah yang rumit, dan Anda tidak pernah tahu batasan apa yang harus Anda terapkan kali ini. Di sisi lain, validasi yang lebih fleksibel dan dipikirkan matang-matang akan selalu membantu menghindari kesalahan pengguna.


Di bawah potongan, saya mengundang Anda untuk melihat betapa mudahnya menulis batasan Anda dan memperluas perbandingan dua bidang yang tersedia menggunakan contoh validasi dasar dan validasi. Artikel tersebut mungkin menarik bagi mereka yang masih sedikit akrab dengan validasi di Symfony atau yang telah melewati kemungkinan menulis validator mereka sendiri.


Dalam artikel ini kita akan mengingat konteks validasi klasik. Ada entitas tertentu, data yang diisi dari formulir yang sesuai, sementara validasi memberlakukan pembatasan pada setiap aspek data yang dimasukkan. Perlu dicatat bahwa validator dapat digunakan tanpa batasan untuk mengendalikan logika internal, validasi API, dll.


Kami segera menentukan bahwa contoh yang diajukan bukan satu-satunya solusi yang mungkin dan tidak mengklaim optimalitas maksimum atau penggunaan ekstensif, tetapi ditawarkan untuk tujuan demonstrasi. Di sisi lain, bagian dari contoh diambil dan disederhanakan dari proyek pertempuran, dan saya berharap mereka dapat berguna dalam menggambarkan kesederhanaan penulisan validasi kebiasaan tanpa gangguan dari kenyataan.


Validasi dasar


Untuk membuat validasi yang berfungsi penuh, Anda hanya perlu membuat dua kelas - ahli waris dari Constraint dan ConstraintValidator. Kendala, seperti namanya, mendefinisikan dan menjelaskan kendala, sementara ConstraintValidator memvalidasi mereka. Sebagai contoh, kami akan menulis validator untuk waktu dalam format "hh: mm" yang disimpan dalam format teks. Dalam dokumentasi resmi, diusulkan agar Batasan menggambarkan sifat-sifat umum dari kendala. Jadi mari kita lakukan.


namespace App\Custom\Constraints;

use Symfony\Component\Validator\Constraint;

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

Di sini, anotasi Target menentukan apakah validasi akan digunakan: untuk properti atau untuk kelas. Anda juga dapat mengatur parameter ini dengan mengabaikan fungsi.


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

Properti pesan, seperti yang Anda duga, digunakan untuk menampilkan informasi tentang kesalahan validasi.


, .


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;
    }
}

Hanya dalam kasus ini (salah satu implementasi yang mungkin) kami mengesampingkan fungsi compareValues, yang mengembalikan true / false pada keberhasilan validasi dan diproses dalam AbstractComparisonValidator oleh fungsi validate ().


Ini pada dasarnya dijelaskan sebagai berikut


...
 /**
     * @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;
...

Semoga sedikit analisis dan contoh ini menunjukkan betapa mudahnya menggunakan validator khusus. Karena awalnya kami menganggap validator paling sederhana, tentu saja, agar tidak membebani presentasi dengan deskripsi subjek, kami gagal memberikan contoh luar biasa. Dalam artikel mendatang saya berencana untuk mempertimbangkan validator yang lebih kompleks dan kasus spesifik.


All Articles