支付宝扫一扫付款
微信扫一扫付款
(微信为保护隐私,不显示你的昵称)
创建一个注册表单是非常容易的 - 它事实上意味着,你只需要创建一个表单,表单将更新一些User
的模型对象(这个例子是一个Doctrine实体)并保存它。
受欢迎的FOSUserBundle 提供了一个注册表单,重置密码表单和其他用户管理功能。
如果你先前没有一个User
实体和能工作的登录系统,你要先从怎样从数据库加载安全用户开始。
你的User
实体至少应该有以下字段:
username
他是用来登录的,除非你想用email来替代你的用户(在那种情况下,这个字段就不是必要的了)。
email
这是一条不错的信息,很值得收集。您也可以允许用户通过email登录。
password
编译的密码
plainPassword
这个字段不会被持久化:(注意没有上面的@ORM\Column
)。他将临时存储注册表单的明文密码。此字段可以被验证,然后被用于password
字段的填充。
添加了一些验证,你的类可能看起来像这样:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 | // src/AppBundle/Entity/User.php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Security\Core\User\UserInterface;
/**
* @ORM\Entity
* @UniqueEntity(fields="email", message="Email already taken")
* @UniqueEntity(fields="username", message="Username already taken")
*/
class User implements UserInterface
{
/**
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @ORM\Column(type="string", length=255, unique=true)
* @Assert\NotBlank()
* @Assert\Email()
*/
private $email;
/**
* @ORM\Column(type="string", length=255, unique=true)
* @Assert\NotBlank()
*/
private $username;
/**
* @Assert\NotBlank()
* @Assert\Length(max=4096)
*/
private $plainPassword;
/**
* The below length depends on the "algorithm" you use for encoding
* the password, but this works well with bcrypt.
*
* @ORM\Column(type="string", length=64)
*/
private $password;
// other properties and methods
public function getEmail()
{
return $this->email;
}
public function setEmail($email)
{
$this->email = $email;
}
public function getUsername()
{
return $this->username;
}
public function setUsername($username)
{
$this->username = $username;
}
public function getPlainPassword()
{
return $this->plainPassword;
}
public function setPlainPassword($password)
{
$this->plainPassword = $password;
}
public function setPassword($password)
{
$this->password = $password;
}
public function getSalt()
{
// The bcrypt algorithm doesn't require a separate salt.
// You *may* need a real salt if you choose a different encoder.
return null;
}
// other methods, including security methods like getRoles()
} |
UserInterface要求要有一些其他的方法,并且你的security.yml
文件需要被正确配置,来让User
实体工作。更多完整的例子,参见实体提供器文章。
下一步,给User
实体创建表单:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | // src/AppBundle/Form/UserType.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\EmailType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\RepeatedType;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
class UserType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('email', EmailType::class)
->add('username', TextType::class)
->add('plainPassword', RepeatedType::class, array(
'type' => PasswordType::class,
'first_options' => array('label' => 'Password'),
'second_options' => array('label' => 'Repeat Password'),
)
);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\User',
));
}
} |
这里有两个字段:email
, username
和 plainPassword
(重复确认输入的密码)。
探索更多关于表单组件的事情,请阅读表单指南。
下一步,你需要一个控制器去处理表单渲染和提交。如果表单被提交,控制器执行验证并保存数据到数据库:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | // src/AppBundle/Controller/RegistrationController.php
namespace AppBundle\Controller;
use AppBundle\Form\UserType;
use AppBundle\Entity\User;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
class RegistrationController extends Controller
{
/**
* @Route("/register", name="user_registration")
*/
public function registerAction(Request $request)
{
// 1) build the form
$user = new User();
$form = $this->createForm(UserType::class, $user);
// 2) handle the submit (will only happen on POST)
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
// 3) Encode the password (you could also do this via Doctrine listener)
$password = $this->get('security.password_encoder')
->encodePassword($user, $user->getPlainPassword());
$user->setPassword($password);
// 4) save the User!
$em = $this->getDoctrine()->getManager();
$em->persist($user);
$em->flush();
// ... do any other work - like sending them an email, etc
// maybe set a "flash" success message for the user
return $this->redirectToRoute('replace_with_some_route');
}
return $this->render(
'registration/register.html.twig',
array('form' => $form->createView())
);
}
} |
在安全配置中配置上面步骤3的编码器,来定义用于编译密码的算法:
1 2 3 4 | # app/config/security.yml
security:
encoders:
AppBundle\Entity\User: bcrypt |
1 2 3 4 5 6 7 8 9 10 11 | <!-- app/config/security.xml -->
<?xml version="1.0" charset="UTF-8" ?>
<srv:container xmlns="http://symfony.com/schema/dic/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:srv="http://symfony.com/schema/dic/services"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<config>
<encoder class="AppBundle\Entity\User">bcrypt</encoder>
</config>
</srv:container> |
这个案例我们推荐使用bcrypt
算法。了解更多关于如何编码用户密码的细节请看安全章节。
如果您决定不使用注释方式的路由(如上),那么你需要创建一个这个控制器的路由:
1 2 3 4 | # app/config/routing.yml
user_registration:
path: /register
defaults: { _controller: AppBundle:Registration:register } |
1 2 3 4 5 6 7 8 9 10 | <!-- app/config/routing.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<routes xmlns="http://symfony.com/schema/routing"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd">
<route id="user_registration" path="/register">
<default key="_controller">AppBundle:Registration:register</default>
</route>
</routes> |
1 2 3 4 5 6 7 8 9 10 | // app/config/routing.php
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route;
$collection = new RouteCollection();
$collection->add('user_registration', new Route('/register', array(
'_controller' => 'AppBundle:Registration:register',
)));
return $collection; |
下一步,创建模板:
1 2 3 4 5 6 7 8 9 10 | {# app/Resources/views/registration/register.html.twig #}
{{ form_start(form) }}
{{ form_row(form.username) }}
{{ form_row(form.email) }}
{{ form_row(form.plainPassword.first) }}
{{ form_row(form.plainPassword.second) }}
<button type="submit">Register!</button>
{{ form_end(form) }} |
1 2 3 4 5 6 7 8 9 10 11 | <!-- app/Resources/views/registration/register.html.php -->
<?php echo $view['form']->start($form) ?>
<?php echo $view['form']->row($form['username']) ?>
<?php echo $view['form']->row($form['email']) ?>
<?php echo $view['form']->row($form['plainPassword']['first']) ?>
<?php echo $view['form']->row($form['plainPassword']['second']) ?>
<button type="submit">Register!</button>
<?php echo $view['form']->end($form) ?> |
参见如何自定义表单渲染,里面有更多细节。
如果你在这个教程中已经更新了User
实体,你必须要使用下面的命令去更新数据库结构:
1 | $ php bin/console doctrine:schema:update --force |
就是这样!来到/register
来尝试一下吧!
如果你想要你的用户通过email登录并不需要用户名,那么你可以从你的User
实体中彻底移除他。相反,让getUsername()
返回email
属性:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | // src/AppBundle/Entity/User.php
// ...
class User implements UserInterface
{
// ...
public function getUsername()
{
return $this->email;
}
// ...
} |
下一步,只更改你security.yml
文件的providers
部分,以便Symfony知道如何去通过email
属性加载你的用户来登录。参见如何自定义表单渲染。
有时,你想要一个“你接受这个条款和声明吗?”的Checkbox,出现在你的注册表单。唯一窍门,让你要去添加这个字段到你的表单中,而你永远不需要添加多余的termsAccepted
属性到你的User
实体。
要做到这一点,要添加一个termsAccepted
字段到你的表单,但设置它的 mapped 选项为false
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | // src/AppBundle/Form/UserType.php
// ...
use Symfony\Component\Validator\Constraints\IsTrue;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
class UserType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('email', EmailType::class);
// ...
->add('termsAccepted', CheckboxType::class, array(
'mapped' => false,
'constraints' => new IsTrue(),
))
);
}
} |
constraints配置也被使用了,它允许我们添加验证,尽管没有User
中没有termsAccepted
属性。
本文,包括例程代码在内,采用的是 Creative Commons BY-SA 3.0 创作共用授权。