Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

Registration Form

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $12.00

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:

... 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:

... 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:

... 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:

... 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:

... 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:

... 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:

... 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():

... 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:

{% 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):

... 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):

... 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:

... 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'):

... 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:

... 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):

... 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:

... 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():

... 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():

... 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():

... 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:

... 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:

... 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!

48
Login or Register to join the conversation
Vladimir Z. Avatar
Vladimir Z. Avatar Vladimir Z. | posted 5 years ago

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

1 Reply

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!

Reply
Vladimir Z. Avatar
Vladimir Z. Avatar Vladimir Z. | Victor | posted 5 years ago

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

Reply
Default user avatar
Default user avatar Aditya Kothadiya | posted 5 years ago

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?

1 Reply

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/screencast/symfony-security/dynamic-roles).

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!

1 Reply
Default user avatar
Default user avatar Aditya Kothadiya | weaverryan | posted 5 years ago

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.

14 Reply
Łukasz K. Avatar
Łukasz K. Avatar Łukasz K. | posted 2 years ago

Please update course to s5 with reset password and confirmation register and OAuth login

Reply

Hey Marcin,

Thank you for your interest in SymfonyCasts tutorials! Yes, it's on our plan to release a new Security tutorial for Symfony 5 track. Not sure will it be the next Symfony5-related course or after one more course, but we will definitely release this some day. We also will try to cover reset password feature there, actually we have a new bundle that helps with resetting passwords, you can check it our here: https://github.com/SymfonyC... . Unfortunately, I don't have estimations for you yet when the new Symfony 5 security course might be released, sorry.

Btw, you're looking at Symfony3 course about security, we also have an updated one for Symfony 4 that is the latest available course about Symfony security. You can find it here: https://symfonycasts.com/sc... . Feel free to follow it on Symfony 5 project and if you will have any problems - aks them in comments below the video, we will definitely try to help you solving them!

Cheers!

Reply

Hey Abdelamine,

So, you're trying to render a form, and even if you have $email property on User entity you need to have a form field in your form you're rendering here. I mean, open the form class you that you are passing to form_row(), do you have email field there? I guess you don't. Otherwise, make sure you're passing the proper form to the form_row() function.

Cheers!

Reply
Dennis E. Avatar
Dennis E. Avatar Dennis E. | posted 5 years ago | edited

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..

Reply

Hey Dennis E.!

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!

Reply
Dennis E. Avatar
Dennis E. Avatar Dennis E. | weaverryan | posted 5 years ago | edited

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!

Reply

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

Reply
Default user avatar
Default user avatar Jay Drake | posted 5 years ago

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!

Reply

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!

Reply
Default user avatar

Thanks Victor,

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

Reply

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

Yea, this plugin isn't enabled by default.

Reply
javmc Avatar

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.

Reply
javmc Avatar
javmc Avatar javmc | javmc | posted 5 years ago | edited

\src\AppBundle\Form\UserRegistrationForm:

<?php

namespace AppBundle\Form;

use AppBundle\Entity\User;

use Symfony\Component\Form\AbstractType;

use Symfony\Component\Form\Extension\Core\Type\EmailType;

use Symfony\Component\Form\Extension\Core\Type\PasswordType;

use Symfony\Component\Form\Extension\Core\Type\RepeatedType;

use Symfony\Component\Form\FormBuilderInterface;

use Symfony\Component\OptionsResolver\OptionsResolver;

class UserRegistrationForm extends AbstractType

{

    public function buildForm(FormBuilderInterface $builder, array $options)

    {

        $builder

            ->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:

<?php

namespace AppBundle\Form;

use AppBundle\Entity\Organisation;

use AppBundle\Entity\User;

use Symfony\Bridge\Doctrine\Form\Type\EntityType;

use Symfony\Component\Form\AbstractType;

use Symfony\Component\Form\FormBuilderInterface;

use Symfony\Component\OptionsResolver\OptionsResolver;

class OrganisationCreationForm extends AbstractType

{

    public function buildForm(FormBuilderInterface $builder, array $options)

    {

        $builder

            ->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';

    }

Reply

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/current/form/embedded.html

Have a nice day!

Reply

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.

Reply
Vladimir Z. Avatar
Vladimir Z. Avatar Vladimir Z. | posted 5 years ago

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?

Reply

Hey Vlad,

Yes, you're totally right here!

Cheers!

Reply
Mike P. Avatar
Mike P. Avatar Mike P. | posted 5 years ago | edited

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?

Reply

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!

Reply
Default user avatar
Default user avatar Simon Carr | posted 5 years ago

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.

Reply

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!

1 Reply
Default user avatar
Default user avatar Simon Carr | Victor | posted 5 years ago | edited

Thanks victor , 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 victor are a lot to do with how quickly I have fallen in love with the framework.

Reply

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!

Reply
Default user avatar
Default user avatar Simon Carr | Victor | posted 5 years ago

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.

Reply
Patrick Avatar
Patrick Avatar Patrick | posted 5 years ago

Hey Ryan, thanks for another excellent tutorial!

FYI, it looks like the note regarding not needing to call isSubmitted() before isValid() may be out of date when using Symfony 4. If one leaves out isSubmitted(), a LogicException is thrown with the message "Cannot check if an unsubmitted form is valid. Call Form::isSubmitted() before Form::isValid()".

Reply

Hey Dan,

Is it? Let's figure it out... You'll see this "Cannot check if an unsubmitted form is valid..." error only when the form is not submitted, see this inverted logic in if statement inside isValid(): https://github.com/symfony/... . In other words, if you call isValid() on the form which was not sent, i.e. when "isSubmitted = false" - you'll get that exception which means you should call isSubmitted() first before isValid() to avoid throwing that exception, right? Because we totally do not want to see that exception. So note is valid as for me :)

Cheers!

Reply
Patrick Avatar
Patrick Avatar Patrick | Victor | posted 5 years ago | edited

Thanks for your reply Victor. I must be completely misunderstanding the note in this tutorial. It says "In the forms tutorial, we also added $form->isSubmitted() in the if statement, but you technically don't need that: isValid() checks that internally."

The note tells us to use`
if ($form->isValid())`
instead of`
if ($form->isSubmitted() && $form->isValid())`
.

As you say, isValid() does check internally if the form is submitted, but if it's not, it throws an exception, which is not what we want here.

Reply

Exactly that Patrick

In older Symfony's version you could call "$form->isValid()" without calling "isSubmitted()" and nothing would break, but in Symfony4 (probably since version 3.4?) it throws an exception. So, just don't forget to call both methods whenever you are working with a forms :)

Reply
Patrick Avatar

Thanks for you reply, Diego. Ah, okay. Yes, that's exactly what I was pointing out - that it may be useful to note that the tip given in this tutorial doesn't apply to SF4.

Reply
Default user avatar

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?

Reply

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!

Reply
Default user avatar

Thanks Diego you were right!

Reply

Excellent, let's keep learning! :)

Reply
Roberto S. Avatar
Roberto S. Avatar Roberto S. | posted 5 years ago

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".

Reply
Roberto S. Avatar

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

Reply

Hey Roberto,

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

Cheers!

Reply
Default user avatar
Default user avatar Yang Liu | posted 5 years ago

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?

-1 Reply

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!

Reply
Mike P. Avatar

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.

Reply

Hey Mike P.

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!

Reply
Mike P. Avatar
Mike P. Avatar Mike P. | MolloKhan | posted 5 years ago | edited

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,
        ]);

Reply

Hey Mike P.

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

Reply
Cat in space

"Houston: no signs of life"
Start the conversation!

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": ">=5.5.9",
        "symfony/symfony": "3.1.*", // v3.1.4
        "doctrine/orm": "^2.5", // v2.7.2
        "doctrine/doctrine-bundle": "^1.6", // 1.6.4
        "doctrine/doctrine-cache-bundle": "^1.2", // 1.3.0
        "symfony/swiftmailer-bundle": "^2.3", // v2.3.11
        "symfony/monolog-bundle": "^2.8", // 2.11.1
        "symfony/polyfill-apcu": "^1.0", // v1.2.0
        "sensio/distribution-bundle": "^5.0", // v5.0.22
        "sensio/framework-extra-bundle": "^3.0.2", // v3.0.16
        "incenteev/composer-parameter-handler": "^2.0", // v2.1.2
        "composer/package-versions-deprecated": "^1.11", // 1.11.99
        "knplabs/knp-markdown-bundle": "^1.4", // 1.4.2
        "doctrine/doctrine-migrations-bundle": "^1.1" // 1.1.1
    },
    "require-dev": {
        "sensio/generator-bundle": "^3.0", // v3.0.7
        "symfony/phpunit-bridge": "^3.0", // v3.1.3
        "nelmio/alice": "^2.1", // 2.1.4
        "doctrine/doctrine-fixtures-bundle": "^2.3" // 2.3.0
    }
}
userVoice