簡體   English   中英

Symfony 4 - 為動態用戶角色加載裝置

[英]Symfony 4 - load fixtures for dynamic user roles

目前我正在嘗試修改我的類並尋找保存用戶和角色之間動態關系的想法。

我想在加載裝置時創建關聯,並在需要創建具有關系的用戶時在控制器中具有這樣的功能,例如:

...
$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;
        }
    }

角色.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();
        }
    }

我正在尋找以下方面的建議:

  1. 用戶實體緩存在注解中,因為每個請求的 symfony 都會加載它。 配置部分:

     orm: metadata_cache_driver: type: redis result_cache_driver: type: redis query_cache_driver: type: redis

我在 redis 中緩存所有數據 1 分鍾。 有什么更好的解決辦法嗎?

  1. DB 模式在 users_roles 表上創建外鍵 (FK) 和額外的索引,我希望沒有 FK,因為恕我直言,這是“重”的事情。 我寧願只在 (user_id,role_id) 上使用主鍵。 這方面有什么建議嗎?

  2. user+role+roles_users靈活增刪解決方案

十分感謝!

我會嘗試回答這個問題,但正如我在評論中提到的,這個問題有點大,所以其中一些可能是概括的。

  1. 用戶實體緩存在注解中,因為每個請求的 symfony 都會加載它。 配置部分:
 orm: metadata_cache_driver: type: redis result_cache_driver: type: redis query_cache_driver: type: redis

我在 redis 中緩存所有數據 1 分鍾。 有什么更好的解決辦法嗎?

當你在這里說“更好”時,這是主觀的。 每個應用程序都是不同的。 緩存以及緩存保持活動的長度特定於每個應用程序的要求。 我不知道這是否有任何本質上的錯誤,但這在很大程度上取決於您的應用程序的要求。

  1. DB 模式在 users_roles 表上創建外鍵 (FK) 和額外的索引,我希望沒有 FK,因為恕我直言,這是“重”的事情。 我寧願只在 (user_id,role_id) 上使用主鍵。 這方面有什么建議嗎?

首先,FK 很重要,它們旨在保持數據的完整性。 它們確保如果有人試圖刪除user_roles行鏈接到的用戶或角色,操作將失敗。 您通常希望這種行為不會丟失數據或創建孤立數據。

其次,我不確定您使用的是什么版本的 Doctrine,但我類似的 ManyToMany 表確實(user_id, role_id)上創建了 PK 和唯一索引。

  1. user+role+roles_users靈活增刪解決方案

您可以通過使用 Doctrine 的ArrayCollections (單擊菜單中的 Collections 以確保錨鏈接有效,它們有時會損壞)來完成此操作。

但是,在使用默認的 Symfony User實體執行此操作時有一個警告。 它實現了Symfony\\Component\\Security\\Core\\User\\UserInterface接口,該接口定義了一個getRoles()方法,該方法旨在將角色數組作為字符串返回。 這是為了使某些 Symfony 安全功能按預期工作。 這意味着,如果您有一個private $roles屬性,您必須將其標准 Doctrine getter 重命名為其他名稱,以便您可以讓getRoles()按預期運行。

因此,對於User實體,我通常只是將我的 getter、setter、adder 和 remover 重命名為getUserRoles()setUserRoles()addUserRole()removeUserRole() ,然后我離開getRoles()來實現預期的界面。

這是一個沒有 Role 類示例的用戶類的不完整示例。

<?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);
    }

    /* ... */
}

如果您願意,您也可以對Role對象執行相反的操作,但這取決於您是否希望以這種方式將用戶添加到角色,並且通常最好選擇關系的擁有方。

下面是一個示例,說明如何在您使用實體、裝置或其他方式的任何地方使用它。

<?php

use Doctrine\Common\Collections\ArrayCollection;
use App\Entity\User;
use App\Entity\Role;

$entityManager = (get entity manager);

$user = new User();

// You can also add the name as an argument to Role::__construct()
// then call from setName() from that if you wanted to simplify this.
$roleFoo = (new Role())->setName('ROLE_FOO');
$roleBar = (new Role())->setName('ROLE_BAR');

// set roles using ArrayCollection of roles.
$user->setUserRoles(new ArrayCollection($roleFoo, $roleBar));

// add new role, see User::addUserRole() for 
$user->addUserRole((new Role()->setName('ROLE_WIN'));

// remove ROLE_BAR
// You can also do this with entities that you find with Doctrine
// if you want to remove them from a persisted collection.
$user->removeUserRole($roleBar);

// get roles as a collection of Role objects.
// This will return either ArrayCollection or PersistentCollection
// depending on context. These are objects that act like arrays
// and each element will be a Role object.
$roles = $user->getUserRoles();

// get roles as strings... mostly used by Symfony's security system
// but could be used by your app too.
$roleStrings = $user->getRoles();

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM