一个表单只能提交一次 500 Internal Server Error - AlreadySubmittedException

A form can only be submitted once 500 Internal Server Error - AlreadySubmittedException

我正在尝试使用 FOSUserBundle 创建一个 login/registration 表单。登录后,用户将获得一个主页。其中他必须从两个单选按钮类型选项中为 2 个时间段选择两个不同的事件,然后点击提交。此外,如果用户已经注册并登录,则他可以看到他之前选择的选项。他也可以改变它们。当我从控制器内部创建主页时,代码运行良好。

控制器代码如下:

<?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,
        ));
    }

}

下面是事件实体:

<?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;
    }

}

但是当我在 eventsType.php 中移动创建表单的代码时,出现了以下错误 - 表单只能提交一次 500 Internal Server Error - AlreadySubmittedException 这是新的控制器代码:

<?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,
        ));
    }

}

下面是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',
        ));
    }

}

从您的代码中删除 $form->submit($request);,这应该会停止此错误消息。

$form->submit($request);(现已弃用)用于从您的控制器提交表单,在您使用的情况下 $form->handleRequest($request); AND $form->submit($request); 所以一旦用户按下提交按钮 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

旁注:

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());
}

这个 if 条件可以而且应该被断言替换 docs 虽然 flash 消息很棒而且非常有用,但这不是使用它的地方,您可能误解了它的用法,它曾经用于在提交表单后显示成功和失败消息,而不是验证消息,我们有断言消息

并阅读 MVC 模式和 'separation of concerns',将您的代码分成几部分,一个包含向用户显示内容的代码,一个与您的数据库交互的代码,一个执行所有逻辑处理的代码, controller 绝对不是做这些的地方。大多数时候控制器必须很小,并且必须利用框架的内置功能。

我建议从编写代码中休息一下,开始阅读有关软件架构和设计模式的内容,以目前的方式我看不到太多 space 进步

-dheeraj