如何使用表单生成器和表单助手函数创建自定义登录页面以渲染视图?我终于想出了解决方案,以下是我的解决方案:
为什么需要这样做呢?
因为我想使用表单生成器创建Symfony登录页面,并使用表单助手函数来渲染视图。这样,我的字段将始终使用正确的模板(手动编写表单-按照Symfony书籍示例-意味着如果更新主模板,则必须手动更改登录表单中的更改-并可能会忘记!)
如何使用表单生成器和表单助手函数创建自定义登录页面以渲染视图?我终于想出了解决方案,以下是我的解决方案:
为什么需要这样做呢?
因为我想使用表单生成器创建Symfony登录页面,并使用表单助手函数来渲染视图。这样,我的字段将始终使用正确的模板(手动编写表单-按照Symfony书籍示例-意味着如果更新主模板,则必须手动更改登录表单中的更改-并可能会忘记!)
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\CsrfFormLoginBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormError;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Security\Core\Security;
/**
* Form type for use with the Security component's form-based authentication
* listener.
*
* @author Henrik Bjornskov <henrik@bjrnskov.dk>
* @author Jeremy Mikola <jmikola@gmail.com>
*/
class UserLoginType extends AbstractType
{
private $requestStack;
public function __construct(RequestStack $requestStack)
{
$this->requestStack = $requestStack;
}
/**
* {@inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('username', 'Symfony\Component\Form\Extension\Core\Type\TextType')
->add('password', 'Symfony\Component\Form\Extension\Core\Type\PasswordType')
->add('_target_path', 'Symfony\Component\Form\Extension\Core\Type\HiddenType')
;
$request = $this->requestStack->getCurrentRequest();
/* Note: since the Security component's form login listener intercepts
* the POST request, this form will never really be bound to the
* request; however, we can match the expected behavior by checking the
* session for an authentication error and last username.
*/
$builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) use ($request) {
if ($request->attributes->has(Security::AUTHENTICATION_ERROR)) {
$error = $request->attributes->get(Security::AUTHENTICATION_ERROR);
} else {
$error = $request->getSession()->get(Security::AUTHENTICATION_ERROR);
}
if ($error) {
$event->getForm()->addError(new FormError($error->getMessage()));
}
$event->setData(array_replace((array) $event->getData(), array(
'username' => $request->getSession()->get(Security::LAST_USERNAME),
)));
});
}
/**
* {@inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
/* Note: the form's csrf_token_id must correspond to that for the form login
* listener in order for the CSRF token to validate successfully.
*/
$resolver->setDefaults(array(
'csrf_token_id' => 'authenticate',
));
}
}
我使用了不带类的createFormBuilder(参见 Symfony - 不使用类创建表单),这样我就可以仅呈现登录所需的字段:
/**
* @Route("/login", name="login")
*/
public function loginAction(Request $request)
{
if ($this->get('security.authorization_checker')->isGranted('IS_AUTHENTICATED_FULLY')) {
$this->addFlash('warning', 'You are already fully logged in.');
return $this->redirectToRoute('my_homepage');
} else {
$authenticationUtils = $this->get('security.authentication_utils');
$defaultData = array('username' => $authenticationUtils->getLastUsername());
$form = $this->createFormBuilder($defaultData)
->add('username', \Symfony\Component\Form\Extension\Core\Type\TextType::class)
->add('password', \Symfony\Component\Form\Extension\Core\Type\PasswordType::class)
->add('logIn', \Symfony\Component\Form\Extension\Core\Type\SubmitType::class)
->getForm();
if (!is_null($authenticationUtils->getLastAuthenticationError(false))) {
$form->addError(new \Symfony\Component\Form\FormError(
$authenticationUtils->getLastAuthenticationError()->getMessageKey()
));
}
$form->handleRequest($request);
return $this->render('login.html.twig', array(
'form' => $form->createView(),
)
);
}
}
然后我按照通常的方式渲染表单,只不过我使用了full_name
表单Twig变量(参见Symfony - 表单变量参考),以设置正确的_username
和_password
字段名称:
{% extends 'base.html.twig' %}
{% block title %}Log in to {{ app.request.host }}{% endblock %}
{% block body %}
<h1>Account Log In</h1>
{{ form_start(form) }}
{{ form_row(form.username, {'full_name': '_username'}) }}
{{ form_row(form.password, {'full_name': '_password'}) }}
{{ form_errors(form) }}
{{ form_end(form) }}
{% endblock %}
namespace AppBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
class LoginType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('username', TextType::class)
->add('password', PasswordType::class)
->add('logIn', SubmitType::class);
}
}
接着,使用createForm()
来获取您的表单:
$form = $this->createForm(\AppBundle\Form\LoginType::class, $defaultData);
控制器:
public function signInAction(SignInFormFactory $formFactory): Response
{
$form = $formFactory->createForm();
return $this->render('Security/SignIn.html.twig', array(
'form' => $form->createView(),
));
}
表单工厂:
<?php
namespace App\Form\Factory;
use App\Form\SignInForm;
use Symfony\Component\Form\FormError;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
class SignInFormFactory
{
protected $formFactory;
protected $authenticationUtils;
public function __construct(FormFactoryInterface $formFactory, AuthenticationUtils $authenticationUtils)
{
$this->formFactory = $formFactory;
$this->authenticationUtils = $authenticationUtils;
}
public function createForm(): FormInterface
{
$form = $this->formFactory->create(SignInForm::class);
$form->get('_username')->setData($this->authenticationUtils->getLastUsername());
if ($error = $this->authenticationUtils->getLastAuthenticationError()) {
$form->addError(new FormError($error->getMessageKey()));
}
return $form;
}
}
表单类型:
<?php
namespace App\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class SignInForm extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('_username', TextType::class)
->add('_password', PasswordType::class)
->add('_remember_me', CheckboxType::class, array(
'required' => false,
'data' => true,
))
->add('submit', SubmitType::class)
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'csrf_field_name' => '_csrf_token',
'csrf_token_id' => 'authenticate',
));
}
public function getBlockPrefix(): string
{
return '';
}
}