简体   繁体   English

如何过滤继承的Doctrine个对象?

[英]How to filter inherited Doctrine objects?

Each Product is "owned" by a given Tenant (ie user) and requires a color which could be either a standard Color available to all tenants or a proprietary TenantOwnedColor which was created by a given tenant and only available to that tenant.每个Product都由给定Tenant (即用户)“拥有”,并且需要一种颜色,该颜色可以是所有租户可用的标准Color ,也可以是给定租户创建且仅供该租户使用的专有TenantOwnedColor

#[ORM\Entity]
class Product implements BelongsToTenantInterface
{
    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column(type: 'integer')]
    private int $id;

    #[ORM\Column(type: 'string', length: 180)]
    private string $name;

    #[ORM\ManyToOne(targetEntity: Color::class)]
    #[ORM\JoinColumn(nullable: false)]
    private ?Color $color;

    #[ORM\ManyToOne(targetEntity: Tenant::class)]
    #[ORM\JoinColumn(nullable: false)]
    private ?Tenant $tenant;
}
#[ORM\Entity]
#[ORM\InheritanceType(value: 'JOINED')]
#[ORM\DiscriminatorColumn(name: 'type', type: 'string')]
#[ORM\DiscriminatorMap(value: ['open' => Color::class, 'proprietary' => TenantOwnedColor::class])]
class Color
{
    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column(type: 'integer')]
    private int $id;

    #[ORM\Column(type: 'string', length: 180)]
    private string $name;

    #[ORM\Column(type: 'string', length: 255)]
    private string $colorCode;
}
#[ORM\Entity]
class TenantOwnedColor extends Color implements BelongsToTenantInterface
{
    #[ORM\ManyToOne(targetEntity: Tenant::class)]
    #[ORM\JoinColumn(nullable: false)]
    private ?Tenant $tenant;
}

In order to filter all entities that implement BelongsToTenantInterface and limit them to the Tenant that the logged on user belongs to, a listener adds a doctrine filter.为了过滤所有实现BelongsToTenantInterface的实体并将它们限制为登录用户所属的Tenant ,侦听器添加了一个 doctrine 过滤器。

namespace App\EventListener;

use Doctrine\ORM\EntityManager;
use Lexik\Bundle\JWTAuthenticationBundle\Event\JWTAuthenticatedEvent;
use App\Entity\MultiTenenacy\BelongsToTenantInterface;

final class AuthenticatedTenantEntityListener
{
    public function __construct(private EntityManager $entityManager)
    {
    }

    public function onJWTAuthenticated(JWTAuthenticatedEvent $jwtAuthenticatedEvent): void
    {
        $user = $jwtAuthenticatedEvent->getToken()->getUser();
        if (!$user instanceof BelongsToTenantInterface) {
            return;
        }

        $this->entityManager
        ->getFilters()
        ->enable('tenant_filter')
        ->setParameter('tenantId', $user->getTenant()->getId());
    }
}
namespace App\Doctrine;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Query\Filter\SQLFilter;
use App\Entity\MultiTenenacy\BelongsToTenantInterface;

final class TenantFilter extends SQLFilter
{
    public function addFilterConstraint(ClassMetadata $classMetadata, $targetTableAlias): string
    {
        if ($classMetadata->getReflectionClass()->implementsInterface(BelongsToTenantInterface::class)) {
            return sprintf('%s.tenant_id = %s', $targetTableAlias, $this->getParameter('tenantId'));
        }        
        return '';
    }
}

My approach works for Product but not for TenantOwnedColor .我的方法适用于Product但不适用于TenantOwnedColor When troubleshooting, I discovered that TenantFilter::addFilterConstraint() is being passed the parent class (ie Color ) metadata which doesn't implement BelongsToTenantInterface and thus I now know why it isn't filtering.在进行故障排除时,我发现TenantFilter::addFilterConstraint()正在传递未实现BelongsToTenantInterface的父 class (即Color )元数据,因此我现在知道它为什么不过滤。

I also found the following in Doctrine's documentation so evidently it is by design:我还在Doctrine 的文档中发现了以下内容,很明显这是设计使然:

In the case of joined or single table inheritance, you always get passed the ClassMetadata of the inheritance root.在连接表或单表 inheritance 的情况下,您始终会获得 inheritance 根的 ClassMetadata。 This is necessary to avoid edge cases that would break the SQL when applying the filters.这是必要的,以避免在应用过滤器时会破坏 SQL 的边缘情况。

Are there other ways to implement this in order to overcome this shortcoming?为了克服这个缺点,还有其他方法可以实现吗?

It seems that this topic has been brought up by the community some times now.社区似乎已经多次提出这个话题。 There does not seem to be an official workaround, due to innestability provoked by those famous edge cases , although some people have made their changes/hacks/workarounds to the problem so it is not impossible .似乎没有正式的解决方法,由于那些著名的edge cases引起的 innestability,尽管有些人已经对问题进行了更改/破解/解决方法,因此这并非不可能

Links that might help, with some workarounds mentioned in them, I hope you find them useful enough, sorry that I cannot be of more help:可能有帮助的链接,其中提到了一些解决方法,我希望你觉得它们足够有用,很抱歉我不能提供更多帮助:

https://github.com/doctrine/orm/issues/7504#issuecomment-568569307 https://github.com/doctrine/orm/issues/7504#issuecomment-568569307

https://github.com/doctrine/orm/issues/6329 https://github.com/doctrine/orm/issues/6329

https://github.com/doctrine/orm/issues/6329#issuecomment-538854316 https://github.com/doctrine/orm/issues/6329#issuecomment-538854316

https://www.doctrine-project.org/projects/doctrine-orm/en/2.11/reference/php-mapping.html#classmetadata-api https://www.doctrine-project.org/projects/doctrine-orm/en/2.11/reference/php-mapping.html#classmetadata-api

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM