Как реализовать простую форму регистрации

Создать форму регистрации достаточно просто - это действительно значит просто создать форму, которая будет обновлять некоторый объект модели User (сущность Doctrine в этом примере) и потом сохранять его.

Для начала, убедитесь, что у вас установлены все необходимые зависимости:

1
$ composer require doctrine form security validator

Tip

Популярный FOSUserBundle предоставляет форму регистрации, форму сброса пароля и другой функционал управления пользователем.

Если у вас еще нет сущности User и работающей системы входа, начните с провайдера сущностей.

Ваша сущность User скорее всего будет иметь хотя бы одно из следущих полей:

username
Будет использовано для выполнения входа, разве что вы вместо этого захотите, чтобы ваш пользователь выполнял вход через электронную почту (в таком случае, это поле не нужно).
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
 96
 97
 98
 99
100
// src/Entity/User.php
namespace App\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;

    /**
     * Нижеуказанная длина зависит от "алгоритма", который вы используете для шифрования
     * пароля, но это также хорошо работает с bcrypt.
     *
     * @ORM\Column(type="string", length=64)
     */
    private $password;

    // другие свойства и методы

    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 getPassword()
    {
        return $this->password;
    }

    public function setPassword($password)
    {
        $this->password = $password;
    }

    public function getSalt()
    {
        // Алгоритмы bcrypt и argon2i не требуют отдельной соли.
        // Вам *может* понадобиться настоящая соль, если вы выберете другой кодировшик.
        return null;
    }

    // другие методы, включая методы безопасности вроде getRoles()
}

Класс UserInterface требует несколько других методов и ваш файл security.yml должен быть сконфигурирован соответственно, чтобы работать с сущностью User. Для более полного примера, см. статью Провайдер сущностей.

Отметьте, что поле plainPassword имеет максимальную длину в 4096 символов. По соображениям безопасности (CVE-2013-5750), Symfony ограничивает длину простого пароля до 4096 символов при шифровании. Добавление этого ограничения помогает убедиться в том, что ваша форма будет выдавать ошибку валидации, если кто-либо попробует использовать очень длинный пароль.

Вам понадобится добавлять это ограничение везде в вашем приложении, где ваш пользователь отправляет нешифрованный пароль (например, форма смены пароля). Единственное место, где вам не нужно беспокоиться об этом - это ваша форма входа, так как компонент безопасности Symfony заботится об этом за вас.

Создание формы для сущности

Залее, создайте форму для сущности 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
34
// src/Form/UserType.php
namespace App\Form;

use App\Entity\User;
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' => User::class,
        ));
    }
}

Существуют всего три поля: email, username и plainPassword (повторяемое, чтобы подтвердить введенный пароль).

Tip

Чтобы изучить больше вещей о компонентне Формы, прочтите справочник Forms guide.

Управление отправкой форм

Далее вам понадобится контроллер, чтобы управлять отображением и оправкой формы. Если форма отправлена, контроллер выполняет валидацию и сохраняет данные в БД:

 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/Controller/RegistrationController.php
namespace App\Controller;

use App\Form\UserType;
use App\Entity\User;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;

class RegistrationController extends Controller
{
    /**
     * @Route("/register", name="user_registration")
     */
    public function registerAction(Request $request, UserPasswordEncoderInterface $passwordEncoder)
    {
        // 1) постройте форму
        $user = new User();
        $form = $this->createForm(UserType::class, $user);

        // 2) обработайте отправку (произойдёт только в POST)
        $form->handleRequest($request);
        if ($form->isSubmitted() && $form->isValid()) {

            // 3) Зашифруйте пароль (вы также можете сделать это через слушатель Doctrine)
            $password = $passwordEncoder->encodePassword($user, $user->getPlainPassword());
            $user->setPassword($password);

            // 4) сохраните Пользователя!
            $em = $this->getDoctrine()->getManager();
            $em->persist($user);
            $em->flush();

            // ... сделайте любую другую работу - вроде отправки письма и др
            // может, установите "флеш" сообщение об успешном выполнении для пользователя

            return $this->redirectToRoute('replace_with_some_route');
        }

        return $this->render(
            'registration/register.html.twig',
            array('form' => $form->createView())
        );
    }
}

Чтобы определить алгоритм, использованный для шифрования пароля в 3-ем шаге, сконфигурируйте кодировщик в конфигурации безопасности:

  • YAML
    1
    2
    3
    4
    # config/packages/security.yaml
    security:
        encoders:
            App\Entity\User: bcrypt
    
  • XML
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    <!-- config/packages/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="App\Entity\User">bcrypt</encoder>
        </config>
    </srv:container>
    
  • PHP
    1
    2
    3
    4
    5
    6
    7
    8
    // config/packages/security.php
    use App\Entity\User;
    
    $container->loadFromExtension('security', array(
        'encoders' => array(
            User::class => 'bcrypt',
        ),
    ));
    

В этом случае, используется рекомендованный алгоритм bcrypt. Если нужно, просмотрите статью шифрование пользовательских паролей.

Далее, создайте шаблон:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
{# templates/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) }}

См. How to Customize Form Rendering, чтобы узнать больше.

Обновление вашей схемы БД

Если во время этого туториала вы обновляли сущность User, то вам нужно обновить вашу схему БД, используя эту команду:

1
2
$ php bin/console doctrine:migrations:diff
$ php bin/console doctrine:migrations:migrate

Вот и всё! Переходите к /register, чтобы опробовать!

Форма регистрации только с электронной почты (без имени пользователя)

Если вы хотите, чтобы ваши пользователи выполняли вход через электронную почту, и вам не нужно имя пользователя, то вы можете полностью удалить его из вашей сущности User. Вместо этого, заставьте getUsername() возвращать свойство``email``:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// src/Entity/User.php
// ...

class User implements UserInterface
{
    // ...

    public function getUsername()
    {
        return $this->email;
    }

    // ...
}

Далее, просто обновите раздел``providers`` вашего файла security.yml, чтобы Symfony знала, как загружать ваших пользователей через свойство email в качестве логина. См. Аутентификация с пользовательским провайдером сущностей.

Добавление поля (чекбокса) "принятие условий"

Иногда вам может захотеться добавить поле "Принимаете ли вы условия" в вашу форму регистрации. Фокус в том, что вы хотите добавить это поле к форме, не добавляя нового ненужного свойства termsAccepted к вашей сущности User, так как оно вам никогда не понадобится.

Чтобы сделать это, добавьте в вашу форму поле termsAccepted, но установите его опцию отображение как false:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
// src/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(),
            ))
        );
    }
}

Опция ограничений также может быть использована, что позволяет нам добавлять валидацию, несмотря на то, что у User нет свойства termsAccepted.

Эта документация является переводом официальной документации Symfony и предоставляется по свободной лицензии CC BY-SA 3.0.