Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: [Symfony][SF2][Symfony2]Dodanie walidacji do formularza nie używającego encji
Forum PHP.pl > Forum > PHP > Frameworki
Crash89
Chcę tutaj porównać jedną daną pobraną w przesyłanym formularzu z drugą.
Wyczytałem gdzieś że do rozwiązania tego problemu, przy tworzeniu formularza FormType należy wykorzystać obiekt Callback, konstrukcja wygląda mniej więcej tak:
  1. use Symfony\Component\Validator\ExecutionContextInterface;
  2. use Symfony\Component\Validator\Constraints\Callback;
  3.  
  4.  
  5. public function buildForm(FormBuilderInterface $builder, array $options)
  6. {
  7. $builder->add('pole','type',array(
  8. 'mapped' => false,
  9. 'constraints' => array(
  10. new Callback(array($this,'validate')
  11. ))));
  12. // i treść funkcji validate
  13.  
  14. public function validate(ExecutionContextInterface $context){
  15. if($data['pole1'] >= $data->['pole2']){
  16. $context->addViolation("Tekst błędu");
  17. }
  18. }
  19.  


Problem w tym że nie wiem gdzie mam umieścić funckję validate.
Umieszczałem ją w FormType i nie działa, także próbowałem w kontrolerze, ale tam też jakoś na to nie reaguje.
Przy formularzu wykorzystującym entity, umieszczam ją w entity i wszystko działa, ale tutaj nie mam takiej klasy.



Więc, gdzie umieścić tą funkcję? smile.gif
Crozin
A skąd wziąłeś sobie niby zmienną $data w metodzie validate()? Masz tam dostęp do obiektu ExecutionContextInterface i przez niego powinieneś jak już dobrać się do właściwości poszczególnych pól.
Crash89
$data powinienem brać z formularza:
  1. $data = $form->getData();


Wygląda na to że miałbym umieścić tą funkcję w kontrolerze, ale wydaje mi sie że to nie jest dobra praktyka.
Mam stworzyć oddzielną klase walidacji dla tego przypadku?
np. class validatePole?
Crozin
Komponent od walidacji danych nie ma na dobrą sprawę zbyt dużego pojęcia na temat czegoś takiego jak "formularz". Pod $context->getValue() zostanie zwrócona Ci wartość, która jest akutualnie poddawana sprawdzaniu. Możesz w tym miejscu dokonać porównania dwóch właściwości i w przypadku błędu za pomocą $context->buildViolation() dodać błąd.
blahy
Co to znaczy nie dziala?

Nie dziala bo zgodnie z dokumentacja: http://symfony.com/doc/current/reference/c...callback-option
callback dostaje:

Concrete callbacks receive an ExecutionContextInterface instance as only argument.
Static or closure callbacks receive the validated object as the first argument and the ExecutionContextInterface instance as the second argument.


Wiec Twoja metoda validate jest w dobrym miejscu (tzn moglaby byc gdzie indziej niz w form type, ale tutaj tez jest ok), ale powinna miec taka sygnature:
public function validate($value, ExecutionContextInterface $ec).

Tak jak ty masz powinienes dostac:
Catchable Fatal Error: Argument 1 passed to AppBundle\Form\TestType::validate() must implement interface Symfony\Component\Validator\Context\ExecutionContextInterface, string given
albo cos podobnego - bylby to lepszy komunikat niz "nie dziala". Jesli nie widzisz tych bledow to polecam developowac przez kontroler developerski smile.gif

Dla pewnosci wysylam przykladowy form type, ktory dziala. Jesli w pole "Pole" wpiszemy "a" to formularz jest prawidlowy, a jesli b to dostaniemy blad podpiety pod pole: Pole musi byc a

  1. <?php
  2.  
  3. namespace AppBundle\Form;
  4.  
  5. use Symfony\Component\Form\AbstractType;
  6. use Symfony\Component\Form\FormBuilderInterface;
  7. use Symfony\Component\Validator\Constraints\Callback;
  8. use Symfony\Component\Validator\Context\ExecutionContextInterface;
  9.  
  10. class TestType extends AbstractType
  11. {
  12. public function buildForm(FormBuilderInterface $builder, array $options)
  13. {
  14. $builder->add('pole','text',array(
  15. 'mapped' => false,
  16. 'constraints' => array(
  17. new Callback(array($this,'validate')
  18. ))));
  19. }
  20.  
  21. public function validate($value, ExecutionContextInterface $ec)
  22. {
  23. if ($value !== 'a') {
  24. $ec->buildViolation('Pole musi byc a')
  25. ->atPath('pole')
  26. ->addViolation();
  27. }
  28. }
  29.  
  30. public function getName()
  31. {
  32. return 'test';
  33. }
  34. }


no i kontroler:
  1. <?php
  2.  
  3. namespace AppBundle\Controller;
  4.  
  5. use AppBundle\Form\TestType;
  6. use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
  7. use Symfony\Bundle\FrameworkBundle\Controller\Controller;
  8.  
  9. class DefaultController extends Controller
  10. {
  11. /**
  12.   * @Route("/app/example", name="homepage")
  13.   */
  14. public function indexAction(\Symfony\Component\HttpFoundation\Request $request)
  15. {
  16. $form = $this->createForm(new TestType());
  17.  
  18. $form->handleRequest($request);
  19.  
  20. if ($form->isValid()) {
  21. die('valid');
  22. }
  23.  
  24. return $this->render('default/index.html.twig', [
  25. 'form' => $form->createView(),
  26. ]);
  27. }
  28. }
Crash89
Dzięki waszym postom udało mi sie rozwiązać problem smile.gif

Ostatecznie rozwiązałem to tak:

W klasie FormType umieściłem funkcję validate.
  1.  
  2. public function validate($value,ExecutionContextInterface $context){
  3. $form = $context->getRoot();
  4.  
  5. if($form->get('pole1')->getData() <= $form->get('pole2')->getData();
  6. $context->buildViolation('Text błędu')
  7. ->atPath('pole2')
  8. ->addViolation();
  9. }
  10. }
blahy
jest ok, ale jesli ta metoda walidacji ma dotyczyc calego formularza (bo uzywa wiekszej ilosci pol) to moze byc lepszym pomyslem dodanie walidatora do calego formularza, a nie do pojedynczego pola.

Do klasy FormType mozna dodac metode:
  1. public function setDefaultOptions(OptionsResolverInterface $resolver) {
  2. $resolver->setDefaults(array(
  3. 'constraints' => array(
  4. new Callback(array($this,'validate'))
  5. ),
  6. ));
  7. }


Wtedy w metodzie validate w 1 argumencie dostaniemy array z wszystkimi polami z formularza (pola nie powinny miec mapped => false, i tak nie przekazujemy obiektu):

  1. public function validate($value, ExecutionContextInterface $ec)
  2. {
  3. var_dump($value); // array(1) { ["pole"]=> string(3) "fff" }
Crash89
Cytat(blahy @ 17.07.2015, 18:42:27 ) *
jest ok, ale jesli ta metoda walidacji ma dotyczyc calego formularza (bo uzywa wiekszej ilosci pol) to moze byc lepszym pomyslem dodanie walidatora do calego formularza, a nie do pojedynczego pola.

Do klasy FormType mozna dodac metode:
  1. public function setDefaultOptions(OptionsResolverInterface $resolver) {
  2. $resolver->setDefaults(array(
  3. 'constraints' => array(
  4. new Callback(array($this,'validate'))
  5. ),
  6. ));
  7. }


Wtedy w metodzie validate w 1 argumencie dostaniemy array z wszystkimi polami z formularza (pola nie powinny miec mapped => false, i tak nie przekazujemy obiektu):

  1. public function validate($value, ExecutionContextInterface $ec)
  2. {
  3. var_dump($value); // array(1) { ["pole"]=> string(3) "fff" }


Używając powyższej metody, zwraca mi komunikat podobny do: "cannot use DateTime as Array".
Co jest dziwne, bo gdy porównuje te wartości z $value['pole1'] z $form->get('pole1')->getData() przez var_dump(), są takie same.
blahy
Moze zmieniles funkcje validate a zostawiles constraint tez dla pola, tak jak miales wczesniej. Dlatego dalej wartosc datetime przekazywana jest do funkcji validate.
Jesli nie, to znowu przydalby sie kod smile.gif
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.