如何在API Platform中设置路径变量
在API Platform中配置带路径变量的集合操作路径
看起来你已经在正确的方向上配置了带路径变量的集合操作,但要让{shop}变量真正生效并正确过滤数据,还需要补充几个关键步骤。以下是完整的实现方案:
1. 完善XML配置细节
首先先补全你的XML配置,添加路径变量的验证规则和Swagger文档配置,确保API文档能正确识别这个参数:
<collectionOperations> <collectionOperation name="get"> <attribute name="method">GET</attribute> <attribute name="path">/{shop}/books</attribute> <attribute name="normalization_context"> <attribute name="groups"> <attribute>books_list</attribute> <attribute>list</attribute> </attribute> </attribute> <!-- 限制{shop}的格式,避免非法字符 --> <attribute name="requirements"> <attribute name="shop">[\w-]+</attribute> </attribute> <!-- 配置Swagger文档显示路径参数 --> <attribute name="swagger_context"> <attribute name="parameters"> <attribute> <attribute name="name">shop</attribute> <attribute name="in">path</attribute> <attribute name="required">true</attribute> <attribute name="type">string</attribute> <attribute name="description">店铺唯一标识符(如slug或ID)</attribute> </attribute> </attribute> </attribute> </collectionOperation> </collectionOperations>
2. 处理路径变量的数据过滤
API Platform默认的集合数据提供者不会自动识别并使用{shop}变量过滤数据,你可以选择以下两种常用方案:
方案A:自定义数据提供者
创建一个自定义数据提供者,获取路径中的shop参数并过滤书籍数据:
// src/BooksBundle/DataProvider/BookCollectionDataProvider.php namespace App\BooksBundle\DataProvider; use ApiPlatform\Core\DataProvider\CollectionDataProviderInterface; use ApiPlatform\Core\DataProvider\RestrictedDataProviderInterface; use App\BooksBundle\Entity\Book; use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\HttpFoundation\RequestStack; class BookCollectionDataProvider implements CollectionDataProviderInterface, RestrictedDataProviderInterface { private $entityManager; private $requestStack; public function __construct(EntityManagerInterface $entityManager, RequestStack $requestStack) { $this->entityManager = $entityManager; $this->requestStack = $requestStack; } // 指定该数据提供者仅作用于Book实体的get集合操作 public function supports(string $resourceClass, string $operationName = null, array $context = []): bool { return Book::class === $resourceClass && 'get' === $operationName; } // 实现数据查询逻辑 public function getCollection(string $resourceClass, string $operationName = null, array $context = []): iterable { $request = $this->requestStack->getCurrentRequest(); $shopIdentifier = $request->get('shop'); // 根据shop标识符查询对应书籍 return $this->entityManager->getRepository(Book::class) ->createQueryBuilder('b') ->where('b.shop = :shop') ->setParameter('shop', $shopIdentifier) ->getQuery() ->getResult(); } }
然后在services.yaml中注册这个数据提供者:
# config/services.yaml App\BooksBundle\DataProvider\BookCollectionDataProvider: arguments: $entityManager: '@doctrine.orm.default_entity_manager' $requestStack: '@request_stack' tags: - { name: api_platform.data_provider }
方案B:使用自定义控制器
如果更倾向于用控制器直接处理逻辑,可以给集合操作指定自定义控制器:
先在XML配置中添加controller属性:
<collectionOperation name="get"> <!-- 其他已有配置 --> <attribute name="controller">App\BooksBundle\Controller\BookCollectionController::getBooksByShop</attribute> </collectionOperation>
然后创建控制器类:
// src/BooksBundle/Controller/BookCollectionController.php namespace App\BooksBundle\Controller; use App\BooksBundle\Entity\Book; use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use ApiPlatform\Core\Serializer\SerializerInterface; class BookCollectionController { private $entityManager; private $serializer; public function __construct(EntityManagerInterface $entityManager, SerializerInterface $serializer) { $this->entityManager = $entityManager; $this->serializer = $serializer; } public function getBooksByShop(Request $request): Response { $shop = $request->get('shop'); $books = $this->entityManager->getRepository(Book::class)->findBy(['shop' => $shop]); // 应用normalization_context中配置的序列化组 $json = $this->serializer->serialize($books, 'json', [ 'groups' => ['books_list', 'list'] ]); return new Response($json, Response::HTTP_OK, [ 'Content-Type' => 'application/json' ]); } }
3. 可选:验证店铺存在性
如果需要确保{shop}对应的店铺真实存在,可以使用参数转换器自动加载Shop实体,不存在时自动返回404:
在控制器方法中添加参数转换器注解:
use App\BooksBundle\Entity\Shop; use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; /** * @ParamConverter("shop", options={"mapping": {"shop": "slug"}}) */ public function getBooksByShop(Shop $shop): Response { $books = $this->entityManager->getRepository(Book::class)->findBy(['shop' => $shop]); // 后续序列化逻辑 }
完成以上配置后,启动服务器访问类似/my-shop/books的路径,就能获取对应店铺的书籍列表了。
内容的提问来源于stack exchange,提问作者Ivan Vulović
相关产品推荐
相关产品推荐

