I am trying to create a login/registration form using FOSUserBundle. After logging in the user gets a homepage. wherein he has to choose two different events for 2 time slots from two radio button type options and hit submit. Also if a user has registered already and logs in, then he can see his previously selected choices. Also he can change them. When I created the homepage from inside the controller, the code worked fine.
Here is the controller code:
<?php
namespace AppBundle\Controller;
use AppBundle\Entity\events;
//use AppBundle\Entity\eventtype;
use AppBundle\Entity\users;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
class DefaultController extends Controller {
/**
* @Route("/home", name="homepage")
*/
public function indexAction(Request $request) {
$events = new events();
$greet = 'Welcome to the Birthday Party registration!';
$selection1 = '';
$selection2 = '';
$et1 = 0;
$et2 = 0;
//get the events repository
$repository = $this->getDoctrine()->getRepository('AppBundle:events');
//get the user_id of the logged in user
$user = $this->container->get('security.context')->getToken()->getUser();
$events->setUser($user);
$x = $events->getUser()->getID();
//check if the user has already registered or not
$y = $repository->findOneBy(array('user' => $x));
//If the user has registered already, set the data value for the form
if($y){
$et1 = $y->getET1();
$et2 = $y->getET2();
}
//create form
$form = $this->createFormBuilder($events)
->add('eT1', ChoiceType::class, array(
'choices' => array(
'Poker' => 1,
'Chess' => 2,
'Cricket' => 3,
'Marbles' => 4,
'Football' => 5,
),
'choices_as_values' => true,
'expanded' => true,
'multiple' => false,
'label' => 'Choose After Breakfast Event',
'data' => $et1
))
->add('eT2', ChoiceType::class, array(
'choices' => array(
'Poker' => 1,
'Chess' => 2,
'Cricket' => 3,
'Marbles' => 4,
'Football' => 5,
),
'choices_as_values' => true,
'expanded' => true,
'multiple' => false,
'label' => 'Choose After Snacks Event',
'data' => $et2
))
->add('save', SubmitType::class, array('label' => 'Submit'))
->getForm();
//retrieve the choices array for eT1 and eT2
$eT1Choices = $form->get('eT1')->getConfig()->getOption('choices');
$eT2Choices = $form->get('eT2')->getConfig()->getOption('choices');
//intialize the eventname variables
$eT1Name = '';
$eT2Name = '';
if ($y) {
//If the user has registered already, display his previously selected options
$selection1 = 'Your After Breakfast event:';
$selection2 = 'Your After Snacks event:';
//set the eventname based on the value of et1
foreach ($eT1Choices as $key => $value) {
if ($et1 == $value) {
$eT1Name = $key;
}
}
//set the eventname based on the value of et2
foreach ($eT2Choices as $key => $value) {
if ($et2 == $value) {
$eT2Name = $key;
}
}
}
//after submission
if ($request->isMethod('POST')) {
$form->submit($request);
//retrieve maxlimit parameters from parameters.yml
$maxPoker = $this->container->getParameter('pokermaxlimit');
$maxChess = $this->container->getParameter('chessmaxlimit');
$maxCricket = $this->container->getParameter('cricketmaxlimit');
$maxMarbles = $this->container->getParameter('marblesmaxlimit');
$maxFootball = $this->container->getParameter('footballmaxlimit');
//initialize $eventMaxLim
$eventMaxLim = 0;
//retrieve form data
$formData = $form->getData();
$ET1 = $formData->getET1();
$ET2 = $formData->getET2();
$selection1 = 'Your After Breakfast event:';
$selection2 = 'Your After Snacks event:';
//set the eventname based on the value of eT1
foreach ($eT1Choices as $key => $value) {
if ($ET1 == $value) {
$eT1Name = $key;
}
}
//set the eventname based on the value of eT2
foreach ($eT2Choices as $key => $value) {
if ($ET2 == $value) {
$eT2Name = $key;
}
}
//check to see if the user has registered the same event for eT1 and eT2
if ($ET1 == $ET2) {
$this->get('session')->getFlashBag()->set('error', 'You have chosen same events for both time slots! Please choose different ones.');
}
//check to see how many users have registered for the opted event(eT1)
$query1 = $repository->createQueryBuilder('p')
->select('count(p)')
->where('p.eT1 = :eT1')
->setParameter('eT1', $ET1)
->getQuery();
$a = $query1->getSingleScalarResult();
//set the $eventMaxLim based on the chosen event for eT1
if ($ET1 == 1) {
$eventMaxLim = $maxPoker;
} else if ($ET1 == 2) {
$eventMaxLim = $maxChess;
} else if ($ET1 == 3) {
$eventMaxLim = $maxCricket;
} else if ($ET1 == 4) {
$eventMaxLim = $maxMarbles;
} else if ($ET1 == 5) {
$eventMaxLim = $maxFootball;
}
//check to see if the after breakfast event (eT1) is full (ie.has reached the maxlimit)
if ($a >= $eventMaxLim) {
$this->get('session')->getFlashBag()->set('error', 'choose another After Breakfast event, this one is full');
}
//check to see how many users have registered for the opted event(eT2)
$query2 = $repository->createQueryBuilder('p')
->select('count(p)')
->where('p.eT2 = :eT2')
->setParameter('eT2', $ET2)
->getQuery();
$b = $query2->getSingleScalarResult();
//set the $eventMaxLim based on the chosen event for eT2
if ($ET2 == 1) {
$eventMaxLim = $maxPoker;
} else if ($ET2 == 2) {
$eventMaxLim = $maxChess;
} else if ($ET2 == 3) {
$eventMaxLim = $maxCricket;
} else if ($ET2 == 4) {
$eventMaxLim = $maxMarbles;
} else if ($ET2 == 5) {
$eventMaxLim = $maxFootball;
}
//check to see if the after snacks event (eT2) is full (ie.has reached the maxlimit)
if ($b >= $eventMaxLim) {
$this->get('session')->getFlashBag()->set('error', 'choose another After Snacks event, this one is full');
}
if (($a < $eventMaxLim) && ($b < $eventMaxLim) && ($ET1 != $ET2)) {
if ($form->isValid()) {
//get the entity manager
$em = $this->getDoctrine()->getManager();
// If the user is registering for the first time (execute the Insert query)
if (!$y) {
$em->persist($events);
$em->flush();
//return $this->redirectToRoute('homepage');
}
//If the user has registered already and want change his registered events (execute the Update query)
else {
$y->setET1($ET1);
$y->setET2($ET2);
$em->persist($y);
$em->flush();
//return $this->redirectToRoute('homepage');
}
}
}
}
return $this->render('default/index.html.twig', array(
'form' => $form->createView(),
'greet' => $greet,
'selection1' => $selection1,
'eT1Name' => $eT1Name,
'selection2' => $selection2,
'eT2Name' => $eT2Name,
));
}
}
Below is the events entity:
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
//use Symfony\Component\Validator\Constraints as Assert;
/**
* events
*
* @ORM\Table(name="events")
* @ORM\Entity(repositoryClass="AppBundle\Repository\eventsRepository")
*/
class events {
/**
* @var int
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* @var int
*
* @ORM\Column(name="ET1", type="integer")
*/
protected $eT1;
/**
* @var int
*
* @ORM\Column(name="ET2", type="integer")
*/
protected $eT2;
/**
* @ORM\OneToOne(targetEntity="users", inversedBy="event")
* @ORM\JoinColumn(name="user_id", referencedColumnName="id")
*/
protected $user;
/**
* Get id
*
* @return integer
*/
public function getId() {
return $this->id;
}
/**
* Set user
*
* @param users $user
* @return events
*/
public function setUser($user) {
$this->user = $user;
return $this;
}
/**
* Get user
*
* @return events
*/
public function getUser() {
return $this->user;
}
/**
* Set eT1
*
* @param integer $eT1
* @return events
*/
public function setET1($eT1) {
$this->eT1 = $eT1;
return $this;
}
/**
* Get eT1
*
* @return integer
*/
public function getET1() {
return $this->eT1;
}
/**
* Set eT2
*
* @param integer $eT2
* @return events
*/
public function setET2($eT2) {
$this->eT2 = $eT2;
return $this;
}
/**
* Get eT2
*
* @return integer
*/
public function getET2() {
return $this->eT2;
}
}
But when I shifted the code for Form creation in the eventsType.php, The following error has been showing up - A form can only be submitted once 500 Internal Server Error - AlreadySubmittedException Here is the new controller code:
<?php
namespace AppBundle\Controller;
use AppBundle\Entity\events;
use AppBundle\Form\eventsType;
use AppBundle\Entity\users;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
class DefaultController extends Controller {
/**
* @Route("/home", name="homepage")
*/
public function indexAction(Request $request) {
$greet = 'Welcome to the Birthday Party registration!';
$selection1 = '';
$selection2 = '';
//get the events repository
$repository = $this->getDoctrine()->getRepository('AppBundle:events');
//get the user_id of the logged in user
$user = $this->container->get('security.context')->getToken()->getUser()->getID();
//check if the user has already registered or not
$regEvents = $repository->findOneBy(array('user' => $user));
$events = new events();
//create form
$form = $this->createForm(new \AppBundle\Form\eventsType($regEvents), $events);
$form->handleRequest($request);
//retrieve the choices array for eT1 and eT2
$eT1Choices = $form->get('eT1')->getConfig()->getOption('choices');
$eT2Choices = $form->get('eT2')->getConfig()->getOption('choices');
//intialize the eventname variables
$eT1Name = '';
$eT2Name = '';
if ($regEvents) {
$et1 = $regEvents->getET1();
$et2 = $regEvents->getET2();
//If the user has registered already, display his previously selected options
$selection1 = 'Your After Breakfast event:';
$selection2 = 'Your After Snacks event:';
//set the eventname based on the value of et1
foreach ($eT1Choices as $key => $value) {
if ($et1 == $value) {
$eT1Name = $key;
}
}
//set the eventname based on the value of et2
foreach ($eT2Choices as $key => $value) {
if ($et2 == $value) {
$eT2Name = $key;
}
}
}
//after submission
if ($request->isMethod('POST')) {
$form->submit($request);
//First check the value entered by the user
if ($events->getET1() == null || $events->getET2() == null) {
//User did not choose both the events
$this->container->get('session')->getFlashBag()->add('error', 'Oh oh! It is mandatory to choose an option for all the events');
//return array('form' => $form->createView());
}
//retrieve maxlimit parameters from parameters.yml
$maxPoker = $this->container->getParameter('pokermaxlimit');
$maxChess = $this->container->getParameter('chessmaxlimit');
$maxCricket = $this->container->getParameter('cricketmaxlimit');
$maxMarbles = $this->container->getParameter('marblesmaxlimit');
$maxFootball = $this->container->getParameter('footballmaxlimit');
//initialize $eventMaxLim
$eventMaxLim1 = 0;
$eventMaxLim2 = 0;
//retrieve form data
$formData = $form->getData();
$ET1 = $formData->getET1();
$ET2 = $formData->getET2();
$selection1 = 'Your After Breakfast event:';
$selection2 = 'Your After Snacks event:';
//set the eventname based on the value of eT1
foreach ($eT1Choices as $key => $value) {
if ($ET1 == $value) {
$eT1Name = $key;
}
}
//set the eventname based on the value of eT2
foreach ($eT2Choices as $key => $value) {
if ($ET2 == $value) {
$eT2Name = $key;
}
}
//check to see if the user has registered the same event for eT1 and eT2
if ($ET1 == $ET2) {
$this->get('session')->getFlashBag()->set('error', 'You have chosen same events for both time slots! Please choose different ones.');
}
//check to see how many users have registered for the opted event(eT1)
$query1 = $repository->createQueryBuilder('p')
->select('count(p)')
->where('p.eT1 = :eT1')
->setParameter('eT1', $ET1)
->getQuery();
$a = $query1->getSingleScalarResult();
//set the $eventMaxLim based on the chosen event for eT1
if ($ET1 == 1) {
$eventMaxLim1 = $maxPoker;
} else if ($ET1 == 2) {
$eventMaxLim1 = $maxChess;
} else if ($ET1 == 3) {
$eventMaxLim1 = $maxCricket;
} else if ($ET1 == 4) {
$eventMaxLim1 = $maxMarbles;
} else if ($ET1 == 5) {
$eventMaxLim1 = $maxFootball;
}
// var_dump($eventMaxLim1);
// exit;
//check to see if the after breakfast event (eT1) is full (ie.has reached the maxlimit)
if ($a >= $eventMaxLim1) {
$this->get('session')->getFlashBag()->set('error', 'choose another After Breakfast event, this one is full');
}
//check to see how many users have registered for the opted event(eT2)
$query2 = $repository->createQueryBuilder('p')
->select('count(p)')
->where('p.eT2 = :eT2')
->setParameter('eT2', $ET2)
->getQuery();
$b = $query2->getSingleScalarResult();
//set the $eventMaxLim based on the chosen event for eT2
if ($ET2 == 1) {
$eventMaxLim2 = $maxPoker;
} else if ($ET2 == 2) {
$eventMaxLim2 = $maxChess;
} else if ($ET2 == 3) {
$eventMaxLim2 = $maxCricket;
} else if ($ET2 == 4) {
$eventMaxLim2 = $maxMarbles;
} else if ($ET2 == 5) {
$eventMaxLim2 = $maxFootball;
}
//check to see if the after snacks event (eT2) is full (ie.has reached the maxlimit)
if ($b >= $eventMaxLim2) {
$this->get('session')->getFlashBag()->set('error', 'choose another After Snacks event, this one is full');
}
if (($a < $eventMaxLim1) && ($b < $eventMaxLim2) && ($ET1 != $ET2) && ($events->getET1() == null ||
$events->getET2() == null)) {
if ($form->isValid()) {
//get the entity manager
$em = $this->getDoctrine()->getManager();
// If the user is registering for the first time (execute the Insert query)
if (!$regEvents) {
$events->setUser($user);
$events->setET1($ET1);
$events->setET2($ET2);
$em->persist($events);
$em->flush();
//return $this->redirectToRoute('homepage');
}
//If the user has registered already and want change his registered events (execute the Update query)
else {
$events->setET1($ET1);
$events->setET2($ET2);
$em->persist($events);
$em->flush();
//return $this->redirectToRoute('homepage');
}
}
}
}
return $this->render('default/index.html.twig', array(
'form' => $form->createView(),
'greet' => $greet,
'selection1' => $selection1,
'eT1Name' => $eT1Name,
'selection2' => $selection2,
'eT2Name' => $eT2Name,
));
}
}
Below is the eventsType.php:
<?php
namespace AppBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
class eventsType extends AbstractType {
protected $events;
public function __construct($events) {
$this->events = $events;
}
public function buildForm(FormBuilderInterface $builder, array $options) {
if (!empty($this->events)){
if($this->events->getET1() == null){
$et1 = '';
}
else {
$et1 = $this->events->getET1();
}
if($this->events->getET2() == null){
$et2 = '';
}
else {
$et2 = $this->events->getET2();
}
}
else {
$et1 = '';
$et2 = '';
}
$builder->add('eT1', ChoiceType::class, array(
'choices' => array(
'Poker' => 1,
'Chess' => 2,
'Cricket' => 3,
'Marbles' => 4,
'Football' => 5,
),
'choices_as_values' => true,
'expanded' => true,
'multiple' => false,
'label' => 'Choose After Breakfast Event',
'data' => $et1,
// 'mapped' => $map1,
))
->add('eT2', ChoiceType::class, array(
'choices' => array(
'Poker' => 1,
'Chess' => 2,
'Cricket' => 3,
'Marbles' => 4,
'Football' => 5,
),
'choices_as_values' => true,
'expanded' => true,
'multiple' => false,
'label' => 'Choose After Snacks Event',
'data' => $et2,
// 'mapped' => $map2,
))
->add('save', SubmitType::class, array('label' => 'Submit'));
}
public function configureOptions(OptionsResolver $resolver) {
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\events',
));
}
}
remove $form->submit($request);
from your code and that should stop this error message.
$form->submit($request);
(now deprecated) is used to submit the form from your controller, in your case you're using
$form->handleRequest($request);
AND $form->submit($request);
so as soon as the user presses the submit button submit() is called which again attempts to submit the form and hence the error message "A form can only be submitted once" link to docs
side note:
if ($events->getET1() == null || $events->getET2() == null) {
//User did not choose both the events
$this->container->get('session')->getFlashBag()->add('error', 'msg');
//return array('form' => $form->createView());
}
this if condition can and should be replaced by asserts docs and while flash messages are awesome and very useful this is not the place to use it, you may have misunderstood the usage of it, its used to display success and failure messages after form submission, NOT for validation messages, we have assert messages for that
and also read about MVC patterns and 'separation of concerns', divide your code into parts, one with code that displays content to the user, one that interacts with your database, one that does all the logical processing, the controller is definitely NOT the place to do any of this. the controller must be small most of the time and must make use of the built in functionalities of the framework.
i would suggest to take a break from writing codes and start reading about software architecture, and design patterns, with the current way i dont see much space for progress
-dheeraj