[英]How to filter inherited Doctrine objects?
每個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;
}
為了過濾所有實現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 '';
}
}
我的方法適用於Product
但不適用於TenantOwnedColor
。 在進行故障排除時,我發現TenantFilter::addFilterConstraint()
正在傳遞未實現BelongsToTenantInterface
的父 class (即Color
)元數據,因此我現在知道它為什么不過濾。
我還在Doctrine 的文檔中發現了以下內容,很明顯這是設計使然:
在連接表或單表 inheritance 的情況下,您始終會獲得 inheritance 根的 ClassMetadata。 這是必要的,以避免在應用過濾器時會破壞 SQL 的邊緣情況。
為了克服這個缺點,還有其他方法可以實現嗎?
社區似乎已經多次提出這個話題。 似乎沒有正式的解決方法,由於那些著名的edge cases
引起的 innestability,盡管有些人已經對問題進行了更改/破解/解決方法,因此這並非不可能。
可能有幫助的鏈接,其中提到了一些解決方法,我希望你覺得它們足夠有用,很抱歉我不能提供更多幫助:
https://github.com/doctrine/orm/issues/7504#issuecomment-568569307
https://github.com/doctrine/orm/issues/6329
https://github.com/doctrine/orm/issues/6329#issuecomment-538854316
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.