Symfony isValid() always returns true - php

As it stands isValid() returns true on form submission regardless of whether the fields have been filled out or not. The objective is to get isValid() method to actually check the data. Any help would be much appreciated.
Form Entity:
namespace Bookboon\Premium\AppBundle\Entity;
class Form {
protected $email;
protected $id,
$type,
$voucher,
$subscription,
$affiliate;
public function getEmail(){
return $this->email;
}
public function setEmail($email){
$this->email = $email;
}
// The rest of the getters & setters have been omitted for ease of reading, but they look very similar to the above.
}
services.yml:
services:
form.builder:
class: Symfony\Component\Form\FormBuilder
arguments: ['SignUpForm', 'Bookboon\Premium\AppBundle\Entity\Form', #event_dispatcher, #form.factory]
premium.form.entity:
class: Bookboon\Premium\AppBundle\Entity\Form
arguments: [ ]
premium.form.sign_up:
class: Bookboon\Premium\AppBundle\Form\SignUpFormType
tags:
- { name: form.type, alias: SignUpForm }
premium.controller.signup:
class: Bookboon\Premium\AppBundle\Controller\SignUpController
arguments: [#templating, #form.builder, #premium.form.sign_up, #form.factory, #premium.form.entity ]
validation.yml:
Bookboon\Premium\AppBundle\Entity\Form:
properties:
email:
- NotBlank: ~
- NotNull: ~
SignUpController.php:
namespace Bookboon\Premium\AppBundle\Controller;
use Bookboon\Premium\AppBundle\Entity\Form;
use Bookboon\Premium\AppBundle\Form\SignUpFormType;
use Bookboon\Premium\AppBundle\Form\SignUpDetailsForm;
use Symfony\Bundle\FrameworkBundle\Templating\EngineInterface;
use Symfony\Component\Form\FormBuilder;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Form\FormFactory;
class SignUpController implements DetectionController
{
private $_templating;
private $_formBuilder;
private $_form;
private $_formFactory;
private $_formData;
public function __construct(EngineInterface $templating, FormBuilder $formBuilder, SignUpFormType $signUpFormType, FormFactory $formFactory, Form $formData)
{
$this->_templating = $templating;
$this->_formBuilder = $formBuilder;
$this->_form = $signUpFormType;
$this->_formFactory = $formFactory;
$this->_formData = $formData;
}
public function signUpDetailsAction()
{
return $this->renderForm('SignUpDetailsForm', new SignUpDetailsForm(), 'details');
}
public function signUpAction(Request $request)
{
$form = $this->createForm($this->_form, $this->_formData);
$form->handleRequest($request);
if($request->getMethod() == 'POST'){
if($form->isValid()){
dump('Form Is Valid!');
}
return $this->renderForm($form->createView(), 'signup');
}
return $this->renderForm($form->createView(), 'signup');
}
public function renderForm($form, $type)
{
return $this->_templating->renderResponse( 'PremiumBundle:Connect:'.$type.'.html.twig', array('form'=>$form));
}
public function createForm($type, $data = null, array $options = array())
{
return $this->_formFactory->create($type, $data, $options);
}
}
SignUpFormType:
namespace Bookboon\Premium\AppBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Bookboon\Premium\AppBundle\Entity;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class SignUpFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('email', 'text', array('label'=>' ',
'attr' => array('placeholder'=>'Your email', 'class'=>'email')))
->add('save', 'submit', array( 'label'=>'Access Premium',
'attr'=>array('class'=>'btn btnProceed'),
'validation_groups' => true))
->add('facebook', 'submit', array( 'validation_groups' => false,
'attr' =>array('class'=>'btn btnFacebook cancel')))
->add('linkedin', 'submit', array( 'validation_groups' => false,
'attr' =>array('class'=>'btn btnLinkedIn cancel')))
->add('id', 'hidden')
->add('type', 'hidden')
->add('voucher', 'hidden');
}
public function getName()
{
return 'SignUpForm';
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Bookboon\Premium\AppBundle\Entity\Form',
));
}
}

Related

Form rendering for many to many relation in symfony 4 does not work

I have many to many relation between two entities Recipient and Recipient Group. I am trying to setup a form for Recipient where I must be able to add multiple Recipient Group to Recipient. But when I try to run the rendered form, I get the following error even though addXXX and removeXXX methods exist in both the classes.
Could not determine access type for property "recipientGroups" in class
"App\Entity\Recipient": The property "recipientGroups" in class "App\Entity\Recipient" can
be defined with the methods "addRecipientGroup()", "removeRecipientGroup()" but the new
value must be an array or an instance of \Traversable, "App\Entity\RecipientGroup" given.
Entity/Recipient
**
*
#ORM\Entity(repositoryClass="App\Repository\RecipientRepository")
*/
class Recipient
{
...
/**
* #ORM\ManyToMany(targetEntity="App\Entity\RecipientGroup", inversedBy="recipients")
*/
private $recipientGroups;
public function __construct()
{
$this->recipientGroups = new ArrayCollection();
}
/**
* #return Collection|RecipientGroup[]
*/
public function getRecipientGroups(): Collection
{
return $this->recipientGroups;
}
public function addRecipientGroup(RecipientGroup $recipientGroup): self
{
if (!$this->recipientGroups->contains($recipientGroup)) {
$this->recipientGroups[] = $recipientGroup;
$recipientGroup->addRecipient($this);
}
return $this;
}
public function removeRecipientGroup(RecipientGroup $recipientGroup): self
{
if ($this->recipientGroups->contains($recipientGroup)) {
$this->recipientGroups->removeElement($recipientGroup);
$recipientGroup->removeRecipient($this);
}
return $this;
}
public function getCreatedAt(): DateTime
{
return $this->createdAt;
}
public function setCreatedAt(DateTime $createdAt): self
{
$this->createdAt = $createdAt;
return $this;
}
public function getUpdatedAt(): DateTime
{
return $this->updatedAt;
}
public function setUpdatedAt(DateTime $updatedAt): self
{
$this->updatedAt = $updatedAt;
return $this;
}
}
Entity/RecipientGroup:
/**
* #ORM\Entity(repositoryClass="App\Repository\RecipientGroupRepository")
*/
class RecipientGroup
{
/**
* #ORM\ManyToMany(targetEntity="App\Entity\Recipient", mappedBy="recipientGroups")
*/
private $recipients;
public function __construct()
{
$this->recipients = new ArrayCollection();
}
* #return Collection|Recipient[]
*/
public function getRecipients(): Collection
{
return $this->recipients;
}
public function addRecipient(Recipient $recipient): self
{
if (!$this->recipients->contains($recipient)) {
$this->recipients[] = $recipient;
$recipient->addRecipientGroup($this);
}
return $this;
}
public function removeRecipient(Recipient $recipient): self
{
if ($this->recipients->contains($recipient)) {
$this->recipients->removeElement($recipient);
$recipient->removeRecipientGroup($this);
}
return $this;
}
}
form/RecipientType
class RecipientType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('fullName')
->add('email')
->add('recipientGroups', CollectionType::class, [
'entry_type' => RecipientGroupBlockType::class,
'entry_options' => ['label' => false],
'allow_add' => true,
'allow_delete' => true,
'help' => '<a data-collection="add" class="btn btn-info btn-sm" href="#">Add Recipient Group</a>',
'help_html' => true,
'by_reference' => false,
]);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Recipient::class,
]);
}
}
form/RecipientGroupBlockType:
<?php
namespace App\Form;
use App\Entity\Recipient;
use App\Entity\RecipientGroup;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class RecipientGroupBlockType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('recipientGroups', EntityType::class,[
'class' => RecipientGroup::class,
'choice_label' => 'title',
'placeholder' => 'Select an option'
]);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Recipient::class,
]);
}
}

form.post_bind is fired twice

I added a form event to my Symfony Form. The problem is that this event seems to be fired twice. When I am debugging, it goes twice into the method and I don't know why.
I have a form Type that uses another form type. I have a VideoFileType form that is adding a field of ImageType. Both of these forms need some process to be done once the form is submitted.
ImageType
<?php
# src/Acme/PhotoBundle/Form/Type/ThumbnailType.php
namespace OSC\MediaBundle\Form\Type;
use OSC\MediaBundle\Manager\ImageManager;
use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\ChoiceList\ObjectChoiceList;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\HttpKernel\Exception\UnsupportedMediaTypeHttpException;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class ImageType extends AbstractType
{
private $imageManager;
public function __construct(ImageManager $imageManager) {
$this->imageManager = $imageManager;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('file', 'file');
$builder->addEventListener(
FormEvents::POST_SUBMIT,
array($this, 'onPostSetData')
);
}
public function getDefaultOptions(array $options) {
return array('data_class' => 'OSC\MediaBundle\Entity\Image');
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'OSC\MediaBundle\Entity\Image'
));
}
public function onPostSetData(FormEvent $event) {
$image = $event->getData();
$form = $event->getForm();
//We need here to update the image file with the new content
$image = $this->imageManager->uploadImage($image);
$event->setData($image);
}
public function getName()
{
return 'image';
}
}
VideoFileType
<?php
# src/Acme/PhotoBundle/Form/Type/ThumbnailType.php
namespace OSC\MediaBundle\Form\Type;
use OSC\MediaBundle\Manager\VideoFileManager;
use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\ChoiceList\ObjectChoiceList;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
class VideoFileType extends AbstractType
{
public $container;
public $videoPresets = [];
public $videoFileManager;
public function __construct(VideoFileManager $videoFileManager, Container $container) {
$this->container = $container;
$videoPresets = $container->getParameter('osc_media.video.presets');
foreach ($videoPresets as $key => $videoPreset) {
array_push($this->videoPresets, $key);
}
$this->videoFileManager = $videoFileManager;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('thumbnail', 'image');
$builder->add('file', 'file');
$builder->add('videoPreset', 'choice', array(
'choices' => $this->videoPresets,
'multiple' => false,
'required' => true
));
$builder->addEventListener(
FormEvents::POST_SUBMIT,
array($this, 'onPostSetData')
);
$builder->add('save', 'submit');
}
public function onPostSetData(FormEvent $event) {
$videoFile = $event->getData();
$form = $event->getForm();
//We upload the video file
$videoFile = $this->videoFileManager->uploadVideoFile($videoFile);
$event->setData($videoFile);
}
public function getDefaultOptions(array $options) {
return array('data_class' => 'OSC\MediaBundle\Entity\VideoFile');
}
public function getName()
{
return 'video_file';
}
}
I don't know why the onPostSetData method is called twice...

Form Validation Using Injected Forms Symfony2

Problem: When form is submitted with no data isValid() returns true.
Form Entity (Data Class), stores data submitted:
<?php
namespace Bookboon\Premium\AppBundle\Entity;
class Form {
protected $email;
protected $id;
protected $type;
protected $voucher;
protected $subscription;
protected $affiliate;
public function getEmail(){
return $this->email;
}
public function setEmail($email){
$this->email = $email;
}
public function getId(){
return $this->id;
}
public function setId($id){
$this->id = $id;
}
public function getType(){
return $this->type;
}
public function setType($type){
$this->type = $type;
}
public function getVoucher(){
return $this->voucher;
}
public function setVoucher($voucher){
$this->voucher = $voucher;
}
public function setSubscription($subscription){
$this->subscription = $subscription;
}
public function getSubscription(){
return $this->subscription;
}
public function setAffiliate($affiliate){
$this->affiliate = $affiliate;
}
public function getAffiliate(){
return $this->affiliate;
}
}
Form Type Class:
namespace Bookboon\Premium\AppBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Bookboon\Premium\AppBundle\Entity;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class SignUpFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('email', 'email', array('label'=>' ',
'attr' => array('placeholder'=>'Your email', 'class'=>'email')))
->add('save', 'submit', array( 'label'=>'Access Premium',
'attr'=>array('class'=>'btn btnProceed'),
'validation_groups' => true))
->add('facebook', 'submit', array( 'validation_groups' => false,
'attr' =>array('class'=>'btn btnFacebook cancel')))
->add('linkedin', 'submit', array( 'validation_groups' => false,
'attr' =>array('class'=>'btn btnLinkedIn cancel')))
->add('id', 'hidden')
->add('type', 'hidden')
->add('voucher', 'hidden');
}
public function getName()
{
return 'SignUpForm';
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Bookboon\Premium\AppBundle\Entity\Form',
));
}
}
Services.yml (templating service and builder service are written, just ommitted here for ease of reading):
premium.form.sign_up:
class: Bookboon\Premium\AppBundle\Form\SignUpFormType
tags:
- { name: form.type, alias: SignUpForm }
premium.controller.signup:
class: Bookboon\Premium\AppBundle\Controller\SignUpController
arguments: [ #templating, #form.builder, #premium.form.sign_up ]
Validation.yml:
Bookboon\Premium\AppBundle\Entity\Form:
properties:
email:
- NotBlank: ~
Form Controller:
namespace Bookboon\Premium\AppBundle\Controller;
use Bookboon\Premium\AppBundle\Form\SignUpFormType;
use Symfony\Bundle\FrameworkBundle\Templating\EngineInterface;
use Symfony\Component\Form\FormBuilder;
use Symfony\Component\HttpFoundation\Request;
class SignUpController implements DetectionController
{
private $_templating;
private $_formBuilder;
private $_form;
public function __construct(EngineInterface $templating, FormBuilder $formBuilder, SignUpFormType $signUpFormType)
{
$this->_templating = $templating;
$this->_formBuilder = $formBuilder;
$this->_form = $signUpFormType;
}
public function signUpAction(Request $request)
{
$form = $this->_formBuilder->create($this->_form->getName(), $this->_form);
$form = $form->getForm();
$form->handleRequest($request);
dump($form->isValid()); // Returns true regardless of data (even if blank)
if($request->getMethod() == 'POST'){
if($form->isValid()){
dump('yata!');
}
return $this->renderForm($form->createView(), 'signup');
}
return $this->renderForm($form->createView(), 'signup');
}
public function renderForm($form, $type)
{
return $this->_templating->renderResponse( 'PremiumBundle:Connect:'.$type.'.html.twig', array('form'=>$form));
}
}
Would really appreciate it if someone could help me with this....
Hmm ran into this problem a while back too and haven't figured - I'm new to symfony and my cursory search engine search revealed nada, I too would appreciate the quickmefix here!

Access Request parameters inside Symfony2 DataTransformer

Is it possible to get value from another form field inside DataTransformer for a field?
I can create an invitation linked to an email, then when the user register he must type an invitation code, it will work even if that code is not linked to the email he's entered, because Invitation field is a DataTransformer that checks the value inside DB. I would like to check inside that query, if the email exists.
MainForm
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('invitation', 'invitation_type', array(
'required' => true,
'label' => false,
'attr' => array(
'placeholder' => 'form.invitation_code',
'class' => 'form-control',
)
))
;
}
invitation_type is a service where I inject entityManager to a fieldtype that renders the dataTransformer:
InvitationType
public function buildForm(FormBuilderInterface $builder, array $options)
{
$transformer = new InvitationTransformer($this->entityManager);
$builder->addModelTransformer($transformer);
}
And then the transformer
public function reverseTransform($value)
{
$invitation = $this->entityManager->getRepository('Invitation')
->findOneBy(array(
'code' => $value
));
return $invitation;
}
That's the query, which as I said is working without checking the email value, it would be something like 'email' => $emailValue but, I don't know how to access $emailValue
You can inject your value into transformer
public function buildForm(FormBuilderInterface $builder, array $options)
{
$entity = $builder->getData();
$email = $entity->getEmail();
$transformer = new MyTransformer($email);
$builder->add(
$builder->create('sample', 'choice', array(
'attr' => array('class' => 'test')
))->addModelTransformer($transformer)
)
}
class MyTransformer implements DataTransformerInterface {
private $emailValue;
public function __construct($emailValue)
{
$this->emailValue = $emailValue;
}
public function reverseTransform($value)
{
// Do something with $this->emailValue;
$invitation = $this->entityManager->getRepository('Invitation')
->findOneBy(array('code' => $value));
return $invitation;
}
}
So if someone has the same problem I've found a solution.
First create a service for your custom field type, injecting #request_stack
foo.form.type.invitation:
class: Foo\BarBundle\Form\Type\InvitationType
arguments: [ "#doctrine.orm.entity_manager" ]
tags:
- { name: form.type, alias: invitation_type}
calls:
- [setRequest, [#request_stack]]
Then create your custom field type class, which will inject our Request to the DataTransformer
<?php
namespace Foo\BarBundle\Form\Type;
use Doctrine\ORM\EntityManager;
use Foo\BarBundle\Form\DataTransformer\InvitationTransformer;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\HttpFoundation\RequestStack;
class InvitationType extends AbstractType
{
private $entityManager;
protected $request;
public function __construct(EntityManager $em)
{
$this->entityManager = $em;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$transformer = new InvitationTransformer($this->entityManager, $this->request);
$builder->addModelTransformer($transformer);
}
public function getParent()
{
return 'text';
}
public function getName()
{
return 'invitation_type';
}
public function setRequest(RequestStack $request_stack)
{
$this->request = $request_stack->getCurrentRequest();
}
}
And then our DataTransformer needs to fetch the data, change values to match your requests, of course
<?php
namespace Foo\BarBundle\Form\DataTransformer;
use Doctrine\ORM\EntityManager;
use Symfony\Component\Form\DataTransformerInterface;
use Symfony\Component\Form\Exception\UnexpectedTypeException;
use Symfony\Component\HttpFoundation\Request;
class InvitationTransformer implements DataTransformerInterface
{
protected $entityManager;
protected $request;
public function __construct(EntityManager $entityManager, Request $request)
{
$this->entityManager = $entityManager;
$this->request = $request;
}
public function transform($value)
{
if (null === $value) {
return null;
}
if (!$value instanceof Invitation) {
throw new UnexpectedTypeException($value, 'Foo\BarBundle\Entity\Invitation');
}
return $value->getCode();
}
public function reverseTransform($value)
{
if (null === $value || '' === $value) {
return null;
}
if (!is_string($value)) {
throw new UnexpectedTypeException($value, 'string');
}
$formData = $this->request->get('registration_form'); // Your MAIN form goes here
$email = $formData['email']; // The value you need
$invitation = $this->entityManager->getRepository('FooBarBundle:Invitation')
->findOneBy(array(
'code' => $value,
'email' => $email
));
if($this->entityManager->getRepository('FooBarBundle:User')->findOneBy(array("invitation" => $invitation))){
return null;
}
return $invitation;
}
}
Now you have access to your parameters bag inside your DataTransformer, piece of cake.

symfony2 service array parameters. How to get value based on key

I'm creating new invoice form, which includes select input with some constant values.
I've made it through services:
services.yml
parameters:
stawki_vat:
0: 0%
5: 5%
8: 8%
23: 23%
zw: zw.
services:
acme.form.type.stawki_vat:
class: Acme\FakturyBundle\Form\Type\StawkiVatType
arguments:
- "%stawki_vat%"
tags:
- { name: form.type, alias: stawki_vat }
StawkiVatType.php
class StawkiVatType extends AbstractType
{
private $stawkiVatChoices;
public function __construct(array $stawkiVatChoices) {
$this->stawkiVatChoices = $stawkiVatChoices;
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'choices' => $this->stawkiVatChoices,
));
}
public function getParent()
{
return 'choice';
}
public function getName()
{
return 'stawki_vat';
}
}
TowarType.php
class TowarType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('nazwa_towar', null, array('label' => 'Nazwa towaru/usługi'))
->add('cena_netto', null, array(
'label' => 'Cena netto'
))
->add('vat', 'stawki_vat', array(
'attr' => array('class' => 'styled'),
'label' => 'Stawka VAT',
))
;
}
In form, everything works perfect.
But now, I want to get a value stored in database (key of stawki_vat) and show a value of stawki_vat array.
How to achieve this in simple way?
You need to pass your entity manager to your custom form type service. Presuming you are using Doctrine:
services.yml
services:
acme.form.type.stawki_vat:
class: Acme\FakturyBundle\Form\Type\StawkiVatType
arguments: ["#doctrine.orm.entity_manager","%stawki_vat%"]
StawkiVatType.php
class StawkiVatType extends AbstractType
{
private $stawkiVatChoices;
private $em;
public function __construct(EntityManager $em, array $stawkiVatChoices) {
$this->em = $em;
$this->stawkiVatChoices = $stawkiVatChoices;
}
// ...

Categories