symfony - 如何使用基于连接字段的 Doctrine 过滤器?
问题描述
我在我的一个应用程序中使用 ApiPlatform。我刚刚按照这个https://api-platform.com/docs/core/filters/#using-doctrine-orm-filters添加了一个基于登录用户的额外过滤器。
目的是用户只能看到自己的资源,并且效果很好,因为我使用 TokenStorageInterface 获得了当前登录用户的 id。
但是,当您想要过滤存储在 User 实体中的数据以外的其他内容时,如何执行此操作?我不想执行额外的 SQL 查询来检索所需的值。实际上,有时当 ApiPlatform 检索资源时,您不能使用过滤器,因为它缺少连接。所以我想通过添加一个额外的 INNER JOIN 来改变 ApiPlatform 生成的查询。
对这些表和相关资源进行映像:
- 用户:/api/users[/{id}]
- 预订:/api/bookings[/{id}]
- booking_comments:/api/booking/comments[/{id}]
我希望用户只检索其信息。使用 UserAware 类很容易在 User 和 Booking 上执行此操作,因为您拥有登录用户的 userId。但是为了防止有人阅读与其他用户相关的预订评论,我没有找到正确的方法,而在 booking.id = booking_commments.booking_id WHERE booking.user_id = %s 添加额外的内部连接很简单。
表 booking_comments 没有 user_id 字段,我不想将它添加到数据库中。我也不想使用 DataProvider 来构建自定义查询(这是我到目前为止所做的,我认为这不是当前的最先进技术)。
解决方案
我刚刚发现 Doctrine 过滤器与您的问题有关,但我认为它们不适合您的需求,因为 Api 平台利用其自己的扩展系统,该系统在安全页面中提到,这正是您想要做的:过滤您的结果集关于您当前的用户。此外,它可以让您更好地控制过滤(当前资源是什么?当前操作是什么?等)。
过滤所有 Booking 和 BookingComment URL 的结果集的简单示例:
class BookingExtension implements QueryCollectionExtensionInterface, QueryItemExtensionInterface
{
/** @var Security */
private $security;
public function __construct(Security $security)
{
$this->security = $security;
}
public function applyToCollection(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, string $operationName = null)
{
$this->filterResultSet($queryBuilder, $resourceClass);
}
public function applyToItem(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, array $identifiers, string $operationName = null, array $context = [])
{
$this->filterResultSet($queryBuilder, $resourceClass);
}
private function filterResultSet(QueryBuilder $queryBuilder, string $resourceClass)
{
switch ($resourceClass) {
case BookingComment::class:
$comment = $queryBuilder->getRootAliases()[0];
$booking = 'booking';
$queryBuilder->innerJoin("$comment.booking", $booking);
// do not break here !
case Booking::class:
$booking = $booking ?? $queryBuilder->getRootAliases()[0];
$queryBuilder->innerJoin("$booking.user", 'user');
$queryBuilder->andWhere(':user = user');
$queryBuilder->setParameter(':user', $this->security->getUser());
}
}
}
请注意,此示例考虑禁止匿名用户。
推荐阅读
- sql - 替代文本列上的索引视图
- spring - 在 JUnit 5 测试类之间共享数据库连接
- stylecop - 位置记录中的 StyleCopAnalyzers/SA1313:应该禁用吗?
- chapel - 如何使用 Chapel 并行 IO?
- c# - 如何在自定义 FrameworkElement 上设置指针事件样式
- plugins - 追溯签署 Office COM 加载项
- azure - 通过API购买域名
- google-cloud-platform - Google 设备访问 - 获取和访问令牌 - 未经授权
- python - 缩放熊猫系列
- r - 如何在 R 中创建两个连续且具有同意值的条形图