在 Symfony 中向后重定向两步
Redirect two steps back in Symfony
使用 Symfony 4,我有一个编辑实体的路径,在提交后将用户重定向到另一个路径,如下所示:
if ($form->isSubmitted() && $form->isValid()) {
//....
$em->persist($thisEntity);
$em->flush();
return $this->redirectToRoute('this_entity_index');
}
在通过路由 this_entity_index
呈现的实体的 index page
上,我显示了用于编辑实体的按钮,因此这种重定向很有意义。但是,我对此实体有一个不同的 show page
(通过 show_route
呈现),它还包含一个路由到编辑路径的编辑按钮。编辑后,用户将再次被重定向到实体的 index page
。在这种情况下,我希望发生的情况是必须将用户重定向到 show page
,因此基本上,我需要向后发送用户两步。
如果我使用:
return $this->redirect($request->headers->get('referer'));
用户只是被发送回编辑表单(后退一步)。也许有一个简单的解决方案可以让用户返回两步?这样我就可以将用户发送回 index page
、show page
或用户来自的任何其他页面。我必须使用用户会话吗?任何建议表示赞赏。
在表单中添加隐藏输入:
<input type="hidden" name="referer" value="{{app.request.headers.get('referer')}}"/>
和
if ($form->isSubmitted() && $form->isValid()) {
//....
$em->persist($thisEntity);
$em->flush();
return $this->redirect($request->request->get('referer'));
}
我遇到了同样的问题,我想出了更复杂的解决方案,可以作为 类 方法的形式使用。我刚刚创建了一个重定向字段类型,它可以为我处理一些逻辑并且很容易放入每个表单中。背后的逻辑与 Deniz Aktürk 解决方案中的相同。
<?php
namespace App\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\OptionsResolver\OptionsResolver;
class FieldRedirectType extends AbstractType
{
/**
* @var RequestStack
*/
private $requestStack;
public function __construct(RequestStack $requestStack)
{
$this->requestStack = $requestStack;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'mapped' => false,
]);
}
/**
* {@inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->addEventListener(FormEvents::PRE_SET_DATA, function(FormEvent $event) {
$form = $event->getForm();
$parentName = $form->getParent()->getName();
$currentRequest = $this->requestStack->getCurrentRequest();
$post = $currentRequest->request->get($parentName);
$redirectUrl = isset($post[$form->getName()]) ? $post[$form->getName()] : '';
$referer = $currentRequest->headers->get('referer');
if (empty($redirectUrl) && ! empty($referer)) {
$redirectUrl = $referer;
} else if ($redirectUrl == $referer) {
$redirectUrl = '';
}
$config = $form->getParent()->get($form->getName())->getConfig();
$options = $config->getOptions();
$form->getParent()->add($form->getName(), HiddenType::class, array_replace($options, [
'data' => $redirectUrl,
]))
;
});
}
public function getParent()
{
return HiddenType::class;
}
}
首先,我 "extends" HiddenType
字段使其成为基本字段类型。
然后我使用 PRE_SET_DATA 事件根据先前的请求或 header 将正确的数据设置到我的隐藏字段。如果出现验证错误,我需要从我之前的请求中获取数据。因为在提交这样的表格后,我的推荐人将改变并指向我的 edit/add 表单页面。这就是为什么第一次我需要将我的推荐人保存到隐藏字段中,然后依赖 posted 数据,而不是直接推荐人 header。
在该事件中,您无法从 redirect 字段中检索数据,因为尚未设置表单数据。这就是为什么我需要直接从 post 请求中获取数据。
SF 通过将主表单类型键添加到响应来创建 posts 数组。所以对于 user 表单类型,最终数组是这样的:
array:1 [
"user" => array:9 [
"email" => "email"
"first_name" => "name"
"last_name" => "lastname"
"_redirectUrl" => "where_to_redirect_back"
]
]
您可以通过 $parentName = $form->getParent()->getName();
检索的 user 密钥
现在,根据 posted $redirectUrl
和引荐来源,您可以确定哪个值是正确的。
然后您必须为该字段设置适当的值,但是由于事件的循环,您不能再次使用 setData()
方法。
这就是为什么我们必须以 parent 形式获取字段集的所有初始选项。然后使用 data 选项向该字段注入新值。最后将字段添加到 parent 表单,如果名称已存在,SF 会将其视为替换字段。
最后您可以按如下方式使用该字段:
$form->add('_redirectUrl', FieldRedirectType::class); // inside controller or in other form type class
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$redirectUrl = $form->get('_redirectUrl')->getData();
if (empty($redirectUrl)) { // in case of empty value, redirect to some default place
return $this->redirectToRoute('default_basic_route');
}
return $this->redirect($redirectUrl);
}
如果您要填写的表单不止一个,最后您希望将该用户重定向到他来自的第一个页面,它也可以用作 2 (n) 步后退重定向。您必须将先前 redirectField 的值传递给 redirectField。首先添加的 redirectField 应该没有初始值,所以可以从 referer header.
设置它
使用 Symfony 4,我有一个编辑实体的路径,在提交后将用户重定向到另一个路径,如下所示:
if ($form->isSubmitted() && $form->isValid()) {
//....
$em->persist($thisEntity);
$em->flush();
return $this->redirectToRoute('this_entity_index');
}
在通过路由 this_entity_index
呈现的实体的 index page
上,我显示了用于编辑实体的按钮,因此这种重定向很有意义。但是,我对此实体有一个不同的 show page
(通过 show_route
呈现),它还包含一个路由到编辑路径的编辑按钮。编辑后,用户将再次被重定向到实体的 index page
。在这种情况下,我希望发生的情况是必须将用户重定向到 show page
,因此基本上,我需要向后发送用户两步。
如果我使用:
return $this->redirect($request->headers->get('referer'));
用户只是被发送回编辑表单(后退一步)。也许有一个简单的解决方案可以让用户返回两步?这样我就可以将用户发送回 index page
、show page
或用户来自的任何其他页面。我必须使用用户会话吗?任何建议表示赞赏。
在表单中添加隐藏输入:
<input type="hidden" name="referer" value="{{app.request.headers.get('referer')}}"/>
和
if ($form->isSubmitted() && $form->isValid()) {
//....
$em->persist($thisEntity);
$em->flush();
return $this->redirect($request->request->get('referer'));
}
我遇到了同样的问题,我想出了更复杂的解决方案,可以作为 类 方法的形式使用。我刚刚创建了一个重定向字段类型,它可以为我处理一些逻辑并且很容易放入每个表单中。背后的逻辑与 Deniz Aktürk 解决方案中的相同。
<?php
namespace App\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\OptionsResolver\OptionsResolver;
class FieldRedirectType extends AbstractType
{
/**
* @var RequestStack
*/
private $requestStack;
public function __construct(RequestStack $requestStack)
{
$this->requestStack = $requestStack;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'mapped' => false,
]);
}
/**
* {@inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->addEventListener(FormEvents::PRE_SET_DATA, function(FormEvent $event) {
$form = $event->getForm();
$parentName = $form->getParent()->getName();
$currentRequest = $this->requestStack->getCurrentRequest();
$post = $currentRequest->request->get($parentName);
$redirectUrl = isset($post[$form->getName()]) ? $post[$form->getName()] : '';
$referer = $currentRequest->headers->get('referer');
if (empty($redirectUrl) && ! empty($referer)) {
$redirectUrl = $referer;
} else if ($redirectUrl == $referer) {
$redirectUrl = '';
}
$config = $form->getParent()->get($form->getName())->getConfig();
$options = $config->getOptions();
$form->getParent()->add($form->getName(), HiddenType::class, array_replace($options, [
'data' => $redirectUrl,
]))
;
});
}
public function getParent()
{
return HiddenType::class;
}
}
首先,我 "extends" HiddenType
字段使其成为基本字段类型。
然后我使用 PRE_SET_DATA 事件根据先前的请求或 header 将正确的数据设置到我的隐藏字段。如果出现验证错误,我需要从我之前的请求中获取数据。因为在提交这样的表格后,我的推荐人将改变并指向我的 edit/add 表单页面。这就是为什么第一次我需要将我的推荐人保存到隐藏字段中,然后依赖 posted 数据,而不是直接推荐人 header。
在该事件中,您无法从 redirect 字段中检索数据,因为尚未设置表单数据。这就是为什么我需要直接从 post 请求中获取数据。
SF 通过将主表单类型键添加到响应来创建 posts 数组。所以对于 user 表单类型,最终数组是这样的:
array:1 [
"user" => array:9 [
"email" => "email"
"first_name" => "name"
"last_name" => "lastname"
"_redirectUrl" => "where_to_redirect_back"
]
]
您可以通过 $parentName = $form->getParent()->getName();
检索的 user 密钥
现在,根据 posted $redirectUrl
和引荐来源,您可以确定哪个值是正确的。
然后您必须为该字段设置适当的值,但是由于事件的循环,您不能再次使用 setData()
方法。
这就是为什么我们必须以 parent 形式获取字段集的所有初始选项。然后使用 data 选项向该字段注入新值。最后将字段添加到 parent 表单,如果名称已存在,SF 会将其视为替换字段。
最后您可以按如下方式使用该字段:
$form->add('_redirectUrl', FieldRedirectType::class); // inside controller or in other form type class
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$redirectUrl = $form->get('_redirectUrl')->getData();
if (empty($redirectUrl)) { // in case of empty value, redirect to some default place
return $this->redirectToRoute('default_basic_route');
}
return $this->redirect($redirectUrl);
}
如果您要填写的表单不止一个,最后您希望将该用户重定向到他来自的第一个页面,它也可以用作 2 (n) 步后退重定向。您必须将先前 redirectField 的值传递给 redirectField。首先添加的 redirectField 应该没有初始值,所以可以从 referer header.
设置它