symfony2 login by username or email

kosaidpo picture kosaidpo · Nov 30, 2011 · Viewed 7k times · Source

I'm using the standard authentication mechanism of Symfony2 and I want to let the user use either his username or email to login, but I can't find out why it's not working. I've tested the repository class and it works as expected. I've followed this how-to.

Here's my user provider class:

<?php

namespace My\UserBundle\Entity;

use Doctrine\ORM\EntityRepository ,
Symfony\Component\Security\Core\User\UserProviderInterface ,
Symfony\Component\Security\Core\User\UserInterface;

/**
* UserRepository
*
* This class was generated by the Doctrine ORM. Add your own custom
* repository methods below.
*/
class UserRepository extends EntityRepository implements UserProviderInterface 
{
    function loadUserByUsername($username)
    {

        $qb = $this->createQueryBuilder('u') ;
        return 
        $qb->select('u')
        ->where(
            $qb->expr()->orx(
                $qb->expr()->like('u.username' ,':username') ,
                $qb->expr()->like('u.email' ,':username')
            )
        )
        //->andWhere($qb->expr()->eq('u.enabled' ,'true') )
        ->setParameters(array('username' =>$username ) )        
        ->getQuery()
        ->getResult() ;                  
    }

    function refreshUser(UserInterface $user)
    {
        return $this->loadUserByUsername($user->getUsername() );
    }

    function supportsClass($class)
    {
        return $class === 'My\UserBundle\Entity\User';
    }

}

Answer

Hokusai picture Hokusai · Mar 3, 2016

I propose a more simple approach that only requires to edit security.yml file.

You must to create two diferent security providers but both using the same User class. The first has username as property and the second has email as property.

Then you create a chain_provider that includes the two providers and use it in your firewall section

security:

    providers:
        chain_provider:
            chain:
                providers: [db_username, db_email]
        db_username:
            entity:
                class: MyUserBundle:User
                property: username
        db_email:
            entity:
                class: MyUserBundle:User
                property: email

    firewalls:

        default:
            anonymous: ~            
            provider: chain_provider
            form_login:
                login_path: /login
                check_path: /login

I don't know if this approach is a clean practice, but is fast, simple and it works ok.