In a previous question (Symfony Check if at least one of two fields isn't empty on form validation) I had asked help for form validation using Callback. The answer given by @hous was right, but it doesn't work for elements in a CollectionType, reason why I'm opening a new question.
Based on the previous answer I have done the following:
Here is my "mother" Form:
class BookingVisitorType extends AbstractType{ private $router; private $translator; public function __construct() { } public function buildForm(FormBuilderInterface $builder, array $options) { $builder ->add('visitors', CollectionType::class, ['entry_type' => VisitorType::class,'label' => 'entity.booking.visitors','allow_add' => true,'allow_delete' => true,'delete_empty' => true,'by_reference' => false,'entry_options' => ['label' => false,'delete-url' => $options['visitor-delete-url'] ],'constraints' =>[ new Count(['min' => 1,'minMessage' => 'validator.visitor.at-least-one-visitor','max' => $options['numberOfPlaces'],'maxMessage' => 'validator.visitor.cannot-have-more-visitor-than-spaces','exactMessage' => 'validator.visitor.exact-message' ]) ] ]) ; } public function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults(['data_class' => Booking::class,'numberOfPlaces' => 1,'visitor-delete-url' => '' ]); }}
Here is my "son" Form:
class VisitorType extends AbstractType{ private $phone; public function buildForm(FormBuilderInterface $builder, array $options) { $builder ->add('firstName', TextType::class, ['label' => 'entity.visitor.first-name','constraints' => [ new NotBlank(), new Length(['min' => 2,'max' => 255 ]), new Regex(['pattern' => "/[\pL\s\-]*/",'message' => 'validator.visitor.not-valide-first-name' ]) ] ]) ->add('phone', TextType::class, ['label' => 'entity.visitor.phone-number','required' => false,'constraints' => [ new Regex(['pattern' => "/[0-9\s\.\+]*/",'message' => 'validator.visitor.not-valide-phone-number' ]), new Callback(function($phone, ExecutionContextInterface $context){ $this->phone = $phone; }), ] ]) ->add('email', TextType::class, ['label' => 'entity.visitor.email','required' => false,'constraints' => [ new Email(), new Callback(function($email, ExecutionContextInterface $context){ if ($this->phone == null && $email == null) { $context->buildViolation('validator.visitor.email-or-phone-required')->addViolation(); } }), ] ]) ; } public function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults(['data_class' => Visitor::class,'error_bubbling' => false,'delete-url' => '', ]); }}
My "booking" (shortened) class:
/** * @ORM\Entity(repositoryClass="App\Repository\BookingRepository") */class Booking{ /** * @ORM\Id() * @ORM\GeneratedValue() * @ORM\Column(type="integer") */ private $id; /** * @ORM\OneToMany(targetEntity="App\Entity\Visitor", mappedBy="booking", orphanRemoval=true, cascade={"persist"}) * @Assert\Valid */ private $visitors;}
And finally my "visitor" (shortened) class:
/** * @ORM\Entity(repositoryClass="App\Repository\VisitorRepository") */class Visitor{ /** * @ORM\Id() * @ORM\GeneratedValue() * @ORM\Column(type="integer") */ private $id; /** * @ORM\Column(type="string", length=45, nullable=true) */ private $phone; /** * @ORM\Column(type="string", length=255, nullable=true) */ private $email; /** * @ORM\ManyToOne(targetEntity="App\Entity\Booking", inversedBy="visitors") * @ORM\JoinColumn(nullable=false) */ private $booking; /** * @Assert\Callback */ public function validateAtLeastEmailOrPhone(ExecutionContextInterface $context, $payload) { if ($this->getPhone() === null && $this->getEmail() === null) { $context->buildViolation('validator.visitor.email-or-phone-required-for-all')->addViolation(); } }}
I've been able to workaround the problem by adding a property to my VisitorType form that I define with the Callback constraint on the phone value and then check it with a Callback constraint on the email field, but it doesn't seem very "good practice".
If I only try to call the Callback constraint I get the following error message: "Warning: get_class() expects parameter 1 to be object, string given"
Any help is highly appreciated!