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