I am learning Symfony 4, and I am lost with user roles and I don't know where to start.
Because it will be an intranet like website Users will not register themselves, the admin will register them and set the role. The admin can also create new roles dynamically.
I would like to store user roles in database. I have 2 entities User and Role. (Relations are not defined yet, that´s normal)
Here is my User entity :
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Security\Core\User\UserInterface;
/**
* #ORM\Entity(repositoryClass="App\Repository\UserRepository")
* #UniqueEntity(fields="email", message="Email already taken")
* #UniqueEntity(fields="username", message="Username already taken")
*/
class User implements UserInterface, \Serializable
{
/**
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
* #ORM\Column(type="integer", unique=true)
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $firstname;
/**
* #ORM\Column(type="string", length=255)
*/
private $lastname;
/**
* #ORM\Column(type="string", length=255)
*/
private $email;
/**
* #ORM\Column(type="date")
*/
private $birthdate;
/**
* #ORM\Column(type="integer", nullable=true)
*/
private $roleId;
/**
* #ORM\Column(type="string", length=255, nullable=false)
*/
private $username;
/**
* #Assert\NotBlank()
* #Assert\Length(max=4096)
*/
private $plainPassword;
/**
* #ORM\Column(type="string", length=255, nullable=false)
*/
private $password;
/**
* #ORM\Column(name="is_active", type="boolean", nullable=true)
*/
private $isActive;
// GETTERS
public function getId()
{
return $this->id;
}
public function getFirstname()
{
return $this->firstname;
}
public function getLastname()
{
return $this->lastname;
}
public function getBirthdate()
{
return $this->birthdate;
}
public function getEmail()
{
return $this->email;
}
public function getRoleId()
{
return $this->roleId;
}
public function getRoles()
{
return array('ROLE_USER');
}
public function getUsername()
{
return $this->username;
}
public function getPlainPassword()
{
return $this->plainPassword;
}
public function getPassword()
{
return $this->password;
}
public function getSalt()
{
return null;
}
// SETTERS
public function setFirstname($firstname)
{
$this->firstname = $firstname;
}
public function setLastname($lastname)
{
$this->lastname = $lastname;
}
public function setEmail($email)
{
$this->email = $email;
}
public function setBirthdate($birthdate)
{
$this->birthdate = $birthdate;
}
public function setRoleId($roleId)
{
$this->roleId = $roleId;
}
public function setUsername($username)
{
$this->username = $username;
}
public function setPlainPassword($password)
{
$this->plainPassword = $password;
}
public function setPassword($password)
{
$this->password = $password;
}
// FUNCTIONS
public function serialize()
{
return serialize(array(
$this->id,
$this->username,
$this->password,
// see section on salt below
// $this->salt,
));
}
/** #see \Serializable::unserialize() */
public function unserialize($serialized)
{
list (
$this->id,
$this->username,
$this->password,
// see section on salt below
// $this->salt
) = unserialize($serialized);
}
public function eraseCredentials()
{
}
}
Here is my Role entity :
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass="App\Repository\RoleRepository")
*/
class Role
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer", unique=true)
*/
private $id;
/**
* #ORM\Column(type="string", length=255, nullable=false)
*/
private $type;
// GETTERS
public function getId()
{
return $this->id;
}
public function getType()
{
return $this->type;
}
// SETTERS
public function setType($type): void
{
$this->type = $type;
}
}
Is it a good practice to do so ??
On the Symfony documentation, we can read everywhere the "macros" ROLE_USER, ROLE_ADMIN... Where are they defined ? Can we customize this ?
Symfony used to support role entities with a RoleInterface, much like with your user implementing the UserInterface. It was decided that this is unnecessary as the roles-table will usually only contain 2 fields (id, name) which means we might just as well store the roles directly in the users table.
So, if you want to follow Symfony best practices you would just have your roles in the user, for example like this:
class User implements UserInterface
{
/** #Column(type="json") */
private $roles = [];
public function getRoles(): array
{
return array_unique(array_merge(['ROLE_USER'], $this->roles));
}
public function setRoles(array $roles)
{
$this->roles = $roles;
}
public function resetRoles()
{
$this->roles = [];
}
}
If you don't want to store the roles as JSON-encoded string you can also use #Column(type="array") */ or write a custom DBAL type, e.g some kind of EnumType. You might also want to secure the setter against invalid data or add validation.
Regarding the second question about the roles used in the documentation: Symfony has some predefined roles and pseudo-roles for certain features:
IS_AUTHENTICATED_ANONYMOUSLY
IS_AUTHENTICATED_REMEMBERED
IS_AUTHENTICATED_FULLY
ROLE_ALLOWED_TO_SWITCH (for user switching)
The other roles like ROLE_USER and ROLE_ADMIN are purely exemplary and you may use them as you see fit. You do not even have to start your own roles with a ROLE_ (although some security features rely on this convention and things will not work as expected for those roles unless you do some manual changes).
Recommended Reading from current Symfony Documentation (version 4 at the time of this answer):
https://symfony.com/doc/current/security.html#hierarchical-roles
Symfony recommends defining role inheritance, please see if it helps; from the link just above,
"
Instead of giving many roles to each user, you can define role inheritance rules by creating a role hierarchy:
# config/packages/security.yaml
security:
# ...
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_SUPER_ADMIN: [ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]
Users with the ROLE_ADMIN role will also have the ROLE_USER role. And users with ROLE_SUPER_ADMIN, will automatically have ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH and ROLE_USER (inherited from ROLE_ADMIN).
For role hierarchy to work, do not try to call $user->getRoles() manually. For example, in a controller extending from the base controller:
// BAD - $user->getRoles() will not know about the role hierarchy
$hasAccess = in_array('ROLE_ADMIN', $user->getRoles());
// GOOD - use of the normal security methods
$hasAccess = $this->isGranted('ROLE_ADMIN');
$this->denyAccessUnlessGranted('ROLE_ADMIN');
" end of copying
I think storing the role as a string is enough if inheritance fits your needs.
Related
i'm still a beginner in symfony so i hope my question will be fastly answered. I did a lot of research on internet without finding anything about my problem.
I use Symfony 5.3 basic authenticator which is generated when we do the bin/console make:auth with maker bundle. All basic config, i didn't touch anything in security.yaml. The thing i want to perform is set a authentication error if the user is not verified by Email. My User entity is very basic too with make:user. But it seems like the User representation we get with this token in onAuthenticationSuccess doesn't contain any "isVerified" method, an interesting fact is that he doesn't contain any "getEmail" method too, seems like he just contain the main accessors (username, password). Any solution ?
(Login is actually working, i just can't perform this verifying trick.)
EDIT: Just saw that "isVerifed" and "getEmail" methods can't be called from any $this->getUser() in controllers too. But when we do a new User(), we can call these methods.
EDIT AGAIN: Ok it's because $this->getUser() and $token->getUser() extends UserInterface and there's not any getEmail() or isVerified() method in this interface. So what is the trick ? I keep investigating.
Authenticator:
<?php
namespace App\Security;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Http\Authenticator\AbstractLoginFormAuthenticator;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\PasswordCredentials;
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
use Symfony\Component\Security\Http\Authenticator\Passport\PassportInterface;
use Symfony\Component\Security\Http\Util\TargetPathTrait;
class Authenticator extends AbstractLoginFormAuthenticator
{
use TargetPathTrait;
public const LOGIN_ROUTE = 'app_login';
private UrlGeneratorInterface $urlGenerator;
public function __construct(UrlGeneratorInterface $urlGenerator)
{
$this->urlGenerator = $urlGenerator;
}
public function authenticate(Request $request): PassportInterface
{
$parameters = json_decode($request->getContent(), true);
return new Passport(
new UserBadge($parameters['username']),
new PasswordCredentials($parameters['password'])
);
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
{
if (!$token->getUser()->isVerified()) { //isVerified show "undefined method"
$request->getSession()->set(Security::AUTHENTICATION_ERROR, "You are not verified. Check your emails.");
}
return null;
}
protected function getLoginUrl(Request $request): string
{
return $this->urlGenerator->generate(self::LOGIN_ROUTE);
}
}
My User:
<?php
namespace App\Entity;
use App\Repository\UserSymfangularRepository;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
use Symfony\Component\Security\Core\User\UserInterface;
/**
* #ORM\Entity(repositoryClass=UserSymfangularRepository::class)
* #UniqueEntity(fields={"username"}, message="There is already an account with this username")
*/
class UserSymfangular implements UserInterface, PasswordAuthenticatedUserInterface
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=180, unique=true)
*/
private $username;
/**
* #ORM\Column(type="json")
*/
private $roles = [];
/**
* #var string The hashed password
* #ORM\Column(type="string")
*/
private $password;
/**
* #ORM\Column(type="string", length=255)
*/
private $email;
/**
* #ORM\Column(type="boolean")
*/
private $isVerified = false;
public function getId(): ?int
{
return $this->id;
}
/**
* #deprecated since Symfony 5.3, use getUserIdentifier instead
*/
public function getUsername(): string
{
return (string) $this->username;
}
public function setUsername(string $username): self
{
$this->username = $username;
return $this;
}
/**
* A visual identifier that represents this user.
*
* #see UserInterface
*/
public function getUserIdentifier(): string
{
return (string) $this->username;
}
/**
* #see UserInterface
*/
public function getRoles(): array
{
$roles = $this->roles;
// guarantee every user at least has ROLE_USER
$roles[] = 'ROLE_USER';
return array_unique($roles);
}
public function setRoles(array $roles): self
{
$this->roles = $roles;
return $this;
}
/**
* #see PasswordAuthenticatedUserInterface
*/
public function getPassword(): string
{
return $this->password;
}
public function setPassword(string $password): self
{
$this->password = $password;
return $this;
}
/**
* Returning a salt is only needed, if you are not using a modern
* hashing algorithm (e.g. bcrypt or sodium) in your security.yaml.
*
* #see UserInterface
*/
public function getSalt(): ?string
{
return null;
}
/**
* #see UserInterface
*/
public function eraseCredentials()
{
// If you store any temporary, sensitive data on the user, clear it here
// $this->plainPassword = null;
}
public function getEmail(): ?string
{
return $this->email;
}
public function setEmail(string $email): self
{
$this->email = $email;
return $this;
}
public function getIsVerified(): ?bool
{
return $this->isVerified;
}
public function setIsVerified(bool $isVerified): self
{
$this->isVerified = $isVerified;
return $this;
}
public function isVerified(): bool
{
return $this->isVerified;
}
}
EDIT: Solved it. Actually i can do $this->getUser()->getEmail() or $this->getUser()->isVerified(), but the methods just doesn't appear in the autocomplete of my VSCode. Sorry for all of this.
I've been having trouble implementing Symfony's Security features in my project. I have configured my Security.yaml and created a securityController , my Userclass implements userInterface , and from what I can see on the docs I haven't missed anything out. My form renders fine, and I can input my username and password, but when I submit valid credentials it just refreshes the page. Profiler showed that no SQL queries had been made, and despite me configuring authenticationUtils to display errors (as per the tutorial on the docs) nothing is displayed.
Security.yaml
security:
# https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
encoders:
App\Entity\User: sha256
providers:
in_memory: { memory: ~ }
main_db_provider:
entity:
class: App\Entity\User
property: username
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
# anonymous: true
pattern: ^/$ #test
form_login:
login_path: login
check_path: login
csrf_token_generator: security.csrf.token_manager
provider: main_db_provider
# activate different ways to authenticate
# http_basic: true
# https://symfony.com/doc/current/security.html#a-configuring-how-your-users-will-authenticate
# form_login: true
# https://symfony.com/doc/current/security/form_login_setup.html
# Easy way to control access for large sections of your site
# Note: Only the *first* access control that matches will be used
access_control:
# - { path: ^/admin, roles: ROLE_ADMIN }
# - { path: ^/$, roles: ROLE_USER }
Security Controller
<?php
// src/Controller/SecurityController.php
namespace App\Controller;
use App\Entity\User;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
class SecurityController extends Controller
{
/**
* #Route("/login", name="login")
*/
public function login(Request $request, AuthenticationUtils $authenticationUtils)
{
// get the login error if there is one
$error = $authenticationUtils->getLastAuthenticationError();
// last username entered by the user
$lastUsername = $authenticationUtils->getLastUsername();
return $this->render('ad-lotto-theme/login.html.twig', array(
'last_username' => $lastUsername,
'error' => $error,
));
}
}
User class
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Security\Core\User\UserInterface;
/**
* #ORM\Entity(repositoryClass="App\Repository\UserRepository")
* #UniqueEntity("email", message="This email is already in use.")
* #UniqueEntity("username", message="This username is already in use")
*/
class User implements UserInterface, \Serializable
{
/**
* #ORM\Column(name="roles",type="string", length=255)
*/
private $roles;
/**
* #ORM\Column(name="salt",type="string", length=255)
*/
private $salt = "saltyboye";
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(name="username",type="string", length=255, unique=true)
*/
private $username;
/**
* #ORM\Column(name = "password", type="string", length=255)
*/
private $password;
/**
* #ORM\Column(name="email", type="string", length=255, unique=true)
*/
private $email;
/**
* #ORM\Column(type="datetime")
*/
private $registeredOn;
/**
* #ORM\Column(type="integer", nullable=true)
*/
private $referrer;
/**
* #ORM\Column(type="smallint")
*/
private $entries;
/**
* #ORM\Column(type="string", length=3)
*/
private $currency;
/** #see \Serializable::serialize() */
public function serialize()
{
return serialize(array(
$this->registeredOn,
$this->id,
$this->email,
$this->username,
$this->password,
$this->roles,
$this->referrer,
$this->currency,
$this->entries,
$this->salt));
// see section on salt below
// ,
}
public function unserialize($serialized)
{
list (
$this->id,
$this->username,
$this->password,
// see section on salt below
// $this->salt
) = unserialize($serialized, array('allowed_classes' => false));
}
public function eraseCredentials()
{
}
public function getRoles()
{
return array("ROLE_USER");
}
public function getSalt()
{
return $this->salt;
}
public function getId()
{
return $this->id;
}
public function getUsername(): ?string
{
return $this->username;
}
public function setUsername(string $username): self
{
$this->username = $username;
return $this;
}
public function getPassword(): ?string
{
return $this->password;
}
public function setPassword(string $password): self
{
$this->password = $password;
return $this;
}
public function getEmail(): ?string
{
return $this->email;
}
public function setEmail(string $email): self
{
$this->email = $email;
return $this;
}
public function getRegisteredOn(): ?\DateTimeInterface
{
return $this->registeredOn;
}
public function setRegisteredOn(\DateTimeInterface $registeredOn): self
{
$this->registeredOn = $registeredOn;
return $this;
}
public function getReferrer(): ?interedThisWeek
{
return $this->referrer;
}
public function setReferrer(?int $referrer): self
{
$this->referrer = $referrer;
return $this;
}
public function getEntries(): ?bool
{
return $this->entries;
}
public function setEntries(bool $entries): self
{
$this->entries = $entries;
return $this;
}
public function setCurrency(bool $currency): self
{
$this->currency = $currency;
return $this;
}
public function getCurrency(): ?bool
{
return $this->currency;
}
}
that salt is temporary, don't worry :) I haven't figured out how to implement SHA256 yet, but I needed to fill the field in the db :)
I'm assuming you follow this official tutorial: How to Build a Traditional Login Form
First of all your firewall configured to only 1 URI - / because of regex ^/$, so login form and other routes are not under the firewall.
Try to follow the tutorial from start to end just as it says there, make sure everything works and only then make changes.
Hello Silex (and Symfony) experts,
I need to implement a database authentification User/Role model via Doctrine /ORM.
This is my silex composer setup:
"require": {
"silex/web-profiler": "^2.0",
"monolog/monolog": "1.13.*",
"symfony/twig-bridge": "^3.2",
"symfony/monolog-bridge": "^3.2",
"symfony/console": "^3.2",
"symfony/yaml": "^3.2",
"symfony/security-bundle": "^3.2",
"doctrine/orm": "^2.5",
"dflydev/doctrine-orm-service-provider": "^2.0",
"symfony/form": "^3.2",
"symfony/validator": "^3.2",
"symfony/config": "^3.2",
"symfony/doctrine-bridge": "^3.2",
"doctrine/migrations": "^1.5"
},
Users can register. Registered users can login and logout. Non registered visitors have anonymous role.
The symfony profiler is working, so I can see the security status (authentification/authoriszation). I also track the apache logfile for php errors.
I started from here https://github.com/fredjuvaux/silex-orm-user-provider (User from db, roles as array) and tried to expand it to get user roles from database via doctrine many-to-many relation.
There are:
class MyUserController (different user actions like user,edit, register,... )
class MyUserManager implements UserProviderInterface (loadUserByUsername, ...)
class MyUserServiceProvider implements ServiceProviderInterface, ControllerProviderInterface, BootableProviderInterface (controller routing and template setting)
The ORM entities are:
User:
/**
* MyUser
*
* #Entity
* #Table(name="myuser")
*/
class MyUser implements UserInterface, \Serializable
{
....
/**
* #ManyToMany(targetEntity="MyRole", inversedBy="users")
*
*/
private $roles;
...
* Constructor.
*
* #param string $email
*/
public function __construct($email)
{
$this->email = $email;
$this->created = time();
$this->salt = base_convert(sha1(uniqid(mt_rand(), true)), 16, 36);
$this->roles = new ArrayCollection();
}
...
/**
*
* #return ArrayCollection list of the user's roles.
*/
public function getRoles()
{
$result = $this->roles->toArray(); // throws error for login:
// $result = $this->roles; // test // thhrows error : null object
dump($this->roles);
// $result = array("ROLE_USER", "ROLE_OTHER"); // static setting and
works for login
return $result;
}
...
}
Roles (implements Roleinterface)
/**
* MyRole
*
* #Entity
* #Table(name="myrole")
*/
class MyRole implements RoleInterface
{
/**
* #var string
* #Column(name="role", type="string", length=20, unique=true)
*/
private $role;
/**
* #ManyToMany(targetEntity="MyUser", mappedBy="roles")
*/
private $users;
...
/*
* methods for RoleInterface
* #return string|null A string representation of the role, or null
*/
public function getRole()
{
$result = $this->role;
return $result;
}
}
When a user registers, he gets for that session the ROLE_USER role,
authentification and authorisation are ok and a user is created in the
database.
Then I can assign new roles ("role_test1", "role_test2") in the controller for the new user, the many-to-many table myuser_myrole is filled (myuser_id myrole_id).
When I change the roles, they are correctly updated by the entity manager.
When I access the user Entity from the userController to work on it, I can access the assigned roles:
// MyUserController.php
$user = $em->getRepository('MyEntities\MyUser')->find($id);
$roles= $user->getRoles()
$role_length = count($roles);
$role_list = array();
for ($i=0; $i <$role_length ; $i++)
{
array_push($role_list,$roles[$i]->getRole()); // MyRole::getRole() prints out something to screen.
}
printf("<br> role-list:"); dump($role_list);
Calling this controller prints out the assigned roles via MyRole::getRole(), so ORM access works here.
Now comes the strange:
I want to login the new user with the login form.
When I use
// MyUser::getRoles()
return $this->roles;
It throws:
Argument 4 passed to Symfony\\Component\\Security\\Core\\Authentication\\Token\\UsernamePasswordToken::__construct() must be of the type array, object given,
Ok, makes maybe sense because the $roles is an Doctrine ArrayCollection.
When I use
// MyUser::getRoles()
return $this->roles->toArray();
I can login with user password,but am not authenticated (yellow status). Dumping out the roles, I receive an empty array ArrayCollection.
roles:
ArrayCollection {#388 ▼
-elements: []
}
The UsernamePasswordToken has an empty role-array.
When I use
// MyUser::getRoles()
return array("ROLE_HELLO1", "ROLE_HELLO2"); // static role array with strings
I can login and am authenticated with these roles:
Roles
array:2 [▼
0 => "ROLE_HELLO1"
1 => "ROLE_HELLO2"
]
There are old docs about this (Managing Roles in the Database) for symfony 2 http://symfony.com/doc/2.0/cookbook/security/entity_provider.html, but it doesnt work in symfony3.
Here they use
//class User
public function getRoles()
{
return $this->groups->toArray();
}
//class Group extends Role (not RoleInterface, old?)
public function getRole()
{
return $this->role;
}
The actual symfony docs for user management do not show how to use roles stored in database.
In summary:
Login and user/role do not work as expected:
MyUser::getRoles()
does not receive the Roles from database via doctrine ORM.
has to return a string array of roles for login.
delivers the correct role association in another controller.
Questions:
(1) Is this a Silex specific issue?
(2) How to use it correctly or where is a good link/doc for a workaround?
(3) Does the method LoadUserByUsername() interfere with all this?
(4) Do I need a class MyUserRepository extends EntityRepository {} to do the query and get the Role List?
(5) Do I need to use the Role Hierarchy service?
(6) Are there special naming conventions(tablename or class name) for "user" and "role"?
I found many posts asking the same/similar but they do not help here.
Thank you for help, I am really stuck on that!
dirk
Try this:
public function getRoles()
{
return $this->roles->map(function (MyRole $role) {
return $role->getRole();
})->toArray();
}
You should also check if the relationship is correctly saved in database.
If there is ManyToMany relationship between MyUser and MyRole, you have to ensure that relationship is saved in both entities.
//class MyUser
public function addRole(MyRole $role)
{
$this-roles->add($role);
$role->users->add($user);
}
I had a break on this, but now it seems to work. Thank you miikes for the addRole() suggestion!
finally I have: MyUser.php:
//Myuser.php
/**
* MyUser
*
* #Entity
* #Table(name="myuser")
*/
class MyUser implements UserInterface, \Serializable //, ObjectManagerAware
{
...
/**
* #ManyToMany(targetEntity="MyRole", inversedBy="users")
*/
private $roles;
public function __construct($email)
{
(...)
$this->roles = new ArrayCollection();
/**
*
* #return ArrayCollection list of the user's roles.
*/
public function getRoles()
{
$result = $this->roles->toArray();
return $result;
}
public function assignToRole($role)
{
$this->roles[] = $role;
}
public function setRole($myrole)
{
$this->roles= $myrole;
}
public function hasRole($role)
{
return in_array(strtoupper($role), $this->getRoles(), true);
}
public function addRole(MyRole $role)
{
$this->roles->add($role);
//$role->users->addRole($this); // could not access roles->user->...
// because private variable in MyRole but it works
}
/**
* Remove the given role from the user.
*
* #param string $role
*/
public function removeRole($role)
{
dump($role);
$this->roles->removeElement($role);
}
(...) // other setters getters
public function serialize()
{
return serialize(array(
$this->id,
$this->username,
$this->password,
$this->salt,
));
}
/**
* #see \Serializable::unserialize()
*/
public function unserialize($serialized)
{
list (
$this->id,
$this->username,
$this->password,
$this->salt,
) = unserialize($serialized);
}
}
and MyRole.php:
// MyRole.php
/**
* MyRole
*
* #Entity
* #Table(name="myrole")
*/
class MyRole implements RoleInterface
{
(...)
/**
* #ManyToMany(targetEntity="MyUser", mappedBy="roles")
*/
private $users;
/**
* #var string
* #Column(name="role", type="string", length=20, unique=true)
*/
private $role;
/*
* methods for RoleInterface
* #return string|null A string representation of the role, or null
*/
public function getRole()
{
$result = $this->role;
return ($result);
}
public function setRole($role)
{
$this->role= $role;
return $this;
}
(...)
/**
* Constructor
*/
public function __construct()
{
$this->users = new ArrayCollection();
}
/**
* Add user
* #param \MyEntities\MyUser $user
* #return MyRole
*/
public function addUser($user)
{
$this->users[] = $user;
return $this;
}
public function setUser($user)
{
$this->users[] = $user;
return $this;
}
/**
* Remove user
*
* #param \MyEntities\MyUser $user
*/
public function removeUser($user)
{
$this->users->removeElement($user);
}
/**
* Get users
*
* #return ArrayCollection $users
*/
public function getUsers()
{
return $this->users;
}
/**
* __toString()
*
* #return string
*/
public function __toString()
{
return $this->bezeichnung;
}
}
With the help of the doctrine orm commands
vendor/bin/doctrine orm:validate-schema
vendor/bin/doctrine orm:schema-tool:update --dump-sql
the correct manyToMany table myuser_myrole was generated and the role setting works at login of a user.
I think, the most important was the correct use of the function addRole() (with this->roles->add($role), and not something like this->roles->addRole($role) ) to let doctrine do the magic stuff in the background.
Thanks for any help and comments!
dirk
I try to save related entities User and Profile. I use the cascade={"persist"} option. Data saves properly in Database except user_id field in Profile table, its equal to null. When i turn relations profile_id field in User table saves properly. Where is my mistake? Here is my relations:
User entity:
/**
* #ORM\OneToOne(targetEntity="Profile", mappedBy="user", ,cascade={"persist"})
*/
Profile entity:
/**
* #ORM\OneToOne(targetEntity="User", inversedBy="profile", cascade={"persist"})
* #ORM\JoinColumn(name="user_id", referencedColumnName="id")
*/
Getters & setters:
User:
` /**
* #param Profile $profile
*/
public function setProfile(Profile $profile)
{
$this->profile = $profile;
}
/**
* #return Profile
*/
public function getProfile()
{
return $this->profile;
}`
Profile:
`/**
* #param User $user
*/
public function setUser(User $user)
{
$this->user = $user;
}
/**
* #return User
*/
public function getUser()
{
return $this->user;
}`
This is essentially #skler answer but I couldn't explain myself in comments.
Try doing this in your User.php's setProfile():
public function setProfile(Profile $profile)
{
$this->profile = $profile;
$this->profile->setUser($this);
}
Maybe you add only profile in user: $user->addProfile($profile); and you didn't the viceversa: $profile->addUser($user);
I use symfony2 in my project, i have an entity employee that includes all informations of the emlpolyee and also the username and password to do the login to his session. i want to create a secured authentification to the employee but when i check the documentation of FOS Userbundle and SonataUserBundle i see that i should create a new entity USER that includes new usernames and passwords. but i want to use usernames and passwords stored in my entity employee. this is my entity:
employee.php
class employee
{
private $codeEmployee;
//////
private $username;
private $password;
public function getCodeEmployee()
{
return $this->codeEmployee;
}
/////
public function setusername($username)
{
$this->username = $username;
return $this;
}
public function getusername()
{
return $this->username;
}
public function setPassword($password)
{
$this->password = $password;
return $this;
}
public function getPassword()
{
return $this->password;
}
////////
}
how to do that??
The official documentation explains the procedure to do that.
Your Employee class must extend the base User class :
<?php
namespace Foo\BarBundle\Entity;
use FOS\UserBundle\Model\User as BaseUser;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="employees")
*/
class Employee extends BaseUser
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="string", length=255)
*/
protected $codeEmployee;
// Optional
public function __construct()
{
parent::__construct();
// your own logic
}
public function getCodeEmployee()
{
return $this->codeEmployee;
}
public function setCodeEmployee($codeEmployee)
{
$this->codeEmployee = $codeEmployee;
return $this;
}
}
Don't forget to configure FOSUserBundle to choose your user class :
# app/config/config.yml
fos_user:
db_driver: orm
firewall_name: main
user_class: Foo\BarBundle\Entity\Employee
The rest of the configuration (security.yml file, the FOSUserBundle routing files and the other commands to launch) is explained is the documentation.
The documentation also explain how to extend the User entity and to override the form.