首页 > 解决方案 > 根据经过身份验证的用户自动过滤属性

问题描述

我有一个名为事件的实体,该事件可以有很多房间,一个房间可以有很多参与者。如果我访问所有事件(与特定用户),我可以使用扩展过滤用户没有访问权限的事件(没有与特定用户连接的空间)。

这很好用。响应包含至少有一个房间具有访问权限的所有事件。

但是如果活动有多个房间并且用户只能访问一个房间。响应包括两个房间。我创建了一个 RoomExtension,但不会调用这个类。

谢谢

标签: api-platform.com

解决方案


您的问题是由于过滤器和扩展仅适用于检索主要实体的查询。使用 Doctrine 关联检索相关实体,该关联是域模型的一部分,旨在成为所有目的的单一事实来源。您需要的是该模型的用户特定视图,在 api 的上下文中通常由 DTO 组成。

我认为基本上有两种解决方案:

  1. 主要查询事件并将其转换为 EventDTO,然后查询或过滤掉相关的房间,
  2. 主要查询房间,然后将它们分组到 EventDTO。

我在这里解释第二个解决方案,因为我猜它更简单,它应该让你的 RoomExtension 开箱即用,这使它更适合你的问题,但也因为我碰巧在教程中构建和测试了类似的东西因此,自信地写下答案的工作量要少得多。

这个解决方案的缺点是它不支持分页。

因为这个解决方案主要是查询房间,操作是在房间资源上的。如果它是 Room 的唯一 collectionOperation 可能是这样的:

(..)
 * @ApiResource(
 *     collectionOperations={
 *         "get_accessible_events"={
 *             "method"="GET",
 *             "path"="/rooms/accessible-events",
 *             "output"=EventDTO::class,
 *             "pagination_enabled"=false
 *         }
 *     }
 * }
 */
class Room {
(..) 

(这不一定是你唯一的收藏操作,你仍然可以有“get”、“post”等)。

现在这仍然会产生一个平面的房间集合,你需要将它们分组到 EventDTO 中。文档的DTO 页面建议使用 DataTransformer 来生成 DTO,但这仅在您的 DTO 与查询检索到的实体一对一的情况下才有效。但是 CollectionDataProvider 可以做到这一点。因为您不需要调整查询本身,您可以简单地装饰默认的 CollectionDataProvider 服务:

namespace App\DataProvider;

use ApiPlatform\Core\Api\OperationType;
use App\DTO\EventDTO;
use ApiPlatform\Core\DataProvider\ContextAwareCollectionDataProviderInterface;
use ApiPlatform\Core\DataProvider\CollectionDataProviderInterface;
use ApiPlatform\Core\DataProvider\RestrictedDataProviderInterface;
use App\Entity\Room;

class RoomAccessibleEventCollectionDataProvider implements ContextAwareCollectionDataProviderInterface, RestrictedDataProviderInterface
{
    /** @var CollectionDataProviderInterface */
    private $dataProvider;

    /**
     * @param CollectionDataProviderInterface $dataProvider The built-in orm CollectionDataProvider of API Platform
     */
    public function __construct(CollectionDataProviderInterface $dataProvider)
    {
        $this->dataProvider = $dataProvider;
    }

    /**
     * {@inheritdoc}
     */
    public function supports(string $resourceClass, string $operationName = null, array $context = []): bool
    {
        return Room::class === $resourceClass
            && $operationName == 'get_accessible_events';
    }

    /**
     * {@inheritdoc}
     */
    public function getCollection(string $resourceClass, string $operationName = null, array $context = []): array
    {

        $rooms = $this->dataProvider->getCollection($resourceClass, $operationName, $context);

        $dtos = [];
        foreach ($rooms as $room) {
            $key = $room->getId();
            if (isset($dtos[$key])) {
                $dtos[$key]->addRoom($room);
            } else {
                $dto = new EventDTO($room->getEvent());
                $dto->addRoom($room);
                $dtos[$key] = $dto;
            }

        }
 
        return $dtos;
    }
}

您确实需要在 config/services.yaml 中配置服务:

'App\DataProvider\RoomAccessibleEventCollectionDataProvider':参数:$dataProvider:'@api_platform.doctrine.orm.default.collection_data_provider'

这不会替换默认的 CollectionDataProvider,而是添加另一个注入默认的 CollectionDataProvider。

我想你现在可以自己制作 EventDTO 类了。然后它应该工作。在房间上定义的过滤器也将照常工作,例如,如果房间可以按活动日期过滤?event.date[gte]=2020-10-10 将仅查找在 2020-10-10 或之后举行活动的房间并返回他们的 EventDTO。

但是,在 swagger 文档中,get_accessible_events 操作摘要和描述仍然来自 Room。您可以查看如何在文档中添加 SwaggerDecorator 或查看教程的 chapter9-api 分支。后者还包含实体的完整解释和测试代码、DTO(报告模型)和仅显示用户被授权的数据的扩展,但不是针对您的问题而定制的,并且所有这些都超出了重点回答。

关于其他解决方案,我无法在此站点上向您提供更多提示,因为该站点可能会将它们视为不完整或不清楚的答案,并因此而惩罚我。


推荐阅读