我的API需要自定义操作,它返回给定slug的实体。因此,资源Product
的操作总数为6(5个是默认操作+ 1个自定义操作)。
api_products_get_collection GET ANY ANY /api/products.{_format}
api_products_post_collection POST ANY ANY /api/products.{_format}
api_products_get_item GET ANY ANY /api/products/{id}.{_format}
api_products_put_item PUT ANY ANY /api/products/{id}.{_format}
api_products_delete_item DELETE ANY ANY /api/products/{id}.{_format}
api_products_slug GET ANY ANY /api/products/by-slug/{slug}
我按照official documentation来定义自定义操作。但令人惊讶的是,我注意到路由api_products_get_collection
正在使用路由api_products_slug
的集合数据提供程序。
我起初认为这是路线顺序的问题。但是,情况并非如此,因为它显示在下面的命令中:
$ bin/console debug:router | grep products
app_product_products ANY ANY ANY /
api_products_get_collection GET ANY ANY /api/products.{_format}
api_products_post_collection POST ANY ANY /api/products.{_format}
api_products_get_item GET ANY ANY /api/products/{id}.{_format}
api_products_put_item PUT ANY ANY /api/products/{id}.{_format}
api_products_delete_item DELETE ANY ANY /api/products/{id}.{_format}
api_products_slug GET ANY ANY /api/products/by-slug/{slug}
此外,这是我宣布路线的方式:
api_platform:
resource: .
type: api_platform
prefix: /api
# the routes is declared after the routes of the api_platfom to make sure having to the custom operation route after api_platfom routes
actions:
resource: ../../src/Action/
type: annotation
因此,当然,这不是与路线顺序相关的问题。
问题是:我应该为每个资源设置一个且只有一个收集数据提供程序吗?
以下是我宣布自定义操作的方法。
(1)声明实施CollectionDataProviderInterface的提供者
<?php
namespace App\Provider;
use App\Entity\Product;
use ApiPlatform\Core\DataProvider\CollectionDataProviderInterface;
use ApiPlatform\Core\Exception\ResourceClassNotSupportedException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use App\Repository\ProductRepository;
use Symfony\Component\HttpFoundation\RequestStack;
use Monolog\Logger;
final class ProductCollectionDataProvider implements CollectionDataProviderInterface
{
/**
* @var ProductRepository
*/
private $repository;
/**
* @var RequestStack
*/
private $requestStack;
/**
* @var Logger
*/
private $logger;
/**
* ProductCollectionDataProviderconstructor.
*
* @param ProductRepository $repository
* @param RequestStack $requestStack
* @param Logger $logger
*/
public function __construct(ProductRepository $repository, RequestStack $requestStack, Logger $logger)
{
$this->repository = $repository;
$this->requestStack = $requestStack;
$this->logger = $logger;
}
/**
* Retrieves a collection.
*
* @param string $resourceClass
* @param string|null $operationName
*
* @throws ResourceClassNotSupportedException
*
* @return array|\Traversable
*/
public function getCollection(string $resourceClass, string $operationName = null){
$this->logger->info('getCollection() is called');
$slug = $this->requestStack->getCurrentRequest()->attributes->get('slug');
$this->logger->info($slug);
if (Product::class !== $resourceClass) {
throw new ResourceClassNotSupportedException();
}
// if($operationName !== 'product_slug') return null;
$product = $this->repository->findProductBySlug($this->requestStack->getCurrentRequest()->attributes->get('slug'));
if($product===null){
throw new NotFoundHttpException(sprintf('The Product resource \'%s\' was not found.',$slug));
}
return $product;
}
}
(2)行动的定义
<?php
namespace App\Action;
use App\Entity\Product;
use App\Provider\ProductCollectionDataProvider;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Symfony\Component\Routing\Annotation\Route;
class ProductSlug
{
private $productCollectionDataProvider;
public function __construct(ProductCollectionDataProvider $productCollectionDataProvider)
{
$this->productCollectionDataProvider = $productCollectionDataProvider;
}
/**
* @Route(
* name="api_products_slug",
* path="/api/products/by-slug/{slug}",
* defaults={"_api_resource_class"=Product::class, "_api_collection_operation_name"="product_slug"}
* )
* @Method("GET")
*/
public function __invoke()
{
return $this->productCollectionDataProvider->getCollection(Product::class);
}
}
(3)将自定义操作声明为服务
services:
# ...
App\Action\:
resource: '../src/Action'
tags: ['controller.service_arguments']
(4)声明自定义操作的路径
api_platform:
resource: .
type: api_platform
prefix: /api
# the routes is declared after the routes of the api_platfom to make sure having to the custom operation route after api_platfom routes
actions:
resource: ../../src/Action/
type: annotation
环境
$ composer info | grep api
api-platform/api-pack v1.0.1 A pack for API Platform
api-platform/core v2.1.5 The ultimate solution to create web APIs.
$ composer info | grep symfony/framework
symfony/framework-bundle v4.0.4 Symfony FrameworkBundle