• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

Symfony validacja nie przesłanego pola

Object Storage Arubacloud
+1 głos
347 wizyt
pytanie zadane 16 września 2020 w PHP przez Klik Obywatel (1,540 p.)
edycja 17 września 2020 przez Klik

Witam.

Przepisuję projekt z Laravela na Symfony 5 i natrafiłem na taki mały szkopuł.

Stworzyłem ręcznie formulrz w html, który posiada jedno pole typu checkbox. A checkboxy w formularzu zachowują się tak że gdy nie są zaznaczone to nie jest nawet wysyłane na serwer.

Więc moja ręczna walidacja cały czas rzuca mi błędem że tego pola brakuje.

Czy jest jakaś "constrain" w symfony która pozowli mi w przypadku braku tego pola rzucić komunikatem "Że to pole musi być zaznaczone"? Albo przepuścić ten formularz że to pole nie jest wymagane i zwalidować poprawnie?

Input w formularzu:

 <input type="checkbox" id="regulation" name="regulation" class="form-control">

Constrains

 'regulation' => [
                    new NotBlank(['message' => 'aaaaaaaa']),
                    new NotNull(['message' => 'cccccc']),
                ]

I validacja w kontrolerze:

$validator->validate($request->request->all(), $constrains);



Dziękuję.

komentarz 16 września 2020 przez VBService Ekspert (253,400 p.)

Może użyj: <input type="checkbox" ... requiredwink

komentarz 17 września 2020 przez Klik Obywatel (1,540 p.)
Tak, to niby rozwiązuje problem ale nie odpowida na moje pytanie :)

1 odpowiedź

0 głosów
odpowiedź 16 września 2020 przez Assasz Nałogowiec (30,460 p.)
komentarz 16 września 2020 przez Klik Obywatel (1,540 p.)
Nie, to tak nie działa jak się wydaje że ma działać. To jest do validacji stringów.
komentarz 16 września 2020 przez Assasz Nałogowiec (30,460 p.)
edycja 16 września 2020 przez Assasz
Nie jest wyłącznie do stringów - to o czym mówisz dotyczy jeszcze Symfony 2. Constraint sprawdza, czy wartość nie jest pusta, tzn. nie jest pustym stringiem, tablicą lub nullem. Jeśli nie podasz takiego pola w ogóle, to walidacja tego nie przejdzie.

Chociaż w przypadku checkboxów lepszy byłby pewnie NotNull (bo false możesz podać, a NotBlank tego nie przepuści). Albo nawet IsTrue, jeśli spodziewasz się dostać 1 (true).
komentarz 16 września 2020 przez Klik Obywatel (1,540 p.)
Może nie wyraziłem się jasno w opisie problemu ale sprawdziłem (chyba) wszystkie możliwe kombinacje "Constraint". Cały czas wydaje mi się że coś muszę robić nie tak bo to chyba niemożliwe aby twórcy symfony nie przewidzieli takiej sytuacji. Tylko nie wiem co jest nie tak.

Z tego co piszesz to widzę że twoje pomysły to tylko teoria "że to powinno zadziałać" i z własnego doświadczenia nie znasz rozwiązania problemu. Czy może sprawdzałeś u siebie i działa.
komentarz 16 września 2020 przez Assasz Nałogowiec (30,460 p.)

Okej, przeprowadziłem szybki test, bo nie wierzyłem, aby żaden constraint nie był w stanie sprostać Twoim potrzebom:

  • NotBlank wywala domyślny błąd "This value should not be blank." w przypadku braku danego pola w request body.
  • NotNull to samo co powyżej.
  • IsTrue rzeczywiście pozwala na brak takiego pola. 

Napisałeś, że chcesz rzucić komunikatem "Że to pole musi być zaznaczone"  w przypadku braku takiej wartości. Jeśli używasz adnotacji, możesz to osiągnąć w ten sposób:

/**
 * @Assert\NotNull(message="To pole musi być zaznaczone.")
 */
private $yourCheckbox; 

Jeśli chcesz przepuścić walidację, po prostu usuń constraint.

Być może rzeczywiście nie wyraziłeś się jasno i problem leży gdzie indziej. Zapewniam Cię, że twórcy Symfony pomyśleli o tak trywialnych sytuacjach i dostarczyli odpowiednie rozwiązania. I nie są to moje domysły, tylko fakty poparte moim doświadczeniem w pracy z tym frameworkiem.

komentarz 17 września 2020 przez Bosswell Nałogowiec (36,470 p.)
Używasz typu https://symfony.com/doc/current/reference/forms/types/checkbox.html?

W dokumentacji piszę, że zwraca bool, więc przy walidacji mógłbyś sprawdzić typ bool

https://symfony.com/doc/current/reference/constraints/Type.html
komentarz 17 września 2020 przez Klik Obywatel (1,540 p.)
edycja 17 września 2020 przez Klik

@Assasz, Dzięki wielkie za sprawdzenie. Jak napisałem wyżej nie używam asercji tylko ręcznie tworzonego formularza i validacji do tego.

Edytowałem opis task gdzie dodałem trochę kodu jak to u mnie wygląda.

Proponowane przez Ciebie constraint u mnie z jakiegoś powodu nie działają. Nie zwracają mi domyslengo komunikatu dla tych constraint tylko "This field is missing.", co jest domyślnym komunikatem dla braku danego pola w formularzu.

Czyli w/w constraint w ogóle nie są odpalane. Więc moje pytanie dalej istnieje, jak rzucić własnym komunikatem w przypadku braku danego pola w przesłanych danych.

Jeszcze druga sprawa co mi się rzuciła w oczy teraz że te dane nie są w ogóle rzutowane na type bool. 

Nie ważne czy z to jest "on" czy przez dodanie value="1 lub true" dla validatora to zawsze jest string i "IsTrue" też nie działa.

komentarz 17 września 2020 przez Assasz Nałogowiec (30,460 p.)

Domyślną wartością checkboxa jest string "on", także nie wiem, dlaczego miałby być on rzutowany na boola. Masz dwie opcje, albo na serwer przychodzi "regulation": "on" albo nic. Zakładam, że próbowałeś dumpować zawartość requesta.

Ja bym taką walidację napisał z użyciem DTO:

final class InputDto
{
    /**
     * @Assert\NotNull(message="Ta wartość nie może być pusta.")
     */
    public ?string $regulation;

    public static function fromRequest(Request $request): self
    {
        $self = new self();
        $self->regulation = $request->request->get('regulation', null);

        return $self;
    }
}

// potem w kontrolerze

/** @var ValidatorInterface $validator */
$validator->validate(InputDto::fromRequest($request));

W przypadku braku wartości "regulation" w requescie, własność "regulation" w DTO będzie nullem. To ztriggeruje NotNull i wywali komunikat.

Możesz również przerobić swojego checkboxa, aby wysyłał boola:

<input type="checkbox" id="regulation" name="regulation" class="form-control" value="1">

Wtedy w DTO:

final class InputDto
{
    /**
     * @Assert\IsTrue(message="Ta wartość nie może być pusta.")
     */
    public bool $regulation;

    public static function fromRequest(Request $request): self
    {
        $self = new self();
        $self->regulation = $request->request->getBoolean('regulation', false);

        return $self;
    }
}

Przykład z adnotacjami, ale możesz równie dobrze użyć XMLa albo Yamla (co jest nawet lepsze). Pytanie, czy Twój walidator też jest dobrze skonfigurowany. Jeśli chcesz korzystać np. z adnotacji, musisz je włączyć w konfiguracji:

# config/packages/framework.yaml
framework:
    validation: { enable_annotations: true }

Możesz też przepisać to na Symfony Forms jak zasugerował Boswell ;)

Jeśli nadal nic z tego nie działa, to możesz mi wysłać link do repo (jeśli jest publiczne) i mogę w wolnej chwili rzucić okiem na szerszy kontekst, może coś zobaczę.

komentarz 17 września 2020 przez Klik Obywatel (1,540 p.)

Ale zachodu żeby jeden checkbox z validować :). Strach pomyśleć co będzie jak będę chciał jakiś bardziej skomplikowane rzeczy zwalidować.

Co do tego "on" to racja, ale miałem na myśli szerszy kontekst. Że jak przypiszę value="1" to też twierdzi że to nie jest bool. Jak przypisałem wartość value="true" to też twierdzi że to nie jest bool.

Zrobiłem test. Utworzyłem, nowy input typu number i przesyłam tam jakąś liczbę np. 10. W celu walidacji. 

'numreg' => [
                    new Negative(['message' => "Nie większe"]),
                    new Type(['type' => 'integer', 'message' => 'to nie int'])
                ]

I cały czas dostaje błąd że nie jest int. I dlaczego?

Jeszcze raz napiszę że to wszystko jest ręcznie zrobione bez użycia annotacji czy form.

Czyli co cała ta validacja to działa poprawnie tylko z dowma najgorszymi rzeczami w całym symfony (annotacje i symfony forms)?

Wrzucę to później do repo i podeślę linka. 

Dzięki

komentarz 17 września 2020 przez Klik Obywatel (1,540 p.)
komentarz 17 września 2020 przez Assasz Nałogowiec (30,460 p.)
edycja 17 września 2020 przez Assasz

W żądaniu zawierającym przesłany formularz zawsze dostajesz stringi (inputy są tekstowe, również checkbox). To już Twoja rola (albo Symfony), aby to rzutować na typy. Jak napisałem wyżej:

$self->regulation = $request->request->getBoolean('regulation', false);

$request->request->get('regulation') zwróci stringa, ponieważ w żadaniu nie ma 1, tylko "1" (zakładając, że wysyłasz taką wartość).

$request->request->getBoolean('regulation', false) spróbuje zrzutowac Twojego stringa na boola, czyli wysyłając "1" dostaniesz true. W przypadku braku wartości zwracany jest false (drugi argument).

(Swoją drogą, IsTrue akceptuje nie tylko true, ale również 1 oraz "1". Aczkolwiek akceptuje również wartość pustą, więc nie tędy droga.) 

Dlatego też napisałem, że zrobiłbym to przez DTO. Ponieważ konkretnie określam, jakie wartości potrzebuję z Requesta oraz jakiego typu mają być (w przypadku braku wartości w żądaniu przypisuję wartość domyślną - null albo false) - Ty po prostu przekazujesz parameter bag requesta ($request->request->all()) do walidatora.

Kod z repo obejrzę potem - jak będę się nudził, to sobie sklonuję i popatrzę "na żywo" ;)

komentarz 18 września 2020 przez Klik Obywatel (1,540 p.)
edycja 18 września 2020 przez Klik
Przeanalizowałem sposób validacji "Type" i już wiem dlaczego mi rzuca błędem przy sprawdzaniu typu "int, integer" że "10" to nie liczba. Podobnie dla bool, "true" i "1", też rzuca błędem.

Wszystko przez to że jest wykonywane proste sprawdzanie funkcjami "is_int" lub "is_bool".A te funkcje aby zwrócić true oczekują dokładnie takich typów na jakich jest robiony test.

Dziwne jest to że mając validację gdzie sprawdzam czy dany zmienna jest typem int nie jest wykonywana próba rzutowania zmiennej na wymagany typ. Tak to jest właśnie robione w Laravel. I to ma sens. I co lepiej na wyniku walidacji otrzymujemy już gotowe zmienne danych typów.

A tutaj co, zanim prześlę dane do walidacji powinienem wszystkie wartości próbować z konwertować na oczekiwane w walidacji typy. Bardzo to bez sensu.

Zgaduję że jeśli bym to zrobił na assercjiach obiektów to w jakiś magiczny sposób by to działało?

Najwyżej zostaje napisanie własnej klasy validującej.

 

Zostaje jeszcze tylko jeden problem. Jak zabezpieczyć validator aby nie rzucał błędem gdy jakiś parametr nie jest wymagany i nie zostanie przesłany. A w wypadku przesłania to zwalidować według wybranych parametrów. Chyba że zostaje tylko DTO.

Dziękuję.

Podobne pytania

0 głosów
1 odpowiedź 191 wizyt
pytanie zadane 14 grudnia 2017 w PHP przez Smatix Obywatel (1,050 p.)
0 głosów
1 odpowiedź 234 wizyt
pytanie zadane 11 lutego 2022 w PHP przez Moonmaker05 Początkujący (410 p.)
0 głosów
1 odpowiedź 142 wizyt

92,579 zapytań

141,429 odpowiedzi

319,657 komentarzy

61,963 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Kolejna edycja największej imprezy hakerskiej w Polsce, czyli Mega Sekurak Hacking Party odbędzie się już 20 maja 2024r. Z tej okazji mamy dla Was kod: pasjamshp - jeżeli wpiszecie go w koszyku, to wówczas otrzymacie 40% zniżki na bilet w wersji standard!

Więcej informacji na temat imprezy znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...