表单只能提交一次 500 内部服务器错误 - 已提交异常


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

我正在尝试使用FOSUserBundle创建一个登录/注册表单。登录后,用户将获得一个主页。其中,他必须从两个单选按钮类型选项中选择 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 内部服务器错误 - 已提交异常以下是新的控制器代码:

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

以下是事件类型.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() 就会调用它再次尝试提交表单,因此会出现错误消息"表单只能提交一次"指向文档的链接

旁注:

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 条件可以而且应该替换为断言文档,虽然 Flash 消息很棒并且非常有用,但这不是使用它的地方,您可能误解了它的用法,它用于在表单提交后显示成功和失败消息,不是用于验证消息,我们有断言消息

并阅读有关MVC模式和"关注点分离"的信息,将代码分成几部分,一个带有向用户显示内容的代码,一个与您的数据库交互,一个执行所有逻辑处理,控制器绝对不是做任何事情的地方。 控制器在大部分时间必须很小,并且必须利用框架的内置功能。

我建议从编写代码中休息一下,开始阅读软件架构和设计模式,以目前的方式,我认为没有太大的进展空间。

-迪拉吉