【发布时间】:2018-08-11 09:37:31
【问题描述】:
目前我正在尝试修改我的类并寻找保存用户和角色之间动态关系的想法。
我想在加载固定装置时创建关联,并在需要创建具有关系的用户时在控制器中具有这样的功能,例如:
...
$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();
我的用户.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) 上有主键。有什么建议吗?
灵活添加/删除用户+角色+角色用户的解决方案
非常感谢!
【问题讨论】:
-
无意冒犯,但我认为您可能在一个问题上问得太多了。由于一些额外的原因,查看how to ask a good question 和this question 可能会有所帮助。您的问题主题与您最后提出的问题无关,并且您有几个问题,因此解释所有细节将是一个相当大的答案。我会将这个问题分成单独的具体问题。
标签: php orm foreign-keys fixtures symfony4