每个资源都有一个且只有一个Collection Data提供程序是必须的吗?

时间:2018-02-04 05:47:31

标签: symfony symfony4 api-platform.com

我的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

0 个答案:

没有答案