Question

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

}
Was it helpful?

Solution 2

well guys the thing is in my security.yml i had this

providers:
        main:
            entity: { class: My\UserBundle\Entity\User ,property : username}

so i had to take off that parameter property :username

OTHER TIPS

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.

Taking off the property: username lets the UserProviderInterface load the user as expected when it logs in, but does not call the refreshUser() method as expected. I put in checks to see if it gets called but it doesn't.

The class that reloads the user on each access is ContextListener::refreshUser(TokenInterface $token) method. In this the interface iterates through the UserProviders and calls the refreshUser that first returns a non-null user.

I could make sure of this because, in the original load, I combine all different entities to make one SQL call instead of 7. And when the user reloads, it calls 7 times.

Also the method EntityUserProvider::refreshUser() doesn't call the repository's method and instead reloads from the database directly.

Your provider class is correct, and you are correct that the problem is in security.yml, however, your solution is incorrect.

According to the documentation, your security.yml file should look like this:

security:
# ...
providers:
    administrators:
        entity: { class: MyUserBundle:User }

Notice that the class is defined as the Bundle, not the direct class. The way you have your code right now, symfony is completly ignoring your repository class until you define your security.yml correctly. And as @Anand pointed out, just removing the property does not invoke refreshUser. However, it looks like if you are using your own Repository, you do not need to define the property (since it's being defined in your query).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top