You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Symfony 4中用Doctrine注解实现多表关联获取用户权限

用Symfony/Doctrine注解实现用户权限获取

咱们先理清楚四张表的关联逻辑:mo_user和mo_role通过中间表mo_user_role是多对多关系(一个用户可以拥有多个角色),mo_role和mo_permission通过中间表mo_role_permission也是多对多关系(一个角色可以关联多个权限)。要实现获取指定用户的权限数组,核心是通过Doctrine注解正确配置这些关联,然后在MoUser实体中封装获取权限的方法。

第一步:配置实体类的多对多关联注解

1. MoUser 实体关联 MoRole

在MoUser类中,添加与MoRole的多对多关联,指定中间表和关联字段:

// src/Entity/MoUser.php
namespace App\Entity;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;

#[ORM\Entity(repositoryClass: MoUserRepository::class)]
#[ORM\Table(name: 'mo_user')]
class MoUser
{
    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column(type: 'integer')]
    private ?int $id = null;

    // 其他业务字段...

    /**
     * 多对多关联MoRole,对应中间表mo_user_role
     */
    #[ORM\ManyToMany(targetEntity: MoRole::class, inversedBy: 'users')]
    #[ORM\JoinTable(name: 'mo_user_role')]
    #[ORM\JoinColumn(name: 'user_id', referencedColumnName: 'id')]
    #[ORM\InverseJoinColumn(name: 'role_id', referencedColumnName: 'id')]
    private Collection $roles;

    public function __construct()
    {
        $this->roles = new ArrayCollection();
    }

    // 生成roles的基础操作方法
    public function getRoles(): Collection
    {
        return $this->roles;
    }
}

2. MoRole 实体关联 MoPermission

接着在MoRole类中,添加与MoPermission的多对多关联:

// src/Entity/MoRole.php
namespace App\Entity;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;

#[ORM\Entity(repositoryClass: MoRoleRepository::class)]
#[ORM\Table(name: 'mo_role')]
class MoRole
{
    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column(type: 'integer')]
    private ?int $id = null;

    // 其他业务字段...

    /**
     * 反向关联MoUser(可选,若需要从角色维度查询用户则保留)
     */
    #[ORM\ManyToMany(targetEntity: MoUser::class, mappedBy: 'roles')]
    private Collection $users;

    /**
     * 多对多关联MoPermission,对应中间表mo_role_permission
     */
    #[ORM\ManyToMany(targetEntity: MoPermission::class, inversedBy: 'roles')]
    #[ORM\JoinTable(name: 'mo_role_permission')]
    #[ORM\JoinColumn(name: 'role_id', referencedColumnName: 'id')]
    #[ORM\InverseJoinColumn(name: 'permission_id', referencedColumnName: 'id')]
    private Collection $permissions;

    public function __construct()
    {
        $this->users = new ArrayCollection();
        $this->permissions = new ArrayCollection();
    }

    // 生成permissions的基础操作方法
    public function getPermissions(): Collection
    {
        return $this->permissions;
    }
}

3. MoPermission 实体

确保MoPermission中有存储权限标识的字段(比如code,对应你需要的view_summary_report这类值):

// src/Entity/MoPermission.php
namespace App\Entity;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;

#[ORM\Entity(repositoryClass: MoPermissionRepository::class)]
#[ORM\Table(name: 'mo_permission')]
class MoPermission
{
    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column(type: 'integer')]
    private ?int $id = null;

    /**
     * 权限唯一标识,如'view_summary_report'
     */
    #[ORM\Column(type: 'string', length: 255, unique: true)]
    private ?string $code = null;

    // 其他业务字段(如权限名称、描述等)...

    /**
     * 反向关联MoRole(可选)
     */
    #[ORM\ManyToMany(targetEntity: MoRole::class, mappedBy: 'permissions')]
    private Collection $roles;

    public function __construct()
    {
        $this->roles = new ArrayCollection();
    }

    // 生成code的getter方法
    public function getCode(): ?string
    {
        return $this->code;
    }
}

第二步:在MoUser中实现getPermissions()方法

现在可以在MoUser类中添加一个方法,遍历所有关联角色的权限,提取code并去重(避免不同角色重复权限):

// 在MoUser类中新增以下方法
public function getPermissions(): array
{
    $permissionCodes = [];
    foreach ($this->getRoles() as $role) {
        foreach ($role->getPermissions() as $permission) {
            $code = $permission->getCode();
            if (!in_array($code, $permissionCodes)) {
                $permissionCodes[] = $code;
            }
        }
    }
    return $permissionCodes;
}

第三步:查询用户并获取权限

在业务代码(比如Controller或Service)中,你可以这样获取ID为9144的用户权限:

use App\Entity\MoUser;
use Doctrine\ORM\EntityManagerInterface;

// 注入EntityManagerInterface
public function getUserPermissions(EntityManagerInterface $em)
{
    $user = $em->getRepository(MoUser::class)->find(9144);
    if (!$user) {
        // 处理用户不存在的场景
        return [];
    }
    // 这里就能得到你预期的权限数组
    $userPermissions = $user->getPermissions();
}

性能优化提示

如果担心懒加载导致的N+1查询问题(比如加载用户后,每个角色都要单独查询权限),可以在MoUserRepository中添加自定义查询方法,一次性加载关联数据:

// src/Repository/MoUserRepository.php
namespace App\Repository;

use App\Entity\MoUser;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;

class MoUserRepository extends ServiceEntityRepository
{
    public function __construct(ManagerRegistry $registry)
    {
        parent::__construct($registry, MoUser::class);
    }

    /**
     * 加载用户并一次性加载关联的角色和权限
     */
    public function findUserWithPermissions(int $userId): ?MoUser
    {
        return $this->createQueryBuilder('u')
            ->leftJoin('u.roles', 'r')
            ->leftJoin('r.permissions', 'p')
            ->addSelect('r', 'p') // 主动选择关联数据,避免懒加载
            ->where('u.id = :id')
            ->setParameter('id', $userId)
            ->getQuery()
            ->getOneOrNullResult();
    }
}

使用这个方法查询:

$user = $em->getRepository(MoUser::class)->findUserWithPermissions(9144);
$userPermissions = $user->getPermissions();

这样就可以避免多次查询数据库,提升整体性能。

内容的提问来源于stack exchange,提问作者ShanjayG

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 07:19:46