ibi0tux
ibi0tux

Reputation: 2639

Symfony get user defined with different classes for roles

I have two roles in my application, for example ROLE_USER and ROLE_SUPERUSER. Users are stored in the database using Doctrine. Users with the ROLE_USER role are based on a simple User class. (Getters and setters have been removed for readability.)

Acme\MyBundle\Entity\User.php

namespace Acme\MyBundle\Entity;
 
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\UserInterface;
 
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
 
/**
* @ORM\Entity
* @ORM\Entity(repositoryClass="Acme\MyBundle\Entity\UserRepository")
* @ORM\Table("users")
* @UniqueEntity(
*       fields={"email"},
*       message="email already used"
* )
*/
class User implements UserInterface, \Serializable
{
    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;
 
    /**
     * @ORM\Column(type="string", length=255, unique=true)
     * @Assert\NotBlank()
     * @Assert\Email()
     */
    protected $email;
 
    /**
     * @ORM\Column(type="string", length=32)
     */
    private $salt;
 
    /**
     * @ORM\Column(type="string", length=4096)
     */
    private $password;
 
 
    /**
     * @ORM\Column(name="is_active", type="boolean")
     */
    private $isActive;
 
 
    public function __construct()
    {
        $this->isActive = true;
        $this->salt = md5(uniqid(null, true));
    }
 
    /**
    * @inheritDoc
    */
    public function getRoles()
    {
    return array('ROLE_USER','ROLE_SUPERUSER');
    }
 
}

I have a SuperUser class that extends User to provides more fields that only users with ROLE_SUPERUSER would need.

Acme\MyBundle\Entity\SuperUser.php

namespace Acme\MyBundle\Entity;
 
use Doctrine\ORM\Mapping as ORM;
 
use Symfony\Component\Validator\Constraints as Assert;
 
 
/**
 * @ORM\Entity
 * @ORM\Table(name="super_users")
 */
class SuperUser extends User
{
 
    /**
     * @var integer
     */
    protected $id;
 
    /**
     * @var string
     */
    protected $email;
 
 
    /**
     * @ORM\Column(type="string", length=32)
     */
    proteced $avatar;
 
 
    /**
     * @ORM\Column(type="string", length=50, unique=true)
     */
    proteced $username;
 
}

In my controllers I'm using $user = $this -> getUser(); to get the current user, but this returns an instance of the User class and I cannot access the SuperUser properties or methods, even if the user has the role ROLE_SUPERUSER.

For example, I would like to be able to use the following code.

if ($this->get('security.context')->isGranted('ROLE_SUPERUSER')) {
    $avatar = $this->getUser()-> avatar;
}

Is there anyway to be able to do that? I would say there's something to do with Doctrine relationships, but I don't really know what to change.

By the way, as you can see I don't have an username field in my standard User class, it's only present in SuperUser. Does this may cause any problems since the authentication is based on username?


I think my problem hasn't been clearly exposed, but this might be due to my current code, which is wrong.

I don't have two user tables. I want only one User class (with one users table). The authentication is operated only on this class with email and password.
I have another class SuperUser```that provides extra fields to users that have the ROLE_SUPERUSER`` role, but the superusers are users and have an entry in the users table. I just want to create a left join on the concerned rows, that's why I used inheritance. (Maybe there's a better way to do it.)

If I want to get all the emails, I can query the users table. If I want to get all the usernames, since only superusers have one, I can query the superusers table.

Upvotes: 1

Views: 1412

Answers (2)

Frank B
Frank B

Reputation: 3697

I would go for two entities with a OneToOne relationship (No extends)

/** @Entity **/
class SuperUser
{
    // ...

    /**
     * @ORM\OneToOne(targetEntity="User", mappedBy="superUser")
     **/
    private $user;

    // ...
}

/** @Entity **/
class User
{
    // ...

    /**
     * @ORM\OneToOne(targetEntity="SuperUser", inversedBy="user")
     * @ORM\JoinColumn(name="user_id", referencedColumnName="id")
     **/
    private $superUser;

    // ...
}

Otherwise take a look at Inheritance Mapping

Upvotes: 2

malcolm
malcolm

Reputation: 5542

You should have two user providers then, and use one master provider:

security:
    providers:
        master_provider:
            chain:
                providers: [user, super_user]

        user:
            entity: { class: Acme\MyBundle\Entity\User, property: username }
        super_user:
            entity: { class: Acme\MyBundle\Entity\SuperUser, property: username }

        firewalls:
            main:
                provider: master_provider

http://symfony.com/doc/current/cookbook/security/multiple_user_providers.html

Upvotes: 0

Related Questions