Buy

That's it for security! We covered authentication and authorization. So, I'm not really sure why I'm still recording.

Oh yea, I remember: let's create a registration form! Actually, this has nothing to do with security: registration is all about creating and saving a User entity. But, there are a few interesting things - call it a bonus round.

Controller, Form, Check!

Start like normal: create a new controller class called UserController - for stuff like registration and maybe future things like reset password:

19 lines src/AppBundle/Controller/UserController.php
... lines 1 - 2
namespace AppBundle\Controller;
... lines 4 - 5
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
... lines 7 - 8
class UserController extends Controller
{
... lines 11 - 17
}

Inside, add registerAction() with the URL /register. Let's call the route user_register:

19 lines src/AppBundle/Controller/UserController.php
... lines 1 - 4
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
... line 6
use Symfony\Component\HttpFoundation\Request;
class UserController extends Controller
{
/**
* @Route("/register", name="user_register")
*/
public function registerAction(Request $request)
{
}
}

Make sure you have your use statement for @Route.

Next, this will be a nice, normal form situation. So click the Form directory, open the new menu, and create a new Symfony Form. Call it UserRegistrationForm:

21 lines src/AppBundle/Form/UserRegistrationForm.php
... lines 1 - 2
namespace AppBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class UserRegistrationForm extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
}
public function configureOptions(OptionsResolver $resolver)
{
}
}

Brilliant! Delete the extra getName() method that's super not needed in Symfony 3.

Now, bind the form to User, with $resolver->setDefaults() and a data_class option to set User::class:

31 lines src/AppBundle/Form/UserRegistrationForm.php
... lines 1 - 4
use AppBundle\Entity\User;
... lines 6 - 12
class UserRegistrationForm extends AbstractType
{
... lines 15 - 23
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => User::class
]);
}
}

Next, the fields! And we need two: first an email field set to EmailType::class:

31 lines src/AppBundle/Form/UserRegistrationForm.php
... lines 1 - 6
use Symfony\Component\Form\Extension\Core\Type\EmailType;
... lines 8 - 12
class UserRegistrationForm extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('email', EmailType::class)
... lines 19 - 21
}
... lines 23 - 29
}

Then, we do need a password field, but think about it: the property we want to set on User is not actually the password property. We need to set plainPassword. Add this. It'll be a password type. But, if you want the user to type the password twice, use a RepeatedType. Then, in the third argument, pass the real type with type set to PasswordType::class:

31 lines src/AppBundle/Form/UserRegistrationForm.php
... lines 1 - 7
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Component\Form\Extension\Core\Type\RepeatedType;
... lines 10 - 12
class UserRegistrationForm extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('email', EmailType::class)
->add('plainPassword', RepeatedType::class, [
'type' => PasswordType::class
]);
}
... lines 23 - 29
}

That'll render two password boxes. And if the values don't match, validation will automatically fail.

Rendering the Form

Form done! In the controller, start with $form = $this->createForm(). And of course, make sure you're extending the Symfony base Controller! Then, pass this UserRegistrationForm::class:

24 lines src/AppBundle/Controller/UserController.php
... lines 1 - 4
use AppBundle\Form\UserRegistrationForm;
... line 6
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
... lines 8 - 9
class UserController extends Controller
{
... lines 12 - 14
public function registerAction(Request $request)
{
$form = $this->createForm(UserRegistrationForm::class);
... lines 18 - 21
}
}

Go straight to the template: return $this->render('user/register.html.twig') and pass it $form->createView():

24 lines src/AppBundle/Controller/UserController.php
... lines 1 - 16
$form = $this->createForm(UserRegistrationForm::class);
return $this->render('user/register.html.twig', [
'form' => $form->createView()
]);
... lines 22 - 24

Ok, all standard!

As a short cut, I'll hover over the template, press Option+Enter and select "Create Template".

You guys know the drill: extends 'base.html.twig' then override the block body. I'll give us just a little bit of markup to get things rolling:

19 lines app/Resources/views/user/register.html.twig
{% extends 'base.html.twig' %}
{% block body %}
<div class="container">
<div class="row">
<div class="col-xs-12">
<h1>Register!</h1>
... lines 8 - 15
</div>
</div>
</div>
{% endblock %}

Rendering the form is exactly how it always is: form_start(form), form_end(form), and inside, form_row(form.email):

19 lines app/Resources/views/user/register.html.twig
... lines 1 - 2
{% block body %}
<div class="container">
<div class="row">
<div class="col-xs-12">
<h1>Register!</h1>
{{ form_start(form) }}
{{ form_row(form.email) }}
... lines 11 - 14
{{ form_end(form) }}
</div>
</div>
</div>
{% endblock %}

Then form_row(form.plainPassword) - but because we used the RepeatedType, this will render as two fields - so use form.plainPassword.first and form_row(form.plainPassword.second):

19 lines app/Resources/views/user/register.html.twig
... lines 1 - 2
{% block body %}
<div class="container">
<div class="row">
<div class="col-xs-12">
<h1>Register!</h1>
{{ form_start(form) }}
{{ form_row(form.email) }}
{{ form_row(form.plainPassword.first) }}
{{ form_row(form.plainPassword.second) }}
... lines 13 - 14
{{ form_end(form) }}
</div>
</div>
</div>
{% endblock %}

Cool, right?

Finally show off your styling skills by adding a <button type="submit"> with some fancy Bootstrap classes. Don't forget the formnovalidate to disable HTML5 validation. And finally say, register:

19 lines app/Resources/views/user/register.html.twig
... lines 1 - 2
{% block body %}
<div class="container">
<div class="row">
<div class="col-xs-12">
<h1>Register!</h1>
{{ form_start(form) }}
{{ form_row(form.email) }}
{{ form_row(form.plainPassword.first) }}
{{ form_row(form.plainPassword.second) }}
<button type="submit" class="btn btn-primary" formnovalidate>Register</button>
{{ form_end(form) }}
</div>
</div>
</div>
{% endblock %}

That oughta do it! Finish things by adding a link to this from the login page. After the button, add a link to path('user_register'):

26 lines app/Resources/views/security/login.html.twig
... lines 1 - 2
{% block body %}
<div class="container">
<div class="row">
<div class="col-xs-12">
... lines 7 - 14
{{ form_start(form) }}
... lines 16 - 17
<button type="submit" class="btn btn-success">Login <span class="fa fa-lock"></span></button>
&nbsp;
<a href="{{ path('user_register') }}">Register</a>
{{ form_end(form) }}
</div>
</div>
</div>
{% endblock %}

Done! Refresh. Click "Register", and we're rendered.

Fixing the Password fields

Ooh - except for the labels: "First" and "Second": those are terrible! We can fix those real quick: pass a variables array to first with label set to Password. For the second one: Repeat Password:

23 lines app/Resources/views/user/register.html.twig
... lines 1 - 2
{% block body %}
<div class="container">
<div class="row">
<div class="col-xs-12">
<h1>Register!</h1>
{{ form_start(form) }}
... line 10
{{ form_row(form.plainPassword.first, {
'label': 'Password'
}) }}
{{ form_row(form.plainPassword.second, {
'label': 'Repeat Password'
}) }}
... lines 17 - 18
{{ form_end(form) }}
</div>
</div>
</div>
{% endblock %}

Refresh. Looking good.

Saving the User

Since the registration form has nothing to do with security, let's just finish this! Type-hint the Request argument, and then do the normal $form->handleRequest($request):

38 lines src/AppBundle/Controller/UserController.php
... lines 1 - 8
use Symfony\Component\HttpFoundation\Request;
class UserController extends Controller
{
... lines 13 - 15
public function registerAction(Request $request)
{
$form = $this->createForm(UserRegistrationForm::class);
$form->handleRequest($request);
... lines 21 - 35
}
}

Then, if($form->isValid()) - to make sure that validation is passed:

38 lines src/AppBundle/Controller/UserController.php
... lines 1 - 10
class UserController extends Controller
{
... lines 13 - 15
public function registerAction(Request $request)
{
$form = $this->createForm(UserRegistrationForm::class);
$form->handleRequest($request);
if ($form->isValid()) {
... lines 22 - 30
}
... lines 32 - 35
}
}

In the forms tutorial, we also added $form->isSubmitted() in the if statement, but you technically don't need that: isValid() checks that internally.

Inside the isValid(), set $user = $form->getData():

38 lines src/AppBundle/Controller/UserController.php
... lines 1 - 4
use AppBundle\Entity\User;
... lines 6 - 10
class UserController extends Controller
{
... lines 13 - 15
public function registerAction(Request $request)
{
... lines 18 - 20
if ($form->isValid()) {
/** @var User $user */
$user = $form->getData();
... lines 24 - 30
}
... lines 32 - 35
}
}

We know this will be a User object, so I'll plan ahead and add some inline PHP documentation so I get auto-completion later. Add the $em = $this->getDoctrine()->getManager(), $em->persist($user), $em->flush():

38 lines src/AppBundle/Controller/UserController.php
... lines 1 - 10
class UserController extends Controller
{
... lines 13 - 15
public function registerAction(Request $request)
{
... lines 18 - 20
if ($form->isValid()) {
/** @var User $user */
$user = $form->getData();
$em = $this->getDoctrine()->getManager();
$em->persist($user);
$em->flush();
... lines 27 - 30
}
... lines 32 - 35
}
}

Now, what do we always do after a successful form submit? We set a flash: $this->addFlash('success') with 'Welcome '.$user->getEmail():

38 lines src/AppBundle/Controller/UserController.php
... lines 1 - 10
class UserController extends Controller
{
... lines 13 - 15
public function registerAction(Request $request)
{
... lines 18 - 20
if ($form->isValid()) {
/** @var User $user */
$user = $form->getData();
$em = $this->getDoctrine()->getManager();
$em->persist($user);
$em->flush();
$this->addFlash('success', 'Welcome '.$user->getEmail());
... lines 29 - 30
}
... lines 32 - 35
}
}

Finally, redirect - at least for right now - to the homepage route:

38 lines src/AppBundle/Controller/UserController.php
... lines 1 - 10
class UserController extends Controller
{
... lines 13 - 15
public function registerAction(Request $request)
{
... lines 18 - 20
if ($form->isValid()) {
/** @var User $user */
$user = $form->getData();
$em = $this->getDoctrine()->getManager();
$em->persist($user);
$em->flush();
$this->addFlash('success', 'Welcome '.$user->getEmail());
return $this->redirectToRoute('homepage');
}
... lines 32 - 35
}
}

That's it.

Try the whole thing out: weaverryan+15@gmail.com, Password foo. Whoops, and if we just fix my typo, and refresh again:

38 lines src/AppBundle/Controller/UserController.php
... lines 1 - 10
class UserController extends Controller
{
... lines 13 - 15
public function registerAction(Request $request)
{
... lines 18 - 20
if ($form->isValid()) {
... lines 22 - 24
$em->persist($user);
... lines 26 - 30
}
... lines 32 - 35
}
}

It's alive!

But notice it did not automatically log me in. That's something we'll fix in a second. But hey, registration. It's a form. It's easy! It's done.

Leave a comment!

  • 2017-08-30 Victor Bocharsky

    Hey Mike,

    1) Inside controllers, you can use shortcut "$this->getUser()" - that's a better way to get currently logged in user. And one line of code is not a code duplication ;)

    2) That's actually up to you - do as you like most ;) There's no a strong reason doing so, except when you want to group those things separately.

    Cheers!

  • 2017-08-28 Diego Aguiar

    Excellent, let's keep learning! :)

  • 2017-08-28 Hank

    Thanks Diego you were right!

  • 2017-08-28 Diego Aguiar

    Hey Hank

    Look's like something is calling "$form->handleRequest($request);" twice, I would need to check your controller's action code so I can help you debugging

    Cheers!

  • 2017-08-28 Hank

    Hi Ryan, I am using the exact same code of the tutorial but get the error: "A form can only be submitted once" what could be the problem?

  • 2017-08-17 Diego Aguiar

    Hey Mike

    You are right, that doesn't work, sorry for miss leading you. Error messages comes from Constraints, like:


    // User.php
    /**
    * @Assert\NotBlank(message="This field should not be blank")
    */
    private $plainPassword;

    You can change them right there or using a translation file

  • 2017-08-17 Mike

    Thanks for that fast reply!
    I can tell you, this doesn't work:

    {{ form_row(form.plainPassword.first, {
    'label': 'Password',
    'invalid_message' : 'The password fields must match.'
    }) }}
    {{ form_row(form.plainPassword.second, {
    'label': 'Repeat Password',
    'invalid_message' : 'The password fields must matchs.' }) }}

    Label works, but error message is " This value is not valid.". If I type invalid_message into the ->add() method it works perfectly. Maybe its a quirk due to using the RepeatedClass?


    ->add('plainPassword', RepeatedType::class, [
    'type' => PasswordType::class,


    ]);
  • 2017-08-16 Diego Aguiar

    Hey Mike

    You are correct, that would be considered presentation logic aswell, but, it *can* be changed in a twig's template. You can override almost every value the form has, you can try something like this:


    {{ form_row(form.password.first, {
    'invalid_message': 'password custom error message'
    }) }}

    Only check that the keys passed to *form_row* matches to the ones from the Form object.

    Cheers!

  • 2017-08-16 Mike

    Great video as always!
    I'm feeling really dangerous and have to more dangerous questions! :D

    1.) I don't want non anonymous user to access register/login. I have used your code from a previous video to check that and put it into the loginAction and registerAction:


    // Check if user is logged in (not anonymous)
    $token = $this->get('security.token_storage')
    ->getToken();
    $user = $token->getUser();

    if (is_object($user)) {
    // They are not anonymous
    return $this->redirectToRoute('homepage');
    }

    Is this best practice? Is it ok to have this code multiple times (in two controllers) or should it be into another file and called with a "one liner"?

    2.) Why have you chosen to set the /login and /register Action in two different Controllers and not both actions in one, by example SecurityController?

  • 2017-08-16 Mike

    Regarding 2)

    Do I see it right that the 'invalid_message' parameter *can't* be set in the twig template and *only* in the form? That would be presentation logic as well if I see it correctly.

  • 2017-07-05 weaverryan

    Ah! Nice catch! It's those subtle things that are the toughest to see! :)

  • 2017-06-30 Dennis

    Hi Ryan,

    Silly me. In my twig template file I had the following:


    {{ form_row(form.plainPassword.first, {
    'label': 'Password'
    }) }}
    {{ form_row(form.plainPassword.second, {
    'label': 'Repeat password'
    }) }}


    That's why the labels were being set. Sorry!

  • 2017-06-29 weaverryan

    Hey Dennis!

    Huh, interesting! From everything I'm looking at, this *should* work. Actually, the important part is the label => false inside first_options and second_options - the label => false on the top level does nothing (it's fine to keep it - just pointing out where things do and don't matter). Question: if you set 'label' => 'Foo', does the label show up as Foo?

    Also, is it any different if you render the fields individually?


    {{ form_row(form.plainPassword.first) }}
    {{ form_row(form.plainPassword.second) }}

    And finally, if you click into the form profiler, and find the plainPassword.first and plainPassword.second fields, under "View Variables", what is the value of the "label" variable for each?

    Cheers!

  • 2017-06-29 Dennis

    Hi Guys,

    I've got a question. I want to NOT have a label for the password fields. I've tried the following (as shown in the docs):


    ->add('plainPassword', RepeatedType::class, [
    'type' => PasswordType::class,
    'options' => ['label' => false],
    'first_options' => [
    'label' => false,
    'attr' => [
    'placeholder' => 'Password'
    ]
    ],
    'second_options' => [
    'label' => false,
    'attr' => [
    'placeholder' => 'Repeat password'
    ]
    ]
    ]);

    The placeholder show, but the labels do too! I don't want that..

  • 2017-04-26 Diego Aguiar

    Hey Taco!

    You are totally right, calling **isValid()** directly is a bad practice, we will add a note about the deprecation.

    Btw, nice name! Cheers.

  • 2017-04-26 Taco

    "In the forms tutorial, we also added $form->isSubmitted() in the if statement, but you technically don't need that: isValid() checks that internally."
    I think this is no longer true ;)
    "Call Form::isValid() with an unsubmitted form is deprecated since version 3.2 and will throw an exception in 4.0. Use Form::isSubmitted() before Form::isValid() instead"

  • 2017-03-31 Diego Aguiar

    Hey Javier!

    Do you want to add many users at a time or only one ?
    If only one is the case, then you can embed User's form into Organization form, like this:


    // \src\AppBundle\Form\OrganisationCreationForm.php:
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
    // other fields...

    $builder->add('admin', UserRegistrationForm::class);
    }

    You can read more about embeding forms here:
    http://symfony.com/doc/curr...

    Have a nice day!

  • 2017-03-31 Javier M

    \src\AppBundle\Form\UserRegistrationForm:

    add('firstName')

    ->add('lastName')

    ->add('email', EmailType::class)

    ->add('plainPassword', RepeatedType::class, [

    'type' => PasswordType::class

    ])

    ;

    }

    public function configureOptions(OptionsResolver $resolver)

    {

    $resolver->setDefaults([

    'data_class' => User::class,

    'validation_groups' => ['Default', 'Registration']

    ]);

    }

    public function getBlockPrefix()

    {

    return 'UserRegistrationForm';

    }

    }

    \src\AppBundle\Form\OrganisationCreationForm.php:

    add('name')

    ->add('address')

    ->add('website')

    ->add('phoneNumber')

    ->add('admin', EntityType::class, [

    'class' => 'AppBundle\Entity\User',

    ])

    ;

    }

    public function configureOptions(OptionsResolver $resolver)

    {

    $resolver->setDefaults([

    'data_class' => Organisation::class,

    ]);

    }

    public function getBlockPrefix()

    {

    return 'OrganisationCreationForm';

    }

    }
  • 2017-03-31 Javier M

    Hello,

    I have 2 entities: Organisation and User. I have a form for user registration and another form for organisation creation. I want to be able to create new users from the organisation creation form. I've tried with EntityType::class but it just pulls existing users and what I want is to add new users that don't existing in the database. How can I achieve this? thanks.

  • 2017-03-21 Simon Carr

    I still use Codeigniter for the primary project i work on. I am starting to look at how i might rewrite that in Symfony. The problem is the 150 existing tables i have. it would take some time to re write them in Doctrine.

  • 2017-03-21 Victor Bocharsky

    You're welcome!

    Haha, I agree with you :) It's not so scary when you have a good resource for learning. Btw, Symfony has pretty good docs and most of them is also written by Ryan.

    I just wonder what framework did you use before Symfony?

    Cheers!

  • 2017-03-20 Simon Carr

    Thanks Victor Bocharsky , Brilliant. I hadn't got that far in the series.

    It's not obvious that it exists before someone tells you, once you know about it, it's another amazing feature of Symfony!

    I can't believe I have stayed away from Symfony for so many years, I was scared that it would eat me alive, but in fact you get so much power for very little effort. I think the amazing teaching skills of weaverryan are a lot to do with how quickly I have fallen in love with the framework.

  • 2017-03-20 Victor Bocharsky

    Hey Simon,

    You can use different validation groups for it, check out this screencast: https://knpuniversity.com/s... . This should fix your problem. Or you can add NotBlank validation constraint to the form field not to the property of your entity. But validation groups is the right way here, I think.

    Cheers!

  • 2017-03-19 Simon Carr

    I have made my password field NotBlank in the entiy. How would you implement a edit profile form that does not include the password in the form and still allow the changes to the rest of the fields to be saved. I keep getting errors that password must not be empty even though the field is not included in the form.

  • 2016-12-17 Aditya Kothadiya

    Yes, I didn't not initialize $roles as an empty array. I had below -

    /**
    * @ORM\Column(type="json_array")
    */
    private $roles;

    I initialized it to an empty array and now the problem is solved! Thanks a ton for a prompt reply.

  • 2016-12-17 weaverryan

    Hey Aditya Kothadiya!

    Hmm, so, we do 2 things:

    1) We add some code to getRoles() to guarantee that the array returned always contains ROLE_USER. But, that's not your problem - that has nothing to do with how the roles property is stored (source: https://knpuniversity.com/s....

    2) Do you have private $roles = array();? The fact that it is trying to insert as NULL tells me that this property may not have been initialized to an empty array. Also, are you using the json_array type?

    Let me know! I'm sure it's something small :).

    Cheers!

  • 2016-12-17 Aditya Kothadiya

    I'm getting "An exception occurred while executing 'INSERT INTO user (email, password, roles)" error after submitting the form as "Roles" value is Null. How do I set default role ROLE_USER?

  • 2016-12-12 Victor Bocharsky

    Hey Roberto,

    I'm glad you had found the bug quicker than I replied to you :)

    Cheers!

  • 2016-12-11 Roberto Santana

    Sorry, my fault, there was a typo in my buildForm method. :(

  • 2016-12-11 Roberto Santana

    Hi!

    I get this weird error when I try to load the Registration form page, any ideas?

    The option "0" does not exist. Defined options are: "action", "allow_extra_fields", "attr", "auto_initialize", "block_name", "by_reference", "compound", "constraints", "csrf_field_name", "csrf_message", "csrf_protection", "csrf_token_id", "csrf_token_manager", "data", "data_class", "disabled", "empty_data", "error_bubbling", "error_mapping", "extra_fields_message", "first_name", "first_options", "inherit_data", "invalid_message", "invalid_message_parameters", "label", "label_attr", "label_format", "mapped", "method", "options", "post_max_size_message", "property_path", "required", "second_name", "second_options", "translation_domain", "trim", "type", "upload_max_size_message", "validation_groups".

  • 2016-10-10 Victor Bocharsky

    Hey Vlad,

    Yes, you're totally right here!

    Cheers!

  • 2016-10-10 Vlad

    If I recall correctly, the form submits itself to the same route ("/register") and then the isValid check is performed? If the user goes directly to /register, then isValid fails and the registration form is rendered?

  • 2016-10-10 Vlad

    That's terrific, Victor! I haven't gotten to that tutorial yet, but I certainly will.
    73,

  • 2016-10-10 Victor Bocharsky

    Hey Vlad,

    It's called Live Templates in PhpStorm and you can create your own templates! Here's a screencast about it:
    https://knpuniversity.com/s...

    Cheers!

  • 2016-10-10 Vlad

    Hi Ryan,
    How do you get the "action" shortcut?
    Thank you!

  • 2016-10-03 Victor Bocharsky

    Ah, good catch! I forgot to notice about it :)

    Yea, this plugin isn't enabled by default.

  • 2016-10-03 Jay Drake

    Thanks Victor,

    I had installed this, but looking into it this morning it looks like it was not enabled. Crazy!

  • 2016-10-03 Victor Bocharsky

    Hey Jay,

    Yea, it's a Symfony Plugin - you should install it first and most probably restart your PhpStorm after installation.

    Oh, and btw, if you already have this template - you won't see this "Create template" option - it works only for non-existent templates.

    Cheers!

  • 2016-10-03 Jay Drake

    Hi Ryan (and other Aquanauts),

    I noticed here you used a great shortcut with Option-Enter where you chose 'Create template'. On my PC I can use Alt-Enter to get the same menu, but the 'Create template' option isn't available. Is there a particular plugin that is being used for that handy little shortcut?

    Thanks!

  • 2016-09-23 weaverryan

    Hey Yang!

    Ah, thanks! Always happy to see you going through things and asking good questions :).

    1) You're right! Actually, the href and template are correct, but the language is wrong. It should say "Register" (this is actually what we do in the video). I've just updated the code block to show this. Thanks for pointing that out! (ref https://github.com/knpunive...

    2) Glad you already found the solution about the repeated fields - good digging! About the labels, labels - whether you're dealing with a normal field or a repeated field - can *always* be modified in either the form class or the template. Which is better? Probably the template, because I consider this - along with other things like setting a "class" on your element - to be *presentation* logic. Also, on larger teams that have frontend devs, you will want to keep this stuff in your template so that frontend devs can find it without searching around for crazy PHP files :).

    Cheers!

  • 2016-09-22 Yang Liu

    Hi Ryan,

    great tutorial like always, thx!

    two things I like to know though:

    1. just out of curiosity: shouldn't this
    < a href="{{ path('user_register') }}">I already have an account

    be

    < a href="{{ path('user_login') }}"> I already have an account

    furthermore, shouldn't this be in the register.html.twig (not in login.html.twig like in the script)? would make more sense to me :-)

    2. now when I type 2 different passwords in the 2 fields, it gives me an ugly error "This value is not valid." under the first "Password"-field. Is it possible to change the error to something like "your passwords are not the same!!" and show this under the "Repeat Password"-field?

    thanks for your help... again...

    edit: first read, then ask... solved the second issure by reading repeatedtype-documentation
    edit2: new question after reading the repeatedtype-documentation :-):
    you defined the 2 passwordfield labels in twig, it seems thats its although possible to do this in the form builder:

    ->add('plainPassword', RepeatedType::class, [
    'type' => PasswordType::class,
    'first_options' => ['label' =>'Password'],
    'second_options' => ['label' => 'Repeat Password'],
    'invalid_message' => 'The password fields must match',
    'error_mapping' => [
    '.' => 'second'
    ]
    ]);
    so, what would be the "best practice" here?