问题
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 App\Entity\Product
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 App\Entity;
use Doctrine\ORM\Mapping as ORM;
use ApiPlatform\Core\Annotation\ApiResource;
use Symfony\Component\Validator\Constraints as Assert;
use Gedmo\Mapping\Annotation as Gedmo;
use Symfony\Component\Serializer\Annotation\Groups;
/**
* @ORM\Entity
* @ORM\Table(name="product")
* @ORM\Entity(repositoryClass="App\Repository\ProductRepository")
* @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"}
* })
* @ORM\HasLifecycleCallbacks()
*/
class Product{
}
The declaration of the ProductLoader
<?php
namespace App\Loader;
use App\Entity\Product;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
use Psr\Cache\CacheItemPoolInterface;
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 App\Repository;
use Doctrine\ORM\EntityRepository;
/**
* 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 App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use App\Loader\ProductLoader;
use App\Entity\Product;
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 App\Entity\Product"
{
"@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
回答1:
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: App\Controller\ProductController:productsGetBySlugAction
_api_resource_class: 'App\Entity\Product'
_api_collection_operation_name: 'product_slug'
来源:https://stackoverflow.com/questions/48283961/unable-to-generate-an-iri-for-the-item-of-type