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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 02:10:15