[英]redirect to next/route action, not the last after user login/registration symfony security
我在視圖中有這樣的形式:
{{ form_start(form, {'action': path('process_route'), 'method': 'POST'}) }}
{# some other fields #}
{{ form_row(form.placeOrder) }}
{{ form_row(form.saveOrder) }}
{{ form_end(form) }}
如您所見,我有兩個提交按鈕,但這不是問題,我使用symfony方法:
if ($form->isValid()) {
// ... do something
// the save_and_add button was clicked
if ($form->get('placeOrder')->isClicked()) {
// probably redirect to the add page again
}
// redirect to the show page for the just submitted item
}
如您所見,我的表單有一個名為'process_route'
的特定操作流程路由。 提交表單時,無論用戶單擊了提交按鈕如何,我都使用security.context
檢查用戶是否連接。
如果用戶已連接,則在單擊兩個提交按鈕之一時,我將重定向至他調用的路由。
但是它沒有連接用戶,我首先在user registration
(如果尚未注冊)或用戶登錄表單上重定向。
用戶注冊或登錄后,我需要重定向到他單擊兩個提交按鈕之一時首先詢問的路由。
實際上,重定向工作良好,除了在上一個請求的操作( 例如 , 在流程路由表單操作 (此處為'process_route'
))上執行重定向外,我需要重定向到下一步,而不是最后一步。
在我的security.yml中,我有:
firewalls:
main:
pattern: ^/
form_login:
provider: custom
csrf_provider: security.csrf.token_manager # Use form.csrf_provider instead for Symfony <2.4
use_referer : true
在用戶需要執行的操作之后,如何通過單擊兩個提交按鈕表單之一而不是最后一個操作來管理重定向?
在請求中手動設置referer
。
嘗試直接從您的控制器執行此操作,例如:
if ($form->get('placeOrder')->isClicked()) {
$request = $this->getRequest();
$request->headers->set('referer', $this->generateUrl('expected_route'));
}
我的疑問是該請求不會在下一次更新。
在這種情況下,請查找RequestListener
並找到一種根據條件設置良好referer
的方法。
更新
對於登錄,請覆蓋默認登錄表單,並添加以下字段:
<input type="hidden" name="_target_path" value="{{ app.request.headers.get('referer') }}" />
在所有情況下,請不要忘記從控制器手動設置引薦來源網址。
更新2
由於在登錄表單中未正確更新referer
,因此我將嘗試為您提供一種快速的替代方法。
如果我理解正確,那么您是從控制器手動呈現登錄表單。
呈現表單時,將一個附加參數傳遞給視圖:
return $this->render('UserBundle:Security:login.html.twig, array(
'form' => $form->createView(),
// ...
'_target_path' => $this->generateUrl('expected_referer'), // Defined from your condition depending on button clicked
);
我找到了解決方案,但我想確定這是最佳實踐。
請注意,此重定向必須僅在應用程序中的特定時刻執行,而不必在所有網站中執行。 因此,我決定創建一個服務偵聽器。
實際上,當會話具有我命名為êntity的特定實體對象時,必須執行重定向。
拳頭,這是app/config/services.yml
:
# apply the redirect response for connected user when and only where $isEntitySession !=null
# to recover the current request in a service we need to set the scope argument, then call the symfony service container where $request is stored
# just understand that it is not recommanded (bad structure and mad practice) to apply directly the request in service without service container
my_bundle.login_listener:
class: MySpace\MyBundle\EventListener\LoginListener
tags:
- { name: kernel.event_listener, event: security.interactive_login, method: onSecurityInteractiveLogin }
scope: request
arguments: ["@router","@security.context","@event_dispatcher","@service_container"]
然后是MySpace/MyBundle/EventListener/LoginListener.php
:
<?php
namespace MySpace\MyBundle\EventListener;
use Symfony\Component\Routing\Router;
use Symfony\Component\Security\Core\SecurityContext;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\DependencyInjection\ContainerInterface;
class LoginListener
{
protected $router;
protected $security;
protected $dispatcher;
protected $container;
public function __construct(Router $router, SecurityContext $security, EventDispatcherInterface $dispatcher, ContainerInterface $container)
{
$this->router = $router;
$this->security = $security;
$this->dispatcher = $dispatcher;
$this->container = $container;
}
public function onSecurityInteractiveLogin(InteractiveLoginEvent $event)
{
$this->dispatcher->addListener(KernelEvents::RESPONSE, array($this, 'onKernelResponse'));
}
public function onKernelResponse(FilterResponseEvent $event)
{
$request = $this->container->get('request');
$session = $this->container->get('session');
$isEntitySession = $session->get('entity_form') != null;
if($isEntitySession && $request->isMethod('POST') ) {
$entity = $session->get('entity_form');
}
if(isset($entity)) {
if ($entity->getState() == 'true') { //here the state is a bool, don't search anymore about this, it's just an entity object relation set to true or false. Following the state, I return a different view.
$response = new RedirectResponse($this->router->generate('place_order_route'));
} else {
$response = new RedirectResponse($this->router->generate('save_order_route'));
}
$event->setResponse($response);
}
}
}
services.yml
中的所有注釋都可以幫助您了解Service容器的應用。 symfony文檔告訴您不要在服務聲明中直接使用Request。 不要猶豫告訴我是否做對了。
請注意,我使用了security.context,實際上,我想通過恢復用戶數據來使用它,但是作為此處的目的,不需要使用它,因此我們可以刪除此argument
和__construct()
的變量。
為了找出單擊了哪個按鈕,我使用Entity getState()
方法。 確實,遵循此狀態,重定向是不同的。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.