当前,我正在尝试修改类,并寻找节省用户和角色之间动态关系的想法。
我想在加载夹具时创建关联,并且还需要在需要创建具有关系的用户时在控制器中具有这种功能,例如:
...
$user = new User();
$user->setName($_POST['name']);
$user->setPassword($_POST['password']);
...
$user->setRole('ROLE_USER');//Role for everyone
...
$role = new Role();
$role->setName('ROLE_' . strtoupper($_POST['name']) );//Role for personal use
...
//Here need to implement user+role association (I'm looking for recommendations)
...
$entityManager->persist($user);
$entityManager->persist($role);
//Persist role+user assosiacion
$entityManager->flush();
$entityManager->clear();
我的User.php:
<?php
namespace App\Entity;
use DateTime;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Security\Core\User\UserInterface;
/**
* User
*
* @ORM\Table(name="user", uniqueConstraints={@ORM\UniqueConstraint(name="user_name", columns={"user_name"}), @ORM\UniqueConstraint(name="email", columns={"email"})})
* @ORM\Entity(repositoryClass="App\Repository\UserRepository")
* @ORM\Cache(usage="NONSTRICT_READ_WRITE", region="fast_cache")
* @UniqueEntity(fields="email", message="Email already taken")
* @UniqueEntity(fields="username", message="Username already taken")
*/
class User implements UserInterface, \Serializable
{
/**
* @var ArrayCollection
*
* @ORM\ManyToMany(targetEntity="App\Entity\Role", inversedBy="users", cascade={"remove"})
* @ORM\JoinTable(name="users_roles",
* joinColumns={@ORM\JoinColumn(name="user_id", referencedColumnName="id")},
* inverseJoinColumns={@ORM\JoinColumn(name="role_id", referencedColumnName="id")}
* )
*/
protected $roles;
/**
* @var int
*
* @ORM\Column(name="id", type="smallint", nullable=false, options={"unsigned"=true})
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @var string
*
* @ORM\Column(name="user_name", type="string", length=255, nullable=false)
*/
private $username;
/**
* @var string
*
* @ORM\Column(name="email", type="string", length=255, nullable=false)
*/
private $email;
/**
* @var string
*
* @ORM\Column(name="password", type="string", length=255, nullable=false)
*/
private $password;
/**
* @var bool
*
* @ORM\Column(name="is_enabled", type="boolean", nullable=false)
*/
private $isEnabled;
/**
* @var bool
*
* @ORM\Column(name="is_verified", type="boolean", nullable=false)
*/
private $isVerified;
/**
* @var DateTime
*
* @ORM\Column(name="created_at", type="datetime", nullable=false)
*/
private $createdAt;
/**
* @var DateTime
*
* @ORM\Column(name="updated_at", type="datetime", nullable=false)
*/
private $updatedAt;
/**
* @return int
*/
public function getId(): int
{
return $this->id;
}
/**
* @return string
*/
public function getEmail(): string
{
return $this->email;
}
/**
* @param string $email
*/
public function setEmail(string $email): void
{
$this->email = $email;
}
/**
* @return bool
*/
public function isEnabled(): bool
{
return $this->isEnabled;
}
/**
* @param bool $isEnabled
*/
public function setIsEnabled(bool $isEnabled): void
{
$this->isEnabled = $isEnabled;
}
/**
* @return bool
*/
public function isVerified(): bool
{
return $this->isVerified;
}
/**
* @param bool $isVerified
*/
public function setIsVerified(bool $isVerified): void
{
$this->isVerified = $isVerified;
}
/**
* @return DateTime
*/
public function getCreatedAt(): DateTime
{
return $this->createdAt;
}
/**
* @param DateTime $createdAt
*/
public function setCreatedAt(DateTime $createdAt): void
{
$this->createdAt = $createdAt;
}
/**
* @return DateTime
*/
public function getUpdatedAt(): DateTime
{
return $this->updatedAt;
}
/**
* @param DateTime $updatedAt
*/
public function setUpdatedAt(DateTime $updatedAt): void
{
$this->updatedAt = $updatedAt;
}
/**
* String representation of object
* @link http://php.net/manual/en/serializable.serialize.php
* @return string the string representation of the object or null
* @since 5.1.0
* NOTE: SYNFONY BUG 3.4 -> 4.1; https://github.com/symfony/symfony-docs/pull/9914
*/
public function serialize(): string
{
// add $this->salt too if you don't use Bcrypt or Argon2i
return serialize([$this->id, $this->username, $this->password]);
}
/**
* Constructs the object
* @link http://php.net/manual/en/serializable.unserialize.php
* @param string $serialized <p>
* The string representation of the object.
* </p>
* @return void
* @since 5.1.0
*/
public function unserialize($serialized): void
{
// add $this->salt too if you don't use Bcrypt or Argon2i
[$this->id, $this->username, $this->password] = unserialize($serialized, ['allowed_classes' => false]);
}
/**
* Returns the roles granted to the user.
*
* <code>
* public function getRoles()
* {
* return array('ROLE_USER');
* }
* </code>
*
* Alternatively, the roles might be stored on a ``roles`` property,
* and populated in any number of different ways when the user object
* is created.
*
* @return array The user roles
*/
public function getRoles(): array
{
$roles = [];
foreach ($this->roles->toArray() AS $role) {
$roles[] = $role->getName();
}
return $roles;
}
/**
* Returns the password used to authenticate the user.
*
* This should be the encoded password. On authentication, a plain-text
* password will be salted, encoded, and then compared to this value.
*
* @return string The password
*/
public function getPassword(): string
{
return $this->password;
}
/**
* @param string $password
*/
public function setPassword(string $password): void
{
$this->password = $password;
}
/**
* Returns the salt that was originally used to encode the password.
*
* This can return null if the password was not encoded using a salt.
*
* @return string|null The salt
*/
public function getSalt()
{
// See "Do you need to use a Salt?" at https://symfony.com/doc/current/cookbook/security/entity_provider.html
// we're using bcrypt in security.yml to encode the password, so
// the salt value is built-in and you don't have to generate one
return null;
}
/**
* Returns the username used to authenticate the user.
*
* @return string The username
*/
public function getUsername()
{
return $this->username;
}
/**
* @param string $username
*/
public function setUsername(string $username): void
{
$this->username = $username;
}
/**
* Removes sensitive data from the user.
*
* This is important if, at any given point, sensitive information like
* the plain-text password is stored on this object.
*/
public function eraseCredentials()
{
// if you had a plainPassword property, you'd nullify it here
$this->plainPassword = null;
}
}
Role.php文件:
<?php
namespace App\Entity;
use DateTime;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
/**
* Role
*
* @ORM\Table(name="role", uniqueConstraints={@ORM\UniqueConstraint(name="name", columns={"name"})})
* @ORM\Entity(repositoryClass="App\Repository\RoleRepository")
*/
class Role
{
/**
* @var ArrayCollection
*
* @ORM\ManyToMany(targetEntity="App\Entity\User", mappedBy="roles", cascade={"remove"})
* @ORM\JoinTable(name="users_roles",
* joinColumns={@ORM\JoinColumn(name="role_id", referencedColumnName="id")},
* inverseJoinColumns={@ORM\JoinColumn(name="user_id", referencedColumnName="id")}
* )
*/
protected $users;
/**
* @var int
*
* @ORM\Column(name="id", type="smallint", nullable=false, options={"unsigned"=true})
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @var string
*
* @ORM\Column(name="name", type="string", length=255, nullable=false)
*/
private $name;
/**
* @var DateTime
*
* @ORM\Column(name="created_at", type="datetime", nullable=false)
*/
private $createdAt;
/**
* @var DateTime
*
* @ORM\Column(name="updated_at", type="datetime", nullable=false)
*/
private $updatedAt;
/**
* Role constructor.
*/
public function __construct()
{
$this->users = new ArrayCollection();
}
/**
* @return array
*/
public function getUsers(): array
{
return $this->users->toArray();
}
/**
* @return int
*/
public function getId(): int
{
return $this->id;
}
/**
* @param int $id
*/
public function setId(int $id): void
{
$this->id = $id;
}
/**
* @return string
*/
public function getName(): string
{
return $this->name;
}
/**
* @param string $name
*/
public function setName(string $name): void
{
$this->name = $name;
}
/**
* @return DateTime
*/
public function getCreatedAt(): DateTime
{
return $this->createdAt;
}
/**
* @param DateTime $createdAt
*/
public function setCreatedAt(DateTime $createdAt): void
{
$this->createdAt = $createdAt;
}
/**
* @return DateTime
*/
public function getUpdatedAt(): DateTime
{
return $this->updatedAt;
}
/**
* @param DateTime $updatedAt
*/
public function setUpdatedAt(DateTime $updatedAt): void
{
$this->updatedAt = $updatedAt;
}
}
我的数据固定装置AppFixtures.php:
<?php
namespace App\DataFixtures;
use App\Entity\Role;
use App\Entity\User;
use DateTime;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Common\Persistence\ObjectManager;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
/**
* Class AppFixtures
* @package App\DataFixtures
*/
class AppFixtures extends Fixture
{
/**
* @var UserPasswordEncoderInterface
*/
private $encoder;
/**
* @var EntityManagerInterface
*/
private $entityManager;
/**
* AppFixtures constructor.
* @param UserPasswordEncoderInterface $userPasswordEncoder
* @param EntityManagerInterface $entityManager
*/
public function __construct(UserPasswordEncoderInterface $userPasswordEncoder, EntityManagerInterface $entityManager)
{
$this->encoder = $userPasswordEncoder;
$this->entityManager = $entityManager;
}
/**
* @param ObjectManager $manager
*/
public function load(ObjectManager $manager)
{
//Creating default roles
$role = new Role();
$role->setName('ROLE_USER');
$role->setCreatedAt(new DateTime());
$role->setUpdatedAt(new DateTime());
$manager->persist($role);
$role = new Role();
$role->setName('ROLE_MODERATOR');
$role->setCreatedAt(new DateTime());
$role->setUpdatedAt(new DateTime());
$manager->persist($role);
$role = new Role();
$role->setName('ROLE_ADMIN');
$role->setCreatedAt(new DateTime());
$role->setUpdatedAt(new DateTime());
$manager->persist($role);
$manager->flush();
$manager->clear();
//Creating users
$user = new User();
$user->setUserName('john');
$user->setEmail('john@localhost');
//$user->setRoles(['ROLE_USER', 'ROLE_JOHN']);
//$roleAssociation = null;
$user->setPassword($this->encoder->encodePassword($user, 'test'));
$user->setCreatedAt(new DateTime());
$user->setUpdatedAt(new DateTime());
$user->setIsVerified(true);
$user->setIsEnabled(true);
//$manager->persist($roleAssociation);
$manager->persist($user);
$user = new User();
$user->setUserName('tom');
$user->setEmail('tom@localhost');
//$user->setRoles(['ROLE_USER', 'ROLE_TOM', 'ROLE_MODERATOR']);
//$roleAssociation = null;
$user->setPassword($this->encoder->encodePassword($user, 'test'));
$user->setCreatedAt(new DateTime());
$user->setUpdatedAt(new DateTime());
$user->setIsVerified(true);
$user->setIsEnabled(true);
//$manager->persist($roleAssociation);
$manager->persist($user);
$user = new User();
$user->setUserName('jimmy');
$user->setEmail('jimmy@localhost');
//$user->setRoles(['ROLE_USER', 'ROLE_JIMMY', 'ROLE_ADMIN']);
//$roleAssociation = null;
$user->setPassword($this->encoder->encodePassword($user, 'test'));
$user->setCreatedAt(new DateTime());
$user->setUpdatedAt(new DateTime());
$user->setIsVerified(true);
$user->setIsEnabled(true);
//$manager->persist($roleAssociation);
$manager->persist($user);
$manager->flush();
$manager->clear();
}
}
我正在寻找建议:
用户实体被缓存在注释中,因为每个请求上的symfony都将其加载。配置部分:
orm:
metadata_cache_driver:
type: redis
result_cache_driver:
type: redis
query_cache_driver:
type: redis
我正在Redis中缓存所有数据1分钟。有更好的解决方案吗?
DB模式在users_roles上创建外键(FK)和额外索引 桌子,我很想不要FK,因为恕我直言,这是“沉重的”事情。我希望仅在(user_id,role_id)上具有主键。有什么建议吗?
为用户+角色+ Roles_users灵活添加/删除的解决方案
非常感谢!
答案 0 :(得分:0)
我会尽力回答这个问题,但是正如我在评论中提到的那样,这个问题有点大,所以其中一些可能会被普遍化。
- 用户实体被缓存在注释中,因为在每个请求上使用symfony 加载它。配置部分:
orm: metadata_cache_driver: type: redis result_cache_driver: type: redis query_cache_driver: type: redis
我正在Redis中缓存所有数据1分钟。有更好的解决方案吗?
在这里说“更好”时,这是主观的。每个应用程序都是不同的。缓存以及缓存保持活动的时间长短取决于每个应用程序的要求。我不知道这是否本质上是错误,但这很大程度上取决于您的应用程序要求。
- 数据库架构在users_roles表上创建外键(FK)和其他索引,我希望不要使用FK,因为恕我直言 这是“沉重的”事情。我希望在主键上 (user_id,role_id)仅。有什么建议吗?
首先,FK非常重要,它们旨在保持数据的完整性。它们确保如果有人尝试删除与user_roles
行链接的用户或角色,则该操作将失败。通常,您希望这样做是为了避免丢失数据或创建孤立数据。
第二,我不确定您使用的是哪个版本的Doctrine,但是我的类似ManyToMany表做在(user_id, role_id)
上创建了PK和唯一索引。
- 为用户+角色+ Roles_users灵活添加/删除的解决方案
您可以使用Doctrine的ArrayCollections(在菜单中单击“收藏”以确保锚点链接有效,有时它们会断开)来实现此目的。
不过,使用默认的Symfony User
实体进行此操作时有一个警告。它实现了Symfony\Component\Security\Core\User\UserInterface
接口,该接口定义了getRoles()
方法,该方法用于将角色数组作为字符串返回。这样,某些Symfony安全功能将按预期工作。这意味着,如果您拥有private $roles
属性,则必须将其标准Doctrine getter重命名为其他名称,以便可以使getRoles()
正常运行。
因此,对于User
实体,我通常只是将自己的getter,setter,adder和remover重命名为getUserRoles()
,setUserRoles()
,addUserRole()
和{ {1}},然后离开removeUserRole()
来实现预期的接口。
这是用户类的不完整示例,没有Role类示例。
getRoles()
如果需要,您也可以对<?php
declare(strict_types=1);
namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection/*Interface*/;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\UserInterface;
use \InvalidArgumentException;
use function array_unique;
class User implements UserInterface
{
/* ... */
/**
* @var Role[]|array User's roles
*
* Note that the getter, setter, adder, and remover for this method are renamed so that the getRoles() method that
* we implement from Symfony\Component\Security\Core\User\UserInterface can function as expected.
*
* @see UserInterface
* @see User::getRoles()
* @see User::getUserRoles()
* @see User::setUserRoles()
* @see User::addUserRole()
* @see User::removeUserRole()
*
* @ORM\ManyToMany(targetEntity="App\Entity\Role", inversedBy="users", cascade={"persist"})
*/
private $roles;
/* ... */
/**
* Constructor
*/
public function __construct()
{
$this->roles = new ArrayCollection();
}
/* ... */
/**
* @return Collection|Role[]
*/
public function getUserRoles(): Collection
{
return $this->roles;
}
/**
* @param Collection|Role[] $roles
*
* @return self
*/
public function setUserRoles(Collection/*Interface*/ $roles): self
{
$this->roles = $roles;
return $this;
}
/**
* @param Role $role
*
* @return self
*/
public function addUserRole(Role $role): self
{
$this->roles->add($role);
return $this;
}
/**
* @param Role $role
*
* @return self
*/
public function removeUserRole(Role $role): self
{
$this->roles->removeElement($role);
return $this;
}
/**
* Get array of roles as strings
*
* This method is an implementation of UserInterface::getRoles(). The getter for self::$roles is
* self::getUserRoles().
*
* @return string[]|array
*
* @see UserInterface
*/
public function getRoles()
{
$roleStrings = [];
foreach ($this->roles as $role) {
$roleStrings[] = $role->getName();
}
// guarantee every user at least has ROLE_USER
$roleStrings[] = 'ROLE_USER';
return array_unique($roleStrings);
}
/* ... */
}
对象执行相反的操作,但这取决于是否要以这种方式将用户添加到角色中,并且通常最好选择关系的所有者。 / p>
这里是一个示例,说明如何在您使用实体,固定装置或其他方式的任何地方使用它。
Role