Symfony6 Api Platform扩展问题:按角色过滤设备数据报错
解决方案分三步走
1. 修复语义错误
这个错误是因为Doctrine查询(或自动应用的Filter)中重复使用了o_garden别名,检查以下两处:
- 自定义Doctrine Filter类:如果用Filter自动关联Garden,确认是否在多实体处理中重复定义了
o_garden,改成动态别名或确保每个join用唯一标识。 - Repository查询代码:比如QueryBuilder中多次join Garden时重复用了同一别名,示例错误代码:
修改为不同别名,比如// 错误:两次join共用o_garden别名 $qb->join('g.owner', 'user') ->join('l.garden', 'o_garden') ->join('p.garden', 'o_garden');l_garden、p_garden,或确保只join一次。
2. 补全设备实体与Garden的关联
只有Watering关联了Garden,其他设备必须补上关联才能通过Garden关联用户做权限过滤:
- 以Lawnmower为例,添加关联属性与映射:
// src/Entity/Lawnmower.php use App\Entity\Garden; use Doctrine\ORM\Mapping as ORM; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; /** * @ORM\Entity(repositoryClass=LawnmowerRepository::class) */ class Lawnmower { // ... 其他属性 /** * @ORM\ManyToOne(targetEntity=Garden::class, inversedBy="lawnmowers") * @ORM\JoinColumn(nullable=false) */ private ?Garden $garden = null; // 生成getter/setter public function getGarden(): ?Garden { return $this->garden; } public function setGarden(?Garden $garden): self { $this->garden = $garden; return $this; } } - 在Garden实体中添加反向关联:
// src/Entity/Garden.php /** * @ORM\OneToMany(targetEntity=Lawnmower::class, mappedBy="garden") */ private Collection $lawnmowers; public function __construct() { $this->lawnmowers = new ArrayCollection(); // ... 其他集合初始化 } public function getLawnmowers(): Collection { return $this->lawnmowers; } - 对Lightning、Pool、Portal执行同样操作,确保每个设备都关联Garden。
- 执行数据库迁移:
php bin/console make:migration php bin/console doctrine:migrations:migrate
3. 实现全局权限过滤(复用Garden逻辑)
用Doctrine ORM Filter自动处理权限,避免重复代码:
步骤1:创建权限Filter类
// src/Doctrine/Filter/OwnedByUserFilter.php namespace App\Doctrine\Filter; use App\Entity\User; use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\Query\Filter\SQLFilter; use Symfony\Component\Security\Core\Security; class OwnedByUserFilter extends SQLFilter { private Security $security; public function setSecurity(Security $security): void { $this->security = $security; } public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAlias): string { // 管理员跳过过滤 if ($this->security->isGranted('ROLE_ADMIN')) { return ''; } /** @var User $user */ $user = $this->security->getUser(); if (!$user) { // 未登录用户无权限,返回空结果条件 return "$targetTableAlias.id = 0"; } // 检查实体是否有garden关联 try { $targetEntity->getAssociationMapping('garden'); } catch (\Doctrine\ORM\Mapping\MappingException $e) { // 无garden关联的实体,返回空条件 return ''; } // 构建过滤条件:设备关联的Garden属于当前用户 return "$targetTableAlias.garden_id IN (SELECT id FROM garden WHERE owner_id = {$user->getId()})"; } }
步骤2:配置Filter
在config/packages/doctrine.yaml中添加:
doctrine: orm: filters: owned_by_user: class: App\Doctrine\Filter\OwnedByUserFilter enabled: true
步骤3:注册Filter服务
在config/services.yaml中:
services: App\Doctrine\Filter\OwnedByUserFilter: calls: - [setSecurity, ['@security.helper']] tags: - { name: doctrine.orm.filter }
步骤4:为设备实体启用Filter
在每个设备实体的注解中添加:
/** * @ORM\Entity(repositoryClass=LawnmowerRepository::class) * @ORM\Filter(name="owned_by_user") */ class Lawnmower { // ... }
完成后,ROLE_USER只能看到自己Garden下的设备,ROLE_ADMIN可查看全部设备。
内容的提问来源于stack exchange,提问作者EmmanuelL
相关产品推荐
相关产品推荐

