Symfony5中如何将表单数据同时发送至两个控制器?
如何在Symfony5中将登录表单数据同时发送到两个控制器?
现有代码
登录模板(login/index.html.twig)
{% extends 'base.html.twig' %} {% block title %}Hello LoginController!{% endblock %} {% block body %} {% if error %} <div>{{ error.messageKey|trans(error.messageData, 'security') }}</div> {% endif %} <form action="{{ path('app_login') }}" method="post"> <label for="username">Username:</label> <input type="text" id="username" name="_username" value="{{ last_username }}"/> <label for="password">Password:</label> <input type="password" id="password" name="_password"/> <button type="submit">login</button> </form> {% endblock %}
控制器1(Cita相关控制器)
#[Route('/consulta', name:'cita_consulta')] public function consultas(ManagerRegistry $doctrine) { $username=$_POST['_username']; $citaRepository = new CitaRepository($doctrine); $citas = $citaRepository->findAll(); return $this->render('familia/reservas.html.twig', ['citas' => $citas,'username' => $username]); }
控制器2(登录控制器)
#[Route('/login', name: 'app_login')] public function index(AuthenticationUtils $authenticationUtils): Response { // 获取登录错误(如果有) $error = $authenticationUtils->getLastAuthenticationError(); // 获取用户上次输入的用户名 $lastUsername = $authenticationUtils->getLastUsername(); return $this->render('login/index.html.twig', ['last_username' => $lastUsername,'error'=> $error,]); }
需求说明
需要将登录表单提交的数据同时传递到上述两个控制器,其中控制器1需要获取用户名。
解决方案
原生HTML表单只能提交到一个地址,结合Symfony的安全规范,推荐以下几种实现方式:
方法一:登录成功后跳转并通过认证用户获取用户名(推荐)
Symfony的登录认证由Security组件处理,app_login控制器仅负责渲染页面和错误展示。可以配置登录成功后自动跳转到控制器1的路由,直接从已认证用户对象中获取用户名,无需依赖POST参数。
- 修改
config/packages/security.yaml,配置登录成功跳转目标:
security: # 保留其他原有配置 firewalls: main: # 保留其他原有配置 form_login: login_path: app_login check_path: app_login default_target_path: cita_consulta # 登录成功后跳转到cita_consulta路由
- 优化控制器1,通过Security组件获取已认证用户:
#[Route('/consulta', name:'cita_consulta')] public function consultas(ManagerRegistry $doctrine, Security $security) { // 获取当前已认证的用户对象 $user = $security->getUser(); // 获取用户名(Symfony5标准方法) $username = $user->getUserIdentifier(); // 规范获取Repository的方式,避免手动实例化 $citaRepository = $doctrine->getRepository(Cita::class); $citas = $citaRepository->findAll(); return $this->render('familia/reservas.html.twig', ['citas' => $citas,'username' => $username]); }
方法二:用JavaScript发送双请求(不推荐,需注意安全)
通过阻止表单默认提交行为,用Fetch API分别向两个控制器发送POST请求。注意需确保站点使用HTTPS,避免敏感数据泄露。
- 修改登录模板,添加JavaScript逻辑:
{% extends 'base.html.twig' %} {% block title %}Hello LoginController!{% endblock %} {% block body %} {% if error %} <div>{{ error.messageKey|trans(error.messageData, 'security') }}</div> {% endif %} <form id="loginForm" method="post"> <label for="username">Username:</label> <input type="text" id="username" name="_username" value="{{ last_username }}"/> <label for="password">Password:</label> <input type="password" id="password" name="_password"/> <button type="submit">login</button> </form> {% block javascripts %} <script> document.getElementById('loginForm').addEventListener('submit', async function(e) { e.preventDefault(); const formData = new FormData(this); // 发送请求到登录控制器 await fetch("{{ path('app_login') }}", { method: 'POST', body: formData }); // 发送请求到consulta控制器 await fetch("{{ path('cita_consulta') }}", { method: 'POST', body: formData }); // 跳转至consulta页面 window.location.href = "{{ path('cita_consulta') }}"; }); </script> {% endblock %} {% endblock %}
- 优化控制器1,用Request对象获取POST参数(替代原生
$_POST):
#[Route('/consulta', name:'cita_consulta')] public function consultas(ManagerRegistry $doctrine, Request $request) { // 从Request对象中获取POST参数 $username = $request->request->get('_username'); $citaRepository = $doctrine->getRepository(Cita::class); $citas = $citaRepository->findAll(); return $this->render('familia/reservas.html.twig', ['citas' => $citas,'username' => $username]); }
方法三:登录成功事件监听器
通过监听Symfony的登录成功事件,在用户认证完成后触发控制器1的逻辑或跳转。
- 创建登录成功监听器类:
// src/EventListener/LoginSuccessListener.php namespace App\EventListener; use App\Repository\CitaRepository; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\Routing\RouterInterface; use Symfony\Component\Security\Http\Event\InteractiveLoginEvent; class LoginSuccessListener { private $router; private $citaRepository; public function __construct(RouterInterface $router, CitaRepository $citaRepository) { $this->router = $router; $this->citaRepository = $citaRepository; } public function onInteractiveLogin(InteractiveLoginEvent $event) { $user = $event->getAuthenticationToken()->getUser(); $username = $user->getUserIdentifier(); // 执行Cita相关逻辑(如数据查询、存储) $citas = $this->citaRepository->findAll(); // 修改响应,跳转到consulta路由并携带用户名参数 $response = new RedirectResponse($this->router->generate('cita_consulta', ['username' => $username])); $event->setResponse($response); } }
- 在
config/services.yaml中配置监听器:
services: App\EventListener\LoginSuccessListener: tags: - { name: kernel.event_listener, event: security.interactive_login }
- 修改控制器1,通过GET参数获取用户名:
#[Route('/consulta', name:'cita_consulta')] public function consultas(ManagerRegistry $doctrine, Request $request) { $username = $request->query->get('username'); $citaRepository = $doctrine->getRepository(Cita::class); $citas = $citaRepository->findAll(); return $this->render('familia/reservas.html.twig', ['citas' => $citas,'username' => $username]); }
内容的提问来源于stack exchange,提问作者Miguel Ángel
相关产品推荐
相关产品推荐

