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!
Related
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,
]);
}
}
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',
));
}
}
I have 2 bundles, 1 works with the validation.yml file and one does not.
Both bundles are set up exactly the same, i have googled high and low and i cannot seem to understand why.
I have created a form type here:
<?php
namespace Brs\UserBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
class Usertype extends AbstractType
{
protected $fname;
protected $lname;
protected $email;
protected $mobile;
protected $active;
protected $mentor;
protected $initialized;
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('fname','text',array('label'=>'First Name'))
->add('lname','text',array('label'=>'Last Name'))
->add('email','email',array('label'=>'Email'))
->add('mobile','text',array('label'=>'Mobile'))
->add('active','choice',array(
'label'=>'Active?',
'choices'=>array('0'=>'No','1'=>'Yes'),
'expanded'=>true,
'multiple'=>false
))
->add('mentor','choice',array(
'label'=>'Mentor?',
'choices'=>array('0'=>'No','1'=>'Yes'),
'expanded'=>true,
'multiple'=>false
))
->add('Add Player?','submit');
}
public function getName()
{
return 'user';
}
public function setFname($fname)
{
$this->fname = $fname;
return $this;
}
public function getFname()
{
return $this->fname;
}
public function setLname($lname)
{
$this->lname = $lname;
return $this;
}
public function getLname()
{
return $this->lname;
}
public function setEmail($email)
{
$this->email = $email;
return $this;
}
public function getEmail()
{
return $this->email;
}
public function setMobile($mobile)
{
$this->mobile = $mobile;
return $this;
}
public function getMobile()
{
return $this->mobile;
}
public function setActive($active)
{
$this->active = $active;
return $this;
}
public function getActive()
{
return $this->active;
}
public function setMentor($mentor)
{
$this->mentor = $mentor;
return $this;
}
public function getMentor()
{
return $this->mentor;
}
public function setInitialized($initialized)
{
$this->initialized = $initialized;
return $this;
}
public function getInitialized()
{
return $this->initialized;
}
}
This is my validation.yml in bundle/Resources/config:
Brs\UserBundle\Form\Type\UserType:
properties:
fname:
- NotBlank: ~
- Length:
min: 3
lname:
- NotBlank: ~
- Length:
min: 3
email:
- NotBlank: ~
- Length:
min: 3
This is my controller that builds the form from the class and renders it renders fine:
<?php
namespace Brs\UserBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
// use Symfony\Component\HttpFoundation\Response;
// use Brs\UserBundle\Entity\User;
use Brs\UserBundle\Form\Type\UserType;
class UserController extends Controller{
public function indexAction(Request $request){
$user = new UserType();
$form = $this->createForm(new UserType(),$user);
$form->handleRequest($request);
if($form->isValid()){
echo 'yes';
}
//print_r($user);
return $this->render(
'BrsUserBundle:User:userForm.html.twig',
array(
'title'=>"Add Player",
'form'=>$form->createView()
));
}
}
?>
In app/config/config.yml i have these params set for validation:
validation: { enabled: true, enable_annotations: false }
Now when I submit the form with no data the request hits the controller and the method
$form->isValid();
is called, this returns true.
This should return false as my constraints in my validation.yml file do not allow blank fields to be processed and should trigger the form to render the field errors within the template.
I am clearly missing something here, Any help would be greatly appreciated.
Thanks
Adam
You should not validate your UserType, but the User object itself. Also the second argument of createForm() needs to be a User object.
$user = new User();
$form = $this->createForm(new UserType(),$user);
validation.yml:
Brs\UserBundle\Entity\User:
properties:
# constraint list
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.
I want to create a form in Symfony2, so I followed the tutorial on this site
namespace Project\Foo\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Project\Foo\Entity\Anfrage;
use Symfony\Component\HttpFoundation\Request;
class UploadController extends Controller
{
public function indexAction(Request $request)
{
$anfrage = new Anfrage();
$anfrage->setName('Güntaa');
$anfrage->setAge(5);
$anfrage->setEmail('foo#foo.de');
$form = $this->createFormBuilder($anfrage)
->add('save', 'submit', array('label' => 'Create Task'))
->getForm();
return $this->render(
'Foo:Upload:index.html.twig',
array(
'title' => 'Foo',
'form' => $form->createView(),
));
}
}
In my template I want to call this form:
{{ form_start(form) }}
{{ form_widget(form) }}
{{ form_end(form) }}
But when I call my template, I get the following error:
Validator must be instance of Symfony\Component\Validator\Validator\ValidatorInterface or Symfony\Component\Validator\ValidatorInterface
I don't know, how to solve this problem.
Edit
Here is the Anfrage's entity:
<?php
namespace Project\MarkupConverterBundle\Entity;
class Anfrage {
protected $name;
protected $age;
protected $email;
public function getName()
{
return $this->name;
}
public function setName($name)
{
$this->name = $name;
}
public function getAge()
{
return $this->age;
}
public function setAge($age)
{
$this->age = $age;
}
public function getEmail()
{
return $this->email;
}
public function setEmail($email)
{
$this->$email = $email;
}
}
Edit2
When I try to use the form without a class, I get the same error
namespace Project\Foo\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
class UploadController extends Controller
{
public function indexAction(Request $request)
{
$defaultData = array('message' => 'The message from you');
$form = $this->createFormBuilder($defaultData)
->add('message', 'text')
->add('save', 'submit', array('label' => 'Create Task'))
->getForm();
$form->handleRequest($request);
if ($form->isValid()){
$data = $form->getData();
}
return $this->render(
'Foo:Upload:index.html.twig',
array(
'title' => 'Foo',
'form' => $form->createView(),
));
}
}
Problem solved: My services name was Validator. Not really a good idea. Thank's to all who tried to help.