Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
484 views
in Technique[技术] by (71.8m points)

symfony - Unable to generate an IRI for the item of type

I am actually building an API based on:

  • symfony/flex: v1.0.61
  • symfony: v4.0.3
  • api-platform/api-pack: v1.0.1
  • api-platform/core: v2.1.4

The CRUD operations were easy to implement. Nevertheless, the custom operation does not seem to be straightforward.

The custom operation I am trying to implement will simply return a AppEntityProduct based on a given $slug.

  • the route is: /api/products/by-slugs/{slug}
  • the method is: GET
  • the operation type is: itemOperations

This is how things are being done:

The declaration of the Product resource

<?php
// src/Entity/Product
namespace AppEntity;

use DoctrineORMMapping as ORM;
use ApiPlatformCoreAnnotationApiResource;
use SymfonyComponentValidatorConstraints as Assert;
use GedmoMappingAnnotation as Gedmo;
use SymfonyComponentSerializerAnnotationGroups;


/**
 * @ORMEntity
 * @ORMTable(name="product")
 * @ORMEntity(repositoryClass="AppRepositoryProductRepository")
 * @ApiResource(attributes={"pagination_client_items_per_page"=true,
 *                          "filters"={"product.search"}
 *                         },
 *              collectionOperations={
 *                      "get"={
 *                      "method"="GET",
 *                      "normalization_context"={"groups"={"product_gets"}} },
 *                      "post"={
 *                      "method"="POST",
 *                      "denormalization_context"={"groups"={"product_post"}} }
 *              },
 *              itemOperations={
 *                      "get"={
 *                      "method"="GET",
 *                      "normalization_context"={"groups"={"product_get"}} },
 *                      "put"={
 *                      "method"="PUT",
 *                      "denormalization_context"={"groups"={"product_put"}} },
 *                      "delete"={
 *                      "method"="DELETE"},
 *                      "product_slug"={"route_name"="route_product_slug"}
 *              })
 * @ORMHasLifecycleCallbacks()
 */

class Product{
}

The declaration of the ProductLoader

<?php

namespace AppLoader;

use AppEntityProduct;
use DoctrineORMEntityManagerInterface;
use PsrLogLoggerInterface;
use PsrCacheCacheItemPoolInterface;

class ProductLoader {
    private $em;
    private $logger;
    private $cache;

    public function __construct(
        EntityManagerInterface $em,
        LoggerInterface $logger,
        CacheItemPoolInterface $cache){
        $this->em = $em;
        $this->logger = $logger;
        $this->cache = $cache;
    }

    public function findBySlug($slug){
        return $this->em->getRepository(Product::class)->findProductBySlug($slug);
    }
}

The declaration of the ProductRepository

<?php

namespace AppRepository;

use DoctrineORMEntityRepository;

/**
 * ProductRepository
 */
class ProductRepository extends EntityRepository {

    public function findProductBySlug($slug) {
        $qb = $this->createQueryBuilder("b")
                ->where("b.slug = :slug")
                ->setParameter('slug', $slug);
        return $qb->getQuery()->getOneOrNullResult();
    }

}

The declaration of the custom operation in a Symfony controller

<?php

namespace AppController;

use SymfonyBundleFrameworkBundleControllerController;
use SensioBundleFrameworkExtraBundleConfigurationRoute;
use SensioBundleFrameworkExtraBundleConfigurationMethod;

use AppLoaderProductLoader;
use AppEntityProduct;

class ProductController extends Controller
{

    /**
     * @Route(
     *     name="route_product_slug",
     *     path="/api/products/by-slug/{slug}",
     *     defaults={"_api_resource_class"=Product::class,
     *               "_api_item_operation_name"="product_slug"
     *     }
     * )
     * @Method("GET")
     */
     public function productsGetBySlugAction(ProductLoader $productLoader, $slug){
        return $productLoader->findBySlug($slug);
     }
}

A quick run of the command below returned:

bin/console debug:router
 --------------------------------------- -------- -------- ------ --------------------------------------- 
  Name                                    Method   Scheme   Host   Path                                   
 --------------------------------------- -------- -------- ------ --------------------------------------- 
  app_product_products                    ANY      ANY      ANY    /                                      
  route_product_slug                      GET      ANY      ANY    /api/products/by-slug/{slug}           
  api_entrypoint                          ANY      ANY      ANY    /api/{index}.{_format}                 
  api_doc                                 ANY      ANY      ANY    /api/docs.{_format}                    
  api_jsonld_context                      ANY      ANY      ANY    /api/contexts/{shortName}.{_format}    
  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_regions_get_collection              GET      ANY      ANY    /api/regions.{_format}                 
  api_regions_post_collection             POST     ANY      ANY    /api/regions.{_format}                 
  api_regions_get_item                    GET      ANY      ANY    /api/regions/{id}.{_format}            
  api_regions_put_item                    PUT      ANY      ANY    /api/regions/{id}.{_format}            
  api_regions_delete_item                 DELETE   ANY      ANY    /api/regions/{id}.{_format}            
  api_countries_get_collection            GET      ANY      ANY    /api/countries.{_format}               
  api_countries_post_collection           POST     ANY      ANY    /api/countries.{_format}               
  api_countries_get_item                  GET      ANY      ANY    /api/countries/{id}.{_format}          
  api_countries_put_item                  PUT      ANY      ANY    /api/countries/{id}.{_format}          
  api_countries_delete_item               DELETE   ANY      ANY    /api/countries/{id}.{_format}          
  api_countries_regions_get_subresource   GET      ANY      ANY    /api/countries/{id}/regions.{_format}  
  _twig_error_test                        ANY      ANY      ANY    /_error/{code}.{_format}               
  _wdt                                    ANY      ANY      ANY    /_wdt/{token}                          
  _profiler_home                          ANY      ANY      ANY    /_profiler/                            
  _profiler_search                        ANY      ANY      ANY    /_profiler/search                      
  _profiler_search_bar                    ANY      ANY      ANY    /_profiler/search_bar                  
  _profiler_phpinfo                       ANY      ANY      ANY    /_profiler/phpinfo                     
  _profiler_search_results                ANY      ANY      ANY    /_profiler/{token}/search/results      
  _profiler_open_file                     ANY      ANY      ANY    /_profiler/open                        
  _profiler                               ANY      ANY      ANY    /_profiler/{token}                     
  _profiler_router                        ANY      ANY      ANY    /_profiler/{token}/router              
  _profiler_exception                     ANY      ANY      ANY    /_profiler/{token}/exception           
  _profiler_exception_css                 ANY      ANY      ANY    /_profiler/{token}/exception.css       
 --------------------------------------- -------- -------- ------ --------------------------------------- 

The route route_product_slug is well existing but I am always getting the following error:

Unable to generate an IRI for the item of type AppEntityProduct"

{
"@context": "/sf-flex-40/public/index.php/api/contexts/Error",
"@type": "hydra:Error",
"hydra:title": "An error occurred",
"hydra:description": "Unable to generate an IRI for the item of type "App\Entity\Product"",
"trace": [
    {
        "namespace": "",
        "short_class": "",
        "class": "",
        "type": "",
        "function": "",
        "file": "/home/amine/docker-projects/sf-flex-40/vendor/api-platform/core/src/Bridge/Symfony/Routing/IriConverter.php",
        "line": 107,
        "args": []
    },
    {
        "namespace": "ApiPlatform\Core\Bridge\Symfony\Routing",
        "short_class": "IriConverter",
        "class": "ApiPlatform\Core\Bridge\Symfony\Routing\IriConverter",
        "type": "->",
        "function": "getIriFromItem",
        "file": "/home/amine/docker-projects/sf-flex-40/vendor/api-platform/core/src/JsonLd/Serializer/ItemNormalizer.php",
        "line": 71,
        "args": [
            [
                "object",
                "App\Entity\Product"
            ]
        ]
    },

This error seems to be recurrent. Nevertheless, I am asking again since the problem seems to be related to bad order of routes in my flex-enable symfony 4 application (please refer to https://github.com/api-platform/core/issues/830).

So, how can set the proper order of the routes if I am using the annotations in symfony 4. The routes are defined in these files:

The first file: annotations.yaml

# config/routes/annotations.yaml
controllers:
    resource: ../../src/Controller/
    type: annotation

The second file: api_platform.yaml

# config/routes/api_platform.yaml
api_platform:
    resource: .
    type: api_platform
    prefix: /api

Does this mean I have to configure all my work with YAML file in stead of annotations so that I can specify the order of the route?

Thanks

Here what

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The declaration of the route of the custom operation can only be through yaml file. In fact, the use of annotation will not us to get the correct order of routes. The route of the custom operation must come after the declaration of the route api_platform.

api_platform:
    resource: .
    type: api_platform
    prefix: /api

api_product_slug:
    path: '/api/products/by-slug/{slug}'
    methods:  ['GET']
    defaults:
        _controller: AppControllerProductController:productsGetBySlugAction
        _api_resource_class: 'AppEntityProduct'
        _api_collection_operation_name: 'product_slug'

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...