Правильные отношения между сущностями пользователей и ролей

#php #frameworks #symfony5

Вопрос:

Мой вопрос более абстрактен, чем задан.

У нас есть две таблицы «Пользователи» и «Роли», и одна из них представляет собой связь между двумя предыдущими. Мой вопрос в том, какова должна быть правильная взаимосвязь между двумя таблицами, чтобы иметь возможность использовать логин и регистрацию из документации Symfony. Я думаю, что мне чего-то не хватает во всей этой концепции. Источник: https://symfony.com/doc/current/security/form_login_setup.html

Заранее спасибо!

РЕДАКТИРОВАТЬ: Я создал таблицы и отношения с помощью терминала доктрины

Сущность пользователя:

 <?php

namespace AppEntity;

use AppRepositoryUserRepository;
use DoctrineCommonCollectionsArrayCollection;
use DoctrineCommonCollectionsCollection;
use DoctrineORMMapping as ORM;
use SymfonyBridgeDoctrineValidatorConstraintsUniqueEntity;
use SymfonyComponentSecurityCoreUserUserInterface;

/**
 * @ORMEntity(repositoryClass=UserRepository::class)
 * @UniqueEntity(fields={"email"}, message="There is already an account with this email")
 */
class User implements UserInterface
{
    public function __construct()
    {
        $this->date_created = new DateTime();
        $this->roles = new ArrayCollection();
    }

    /**
     * @ORMId
     * @ORMGeneratedValue
     * @ORMColumn(type="integer")
     */
    private $id;

    /**
     * @ORMColumn(type="string", length=150, nullable=true)
     */
    private $first_name;

    /**
     * @ORMColumn(type="string", length=150, nullable=true)
     */
    private $last_name;

    /**
     * @ORMColumn(type="string", length=255)
     */
    private $email;

    /**
     * @ORMColumn(type="string", length=255)
     */
    private $password;

    /**
     * @ORMColumn(type="string", length=100)
     */
    private $username;

    /**
     * @ORMColumn(type="boolean")
     */
    private $active = 0;

    /**
     * @ORMColumn(type="datetime")
     */
    private $date_created;

    /**
     * @ORMColumn(type="boolean")
     */
    private $isVerified = false;

    /**
     * @var Collection|Roles[]
     * @ORMManyToMany(targetEntity="Roles")
     * @ORMJoinTable(
     *      name="user_roles",
     *      joinColumns={@ORMJoinColumn(name="user_id", referencedColumnName="id")},
     *      inverseJoinColumns={@ORMJoinColumn(name="role_id", referencedColumnName="id")}
     * )
     */
    private $roles;

    public function getId(): ?int
    {
        return $this->id;
    }

    public function getFirstName(): ?string
    {
        return $this->first_name;
    }

    public function setFirstName(?string $first_name): self
    {
        $this->first_name = $first_name;

        return $this;
    }

    public function getLastName(): ?string
    {
        return $this->last_name;
    }

    public function setLastName(?string $last_name): self
    {
        $this->last_name = $last_name;

        return $this;
    }

    public function getEmail(): ?string
    {
        return $this->email;
    }

    public function setEmail(string $email): self
    {
        $this->email = $email;

        return $this;
    }

    public function getUsername(): ?string
    {
        return $this->username;
    }

    public function setUsername(string $username): self
    {
        $this->username = $username;

        return $this;
    }

    public function getActive(): ?bool
    {
        return $this->active;
    }

    public function setActive(bool $active): self
    {
        $this->active = $active;

        return $this;
    }

    public function getDateCreated(): ?DateTimeInterface
    {
        return $this->date_created;
    }

    public function setDateCreated(DateTimeInterface $date_created): self
    {
        $this->date_created = $date_created;

        return $this;
    }

    /**
     * @param Collection $roles
     */
    public function setRoles(Collection $roles): void
    {
        $this->roles = $roles;
    }

    /**
     * @return Collection
     */
    public function getRoles(): Collection
    {
        return $this->roles;
    }

    /**
     * @param mixed $password
     */
    public function setPassword($password): void
    {
        $this->password = $password;
    }

    public function getPassword()
    {
        return $this->password;
    }

    public function getSalt()
    {
        return null;
    }

    public function eraseCredentials()
    {
        // TODO: Implement eraseCredentials() method.
    }

    public function isVerified(): bool
    {
        return $this->isVerified;
    }

    public function setIsVerified(bool $isVerified): self
    {
        $this->isVerified = $isVerified;

        return $this;
    }

    public function addRole(Roles $role)
    {
        if ($this->roles->contains($role)) {
            return;
        }
        $this->roles->add($role);
    }

    public function removeRole(Roles $role)
    {
        if (!$this->roles->contains($role)) {
            return;
        }

        $this->roles->removeElement($role);
    }

}
 

Сущность ролей:

 <?php

namespace AppEntity;

use AppRepositoryRolesRepository;
use DoctrineORMMapping as ORM;

/**
 * @ORMEntity(repositoryClass=RolesRepository::class)
 */
class Roles
{
    /**
     * @ORMId
     * @ORMGeneratedValue
     * @ORMColumn(type="integer")
     */
    private $id;

    /**
     * @ORMColumn(type="string", length=30)
     */
    private $system_name;

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

    public function getId(): ?int
    {
        return $this->id;
    }

    public function getSystemName(): ?string
    {
        return $this->system_name;
    }

    public function setSystemName(string $system_name): self
    {
        $this->system_name = $system_name;

        return $this;
    }

    public function getName(): ?string
    {
        return $this->name;
    }

    public function setName(string $name): self
    {
        $this->name = $name;

        return $this;
    }
}
 

Here is my LoginFormAuthenticator from Symfony documentantion:

 <?php

namespace AppSecurity;

use AppEntityUser;
use DoctrineORMEntityManagerInterface;
use SymfonyComponentHttpFoundationRedirectResponse;
use SymfonyComponentHttpFoundationRequest;
use SymfonyComponentRoutingGeneratorUrlGeneratorInterface;
use SymfonyComponentSecurityCoreAuthenticationTokenTokenInterface;
use SymfonyComponentSecurityCoreExceptionCustomUserMessageAuthenticationException;
use SymfonyComponentSecurityCoreExceptionInvalidCsrfTokenException;
use SymfonyComponentSecurityCoreSecurity;
use SymfonyComponentSecurityCoreUserUserInterface;
use SymfonyComponentSecurityCoreUserUserProviderInterface;
use SymfonyComponentSecurityCsrfCsrfToken;
use SymfonyComponentSecurityCsrfCsrfTokenManagerInterface;
use SymfonyComponentSecurityGuardAuthenticatorAbstractFormLoginAuthenticator;
use SymfonyComponentSecurityHttpUtilTargetPathTrait;

class LoginFormAuthenticator extends AbstractFormLoginAuthenticator
{
    use TargetPathTrait;

    public const LOGIN_ROUTE = 'app_login';

    private $entityManager;
    private $urlGenerator;
    private $csrfTokenManager;

    public function __construct(EntityManagerInterface $entityManager, UrlGeneratorInterface $urlGenerator, CsrfTokenManagerInterface $csrfTokenManager)
    {
        $this->entityManager = $entityManager;
        $this->urlGenerator = $urlGenerator;
        $this->csrfTokenManager = $csrfTokenManager;
    }

    public function supports(Request $request)
    {
        return self::LOGIN_ROUTE === $request->attributes->get('_route') amp;amp; $request->isMethod('POST');
    }

    public function getCredentials(Request $request)
    {
        $credentials = [
            'email' => $request->request->get('email'),
            'password' => $request->request->get('password'),
            'csrf_token' => $request->request->get('_csrf_token'),
        ];
        $request->getSession()->set(
            Security::LAST_USERNAME,
            $credentials['email']
        );

        return $credentials;
    }

    public function getUser($credentials, UserProviderInterface $userProvider)
    {
        $token = new CsrfToken('authenticate', $credentials['csrf_token']);
        if (!$this->csrfTokenManager->isTokenValid($token)) {
            throw new InvalidCsrfTokenException();
        }
        /** @var User $user */
        $user = $this->entityManager->getRepository(User::class)->findOneBy(['email' => $credentials['email']]);

        if (!$user) {
            // fail authentication with a custom error
            throw new CustomUserMessageAuthenticationException('Email could not be found.');
        }

        return $user;
    }

    public function checkCredentials($credentials, UserInterface $user)
    {
        // Check the user's password or other credentials and return true or false
        // If there are no credentials to check, you can just return true
        return true;
        throw new Exception('TODO: check the credentials inside '.__FILE__);
    }

    public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $providerKey)
    {
        if ($targetPath = $this->getTargetPath($request->getSession(), $providerKey)) {
            return new RedirectResponse($targetPath);
        }

        return new RedirectResponse($this->urlGenerator->generate('index'));
    }

    protected function getLoginUrl()
    {
        return $this->urlGenerator->generate(self::LOGIN_ROUTE);
    }
}
 

Я вручную создаю одного пользователя в базе данных, устанавливаю связи с таблицей ролей, и после попытки входа в систему с этим пользователем возникает ошибка, подобная этой

Аргумент 3, переданный SymfonyComponentSecurityGuardTokenPostAuthenticationGuardToken::__construct() должен иметь тип массива, заданный объект, вызываемый в /var/www/fitter/vendor/symfony/security-guard/AbstractGuardAuthenticator.php на линии 35

По-старому с такими ролями, как [«ROLE_USER», «ROLE_ADMIN»] в приложении «Таблица пользователей», работало, но моя цель-использовать связанные роли.

Я не могу понять, как передать правильный тип ролей, чтобы аутентификатор входа работал с этим потоком.

Комментарии:

1. Абстрактный или нет, ваш вопрос довольно расплывчат. На самом деле вы не представили проблему; что означает «Я думаю, что мне чего-то не хватает во всей концепции»? Вы создаете сущности после создания таблиц (обычно это делается наоборот)? Что заставляет вас думать, что ваши столы не подойдут? С какой частью этой системы у вас возникли проблемы?

2. Я отредактировал сообщение, извините за пропущенную информацию.