57

I only want to have email as mode of login, I don't want to have username. Is it possible with symfony2/symfony3 and FOSUserbundle?

I read here http://groups.google.com/group/symfony2/browse_thread/thread/92ac92eb18b423fe

But then I am stuck with two constraint violations.

Problem is if the user leaves the email address blank, I get two constraint violations:

  • Please enter a username
  • Please enter an email

Is there a way to disable validation for a given field, or a better way to remove a field from the form altogether?

Mick
  • 30,759
  • 16
  • 111
  • 130
Mirage
  • 30,868
  • 62
  • 166
  • 261

8 Answers8

104

A complete overview of what needs to be done

Here is a complete overview of what needs to be done. I have listed the different sources found here and there at the end of this post.

1. Override setter in Acme\UserBundle\Entity\User

public function setEmail($email)
{
    $email = is_null($email) ? '' : $email;
    parent::setEmail($email);
    $this->setUsername($email);

    return $this;
}

2. Remove the username field from your form type

(in both RegistrationFormType and ProfileFormType)

public function buildForm(FormBuilder $builder, array $options)
{
    parent::buildForm($builder, $options);
    $builder->remove('username');  // we use email as the username
    //..
}

3. Validation constraints

As shown by @nurikabe, we have to get rid of the validation constraints provided by FOSUserBundle and create our own. This means that we will have to recreate all the constraints that were previously created in FOSUserBundle and remove the ones that concern the username field. The new validation groups that we will be creating are AcmeRegistration and AcmeProfile. We are therefore completely overriding the ones provided by the FOSUserBundle.

3.a. Update config file in Acme\UserBundle\Resources\config\config.yml

fos_user:
    db_driver: orm
    firewall_name: main
    user_class: Acme\UserBundle\Entity\User
    registration:
        form:
            type: acme_user_registration
            validation_groups: [AcmeRegistration]
    profile:
        form:
            type: acme_user_profile
            validation_groups: [AcmeProfile]

3.b. Create Validation file Acme\UserBundle\Resources\config\validation.yml

That's the long bit:

Acme\UserBundle\Entity\User:
    properties:
    # Your custom fields in your user entity, here is an example with FirstName
        firstName:
            - NotBlank:
                message: acme_user.first_name.blank
                groups: [ "AcmeProfile" ]
            - Length:
                min: 2
                minMessage: acme_user.first_name.short
                max: 255
                maxMessage: acme_user.first_name.long
                groups: [ "AcmeProfile" ]



# Note: We still want to validate the email
# See FOSUserBundle/Resources/config/validation/orm.xml to understand
# the UniqueEntity constraint that was originally applied to both
# username and email fields
#
# As you can see, we are only applying the UniqueEntity constraint to 
# the email field and not the username field.
FOS\UserBundle\Model\User:
    constraints:
        - Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity: 
             fields: email
             errorPath: email 
             message: fos_user.email.already_used
             groups: [ "AcmeRegistration", "AcmeProfile" ]

    properties:
        email:
            - NotBlank:
                message: fos_user.email.blank
                groups: [ "AcmeRegistration", "AcmeProfile" ]
            - Length:
                min: 2
                minMessage: fos_user.email.short
                max: 255
                maxMessage: fos_user.email.long
                groups: [ "AcmeRegistration", "ResetPassword" ]
            - Email:
                message: fos_user.email.invalid
                groups: [ "AcmeRegistration", "AcmeProfile" ]
        plainPassword:
            - NotBlank:
                message: fos_user.password.blank
                groups: [ "AcmeRegistration", "ResetPassword", "ChangePassword" ]
            - Length:
                min: 2
                max: 4096
                minMessage: fos_user.password.short
                groups: [ "AcmeRegistration", "AcmeProfile", "ResetPassword", "ChangePassword"]

FOS\UserBundle\Model\Group:
    properties:
        name:
            - NotBlank:
                message: fos_user.group.blank
                groups: [ "AcmeRegistration" ]
            - Length:
                min: 2
                minMessage: fos_user.group.short
                max: 255
                maxMessage: fos_user.group.long
                groups: [ "AcmeRegistration" ]

FOS\UserBundle\Propel\User:
    properties:
        email:
            - NotBlank:
                message: fos_user.email.blank
                groups: [ "AcmeRegistration", "AcmeProfile" ]
            - Length:
                min: 2
                minMessage: fos_user.email.short
                max: 255
                maxMessage: fos_user.email.long
                groups: [ "AcmeRegistration", "ResetPassword" ]
            - Email:
                message: fos_user.email.invalid
                groups: [ "AcmeRegistration", "AcmeProfile" ]

        plainPassword:
            - NotBlank:
                message: fos_user.password.blank
                groups: [ "AcmeRegistration", "ResetPassword", "ChangePassword" ]
            - Length:
                min: 2
                max: 4096
                minMessage: fos_user.password.short
                groups: [ "AcmeRegistration", "AcmeProfile", "ResetPassword", "ChangePassword"]


FOS\UserBundle\Propel\Group:
    properties:
        name:
            - NotBlank:
                message: fos_user.group.blank
                groups: [ "AcmeRegistration" ]
            - Length:
                min: 2
                minMessage: fos_user.group.short
                max: 255
                maxMessage: fos_user.group.long
                groups: [ "AcmeRegistration" ]

4. End

That's it! You should be good to go!


Documents used for this post:

Community
  • 1
  • 1
Mick
  • 30,759
  • 16
  • 111
  • 130
  • excellent instructions. However after following it, I get the error "Invalid table Name". I must tell you that I renamed my fos_user table to user table in my orm file and my database uses user. – kratos Jan 11 '14 at 20:19
  • I should overwrite core files? – fdrv Nov 06 '16 at 15:23
  • 2
    This solution is working for me even without the step 3. I'm on symfony 3.1 – Vivien Nov 15 '16 at 15:23
  • 1
    Like @Vivien saw, I didn't require step 3 on Symfony 3. – Andy Place Mar 24 '18 at 08:08
  • 1
    This is intense. – tfont Aug 17 '18 at 13:18
  • 1
    I don't see much of a point of overwriting the validation. Everything that is required by the username validation is already required for the email validation. Am I missing something here? – Dakusan Sep 05 '18 at 03:00
8

I was able to do this by overriding both the registration and profile form type detailed here and removing the username field

$builder->remove('username');

Along with overriding the setEmail method in my concrete user class:

 public function setEmail($email) 
 {
    $email = is_null($email) ? '' : $email;
    parent::setEmail($email);
    $this->setUsername($email);
  }
rvanlaarhoven
  • 591
  • 6
  • 16
Ben_hawk
  • 2,476
  • 7
  • 34
  • 59
  • Please enter a username fos_user_registration_form Symfony\Component\Validator\ConstraintViolation Object(Symfony\Component\Form\Form).data.username = null – fdrv Nov 06 '16 at 15:14
2

As of Sf 2.3, a quick workaround is to set the username to any string in the _construct of your class User that extends BaseUser.

public function __construct()
    {
        parent::__construct();
        $this->username = 'username';
    }

This way, the validator wont trigger any violation. But don't forget to set the email to the username as posted by Patt.

public function setEmail($email)
{
    $email = is_null($email) ? '' : $email;
    parent::setEmail($email);
    $this->setUsername($email);
}

You may have to check other files for references to User:username and change accordingly.

Community
  • 1
  • 1
iambray
  • 359
  • 2
  • 5
2

As Michael points out, this can be solved with a custom validation group. For example:

fos_user:
    db_driver: orm
    firewall_name: main
    user_class: App\UserBundle\Entity\User
    registration:
        form:
            type: app_user_registration
            validation_groups: [AppRegistration]

Then in your entity (as defined by user_class: App\UserBundle\Entity\User) you can use the AppRegistration group:

class User extends BaseUser {

    /**
     * Override $email so that we can apply custom validation.
     * 
     * @Assert\NotBlank(groups={"AppRegistration"})
     * @Assert\MaxLength(limit="255", message="Please abbreviate.", groups={"AppRegistration"})
     * @Assert\Email(groups={"AppRegistration"})
     */
    protected $email;
    ...

This is what I ended up doing after posting that reply to the Symfony2 thread.

See http://symfony.com/doc/2.0/book/validation.html#validation-groups for full details.

nurikabe
  • 3,802
  • 2
  • 31
  • 39
  • I've tried this and have run into problems. You cannot redeclare the mapping on an entity unless you extend FOSUserBundle\Models\User. However, if you do this you have to explicitly redeclare EVERYTHING. Have you actually got this to work? If so, I'd really appreciate a more detailed explanation of exactly what you did! – musoNic80 Jul 24 '12 at 09:11
  • Don't forget to add "use Symfony\Component\Validator\Constraints as Assert;" , and to set the email as a username : "public function setEmail($email) { parent::setEmail($email); $this->setUsername($email); }" – Yako Sep 24 '12 at 09:19
1

Instead of Validation replacing I prefer to replace RegistrationFormHandler#process, more precisely add new method processExtended(for example), which is a copy of original method, and use ut in RegistrationController. (Overriding: https://github.com/FriendsOfSymfony/FOSUserBundle/blob/master/Resources/doc/index.md#next-steps)

Before i bind register form i set username for example 'empty':

class RegistrationFormHandler extends BaseHandler
{

    public function processExtended($confirmation = false)
    {
        $user = $this->userManager->createUser();
        $user->setUsername('empty'); //That's it!!
        $this->form->setData($user);

        if ('POST' == $this->request->getMethod()) {


            $this->form->bindRequest($this->request);

            if ($this->form->isValid()) {

                $user->setUsername($user->getEmail()); //set email as username!!!!!
                $this->onSuccess($user, $confirmation);

                /* some my own logic*/

                $this->userManager->updateUser($user);
                return true;
            }
        }

        return false;
    }
    // replace other functions if you want
}

Why? I prefer to user FOSUserBundle validation rules. Cuz if i replace Validation Group in config.yml for registration form i need to repeat validation rules for User in my own user entity.

BenMorel
  • 34,448
  • 50
  • 182
  • 322
ZloyPotroh
  • 377
  • 7
  • 20
1

If none of them works, a quick and dirty solution would be

public function setEmail($email)
{
    $email = is_null($email) ? '' : $email;
    parent::setEmail($email);
    $this->setUsername(uniqid()); // We do not care about the username

    return $this;
}
zeroweb
  • 2,612
  • 4
  • 22
  • 34
1

Have you tried customizing the validation?

To do this, you need to have your own bundle inheriting from the UserBundle, and then copy/adjust Resources/config/validation.xml. Plus, you need to set the validation_groups in the config.yml to your custom validation.

Michael Sauter
  • 271
  • 3
  • 9
0

You can make the username nullable and then remove it from the form type:

First, in AppBundle\Entity\User, add the annotation above the User class

use Doctrine\ORM\Mapping\AttributeOverrides;
use Doctrine\ORM\Mapping\AttributeOverride;

/**
 * User
 *
 * @ORM\Table(name="fos_user")
 *  @AttributeOverrides({
 *     @AttributeOverride(name="username",
 *         column=@ORM\Column(
 *             name="username",
 *             type="string",
 *             length=255,
 *             unique=false,
 *             nullable=true
 *         )
 *     ),
 *     @AttributeOverride(name="usernameCanonical",
 *         column=@ORM\Column(
 *             name="usernameCanonical",
 *             type="string",
 *             length=255,
 *             unique=false,
 *             nullable=true
 *         )
 *     )
 * })
 * @ORM\Entity(repositoryClass="AppBundle\Repository\UserRepository")
 */
class User extends BaseUser
{
//..

When you run php bin/console doctrine:schema:update --force it will make the username nullable in the database.

Second, in your form type AppBundle\Form\RegistrationType, remove the username from the form.

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

        $builder->remove('username');
        // you can add other fields with ->add('field_name')
    }

Now, you won't see the username field in the form (thanks to $builder->remove('username');). and when you submit the form, you won't get the validation error "Please enter a username" anymore because it's no longer required (thanks to the annotation).

Source: https://github.com/FriendsOfSymfony/FOSUserBundle/issues/982#issuecomment-12931663

Eissa
  • 325
  • 2
  • 11