FOSUserBundle icon indicating copy to clipboard operation
FOSUserBundle copied to clipboard

Overriding the 'roles' field (type and associations)

Open BazylPL opened this issue 10 years ago • 3 comments

Hi,

I've decided to manage user roles in a bit different way. I've already created my custom User entity and UserRole entity (with database table corresponding to it). Now I'd like to store an ID of UserRole within my User class using ManyToOne relationship. To do that, the 'roles' field needs to be an integer but I can't seem to overide it properly.

Of course I can't change it here: FOS\UserBundle\Resources\config\doctrine\model\User.orm.xml: < field name="roles" column="roles" type="array" >

So, what is the best way to change/override roles' field type from array to integer (or any given type) ? Basic bundle overriding and creating User.orm.xml in my user bundle did not help :(

Next thing is, I need to create an association to my UserRole via annotations. I've read about @AssociationOverride annotation, but I understand the given field has to have an existing association already, since "association type CANNOT be changed". Well, 'roles' field doesn't have any.

I'd be very grateful for some tips or perhaps there is a solution already I haven't found yet.

p.s. In my project I use Doctrine2 and PostgreSQL.

Regards, Bazyl

BazylPL avatar Mar 06 '14 11:03 BazylPL

What I try to do is this:

<?php

namespace Maith\Common\UsersBundle\Entity;

use Symfony\Component\Security\Core\Role\RoleInterface;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity
 * @ORM\Table(name="maith_role")
 *
 * @author rodrigo
 */
class Role implements RoleInterface{

  /**
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id()
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @ORM\Column(name="name", type="string", length=30)
     */
    private $name;

    /**
     * @ORM\Column(name="role", type="string", length=20, unique=true)
     */
    private $role;

    /**
     * @ORM\ManyToMany(targetEntity="User", mappedBy="tableroles")
     */
    private $users;

    public function __construct()
    {
        $this->users = new ArrayCollection();
    }

    /**
     * @see RoleInterface
     */
    public function getRole()
    {
        return $this->role;
    }

    /**
     * Get id
     *
     * @return integer 
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Set name
     *
     * @param string $name
     * @return Role
     */
    public function setName($name)
    {
        $this->name = $name;

        return $this;
    }

    /**
     * Get name
     *
     * @return string 
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * Set role
     *
     * @param string $role
     * @return Role
     */
    public function setRole($role)
    {
        $this->role = $role;

        return $this;
    }

    /**
     * Add users
     *
     * @param \Maith\Common\UsersBundle\Entity\User $users
     * @return Role
     */
    public function addUser(\Maith\Common\UsersBundle\Entity\User $users)
    {
        $this->users[] = $users;

        return $this;
    }

    /**
     * Remove users
     *
     * @param \Maith\Common\UsersBundle\Entity\User $users
     */
    public function removeUser(\Maith\Common\UsersBundle\Entity\User $users)
    {
        $this->users->removeElement($users);
    }

    /**
     * Get users
     *
     * @return \Doctrine\Common\Collections\Collection 
     */
    public function getUsers()
    {
        return $this->users;
    }
}

And my User class is

<?php

namespace Maith\Common\UsersBundle\Entity;

use FOS\UserBundle\Model\User as BaseUser;
use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity
 * @ORM\Table(name="maith_user")
 *
 * @author Rodrigo Santellan
 */
class User extends BaseUser{

  /**
   *
   * @ORM\Id
   * @ORM\Column(type="integer")
   * @ORM\GeneratedValue(strategy="AUTO")
   * 
   */
  protected $id;

  /**
  * @ORM\ManyToMany(targetEntity="Role", inversedBy="users")
  *
  */
  protected $tableroles;

  function __construct() {

    parent::__construct();
    $this->tableroles = new ArrayCollection();  
  }

    /**
     * Get id
     *
     * @return integer 
     */
    public function getId()
    {
        return $this->id;
    }

    public function getExpiresAt()
    {
        return $this->expiresAt;
    }

    public function getCredentialsExpireAt()
    {
        return $this->credentialsExpireAt;
    }

    /**
     * Returns the user roles
     *
     * @return array The roles
     */
    public function getRoles()
    {
        $roles = array();

        foreach($this->getTableroles() as $tableRole){
          $roles[] = $tableRole->getRole();
        }

        return array_unique($roles);
    }

    /**
     * Add tableroles
     *
     * @param \Maith\Common\UsersBundle\Entity\Role $tableroles
     * @return User
     */
    public function addTablerole(\Maith\Common\UsersBundle\Entity\Role $tableroles)
    {
        $this->tableroles[] = $tableroles;

        return $this;
    }

    /**
     * Remove tableroles
     *
     * @param \Maith\Common\UsersBundle\Entity\Role $tableroles
     */
    public function removeTablerole(\Maith\Common\UsersBundle\Entity\Role $tableroles)
    {
        $this->tableroles->removeElement($tableroles);
    }

    /**
     * Get tableroles
     *
     * @return \Doctrine\Common\Collections\Collection 
     */
    public function getTableroles()
    {
        return $this->tableroles;
    }
}

My problem is that it will not take the role. Just brainstorming.

rsantellan avatar Mar 11 '14 20:03 rsantellan

I'm having the same problem I'd like to create a Role entity with a many to many relationship to my user entity. When I do that, and I'm trying to update my db using php app/console doctrine:schema:update --dump-sql I get the following error: Property "roles" in "Wdnl\Bundle\UserBundle\Entity\User" was already declared, but it must be declared only once.

What should I do to properly override default FOSUserBundle functionalities?

ivodvb avatar Sep 25 '15 15:09 ivodvb

I dont know if you guys have resolved but i post a solution.

This is the Role class

<?php

namespace yourNameSpace;

use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity
 * @ORM\Table(name="roles")
 */
class Role
{
    /**
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column(type="integer")
     */
    protected $id;

    /**
     * @ORM\Column(type="string")
     */
    protected $name;

    /**
     * Gets the ID.
     *
     * @return int
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Gets the role name.
     *
     * @return string
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * Sets the role name.
     * 
     * @param string $name The role name
     *
     * @return $this
     */
    public function setName(string $name)
    {
        $this->name = $name;

        return $this;
    }
}

And this is the User class.

<?php

namespace yourNameSpace;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
use FOS\UserBundle\Model\User as BaseUser;
use pathToRoleEntity;

/**
 * @ORM\Entity
 * @ORM\Table(name="users")
 */
class User extends BaseUser
{
    /**
     * {@inheritdoc}
     *
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column(type="integer")
     */
    protected $id;

    /**
     * @var Role The role
     *
     * @ORM\ManyToMany(targetEntity="pathToRoleEntity")
     * @ORM\JoinTable(name="users_roles")
     * @ORM\JoinColumn(onDelete="CASCADE")
     */
    protected $userRoles;

    public function __construct()
    {
        parent:: __construct();
        $this->userRoles = new ArrayCollection();
    }

    /**
     * Gets all roles.
     *
     * @return ArrayCollection|Role
     */
    public function getUserRoles()
    {
        return $this->userRoles;
    }

    /**
     * Adds the given role.
     *
     * @param Role $role The role object
     *
     * @return $this
     */
    public function addUserRole(Role $role)
    {
        if(!$this->userRoles->contains($role)){
            $this->userRoles->add($role);
        }

        return $this;
    }

    /**
     * Removes the given role.
     *
     * @param Role $role The role object
     *
     * @return $this
     */
    public function removeUserRole(Role $role)
    {
        $this->userRoles->remove($role);

        return $this;
    }
}

After this you just need to create in your database the middle table 'users_roles' with the foreign keys of User and Role.

In your UserType formBuilder for render the roles just use entityType like this

->add('user_roles', EntityType::class, [
                'property_path' => 'userRoles',
                'multiple' => true,
                'expanded' => true,
                'class' => Role::class,
                'choice_label' => 'name',
            ])

Hope it helps you

Mattia1993 avatar May 16 '17 10:05 Mattia1993