Chapters
-
Course Code
Subscribe to download the code!
Subscribe to download the code!
-
This Video
Subscribe to download the video!
Subscribe to download the video!
-
Subtitles
Subscribe to download the subtitles!
Subscribe to download the subtitles!
-
Course Script
Subscribe to download the script!
Subscribe to download the script!
Scroll down to the script below, click on any sentence (including terminal blocks) to jump to that spot in the video!
API Platform funciona tomando una clase como DragonTreasure
y diciendo que quieres exponerla como recurso en tu API. Lo hacemos añadiendo el atributo ApiResource
. En este momento, lo estamos colocando sobre una entidad Doctrine, aunque, en un futuro tutorial, aprenderemos que, en realidad, puedes colocar ApiResource
sobre cualquier clase.
Hola Operaciones
Por defecto, cada ApiResource
incluye 6 rutas, que la API Platform denomina operaciones. Puedes verlas en el perfilador. Este es el perfilador deGET /api/dragon_treasures.json
. Haz clic en la sección "API Platform". En la parte superior, vemos los metadatos de este recurso API. Debajo, vemos las operaciones. Esto... es más información de la que necesitamos ahora, pero hay Get
, GetCollection
, Post
,Put
, Patch
y finalmente Delete
. Estas son las mismas cosas que vemos en la documentación de Swagger.
Echémosles un vistazo rápido. En primer lugar, ¿qué operaciones devuelven datos? En realidad, todas, excepto Delete
. Tanto Get
como las rutas Post
, Put
y Patch
devuelven un único recurso, es decir, un único tesoro. YGET /api/dragon_treasures
devuelve una colección.
¿A qué rutas enviamos datos cuando las utilizamos? A POST
para crear, y a PUT
y PATCH
para actualizar. No enviamos ningún dato para DELETE
ni para ninguna de las dos operaciones deGET
.
PUT vs PATCH
La mayoría de las rutas se explican por sí mismas: obtener una colección de tesoros, un solo tesoro, crear un tesoro y eliminar un tesoro. Los únicos confusos son poner frente a parchear. PUT
dice "sustituye" y PATCH
dice "actualiza". Eso... ¡parecen dos formas de decir lo mismo!
El tema de PUT frente a PATCH en las API puede ponerse picante. Pero en la API Platform, al menos hoy, PUT y PATCH funcionan igual: ambos se utilizan para actualizar un recurso. Y los veremos en acción a lo largo del curso.
Personalizar las operaciones
Una de las cosas que podrías querer hacer es personalizar o eliminar algunas de estas operaciones... o incluso añadir más operaciones. ¿Cómo podríamos hacerlo? Como vimos en el perfilador, cada operación está respaldada por una clase.
De vuelta sobre la clase DragonTreasure
, después de description
, añade una clave operations
. Fíjate en que estoy obteniendo autocompletado para las opciones porque son argumentos con nombre para el constructor de la clase ApiResource
. Te lo mostraré dentro de un momento.
Establece esto como una matriz y luego repite todas las operaciones que tenemos actualmente. Así que,new Get()
, pulsa tabulador para autocompletar eso, GetCollection
, Post
, Put
, Patch
y Delete
.
Ahora, si vamos a la documentación de Swagger y la actualizamos... ¡no cambia absolutamente nada! Eso es lo que queríamos. Acabamos de repetir exactamente la configuración por defecto. Pero ahora somos libres de personalizar las cosas. Por ejemplo, supongamos que no queremos que se borren los tesoros... porque un dragón nunca permitiría que le robaran su tesoro. Elimina Delete
... e incluso eliminaré la declaración use
.
Ahora, cuando actualicemos, la operación DELETE
habrá desaparecido.
Opciones de ApiResource
Vale, así que cada atributo que utilizamos es en realidad una clase. Y saber eso es poderoso. Mantén pulsado comando o control y haz clic en ApiResource
para abrirla. Esto es realmente genial. Cada argumento del constructor es una opción que podemos pasar al atributo. Y casi todos ellos tienen un enlace a la documentación donde puedes leer más. Hablaremos de los elementos más importantes, pero es un gran recurso que hay que conocer.
Cambiar el shortName
Un argumento se llama shortName
. Si miras en Swagger, nuestro "modelo" se conoce actualmente como DragonTreasure
, que obviamente coincide con la clase. A esto se le llama "nombre corto". Y por defecto, las URL -/api/dragon_treasures
- se generan a partir de él.
Supongamos que queremos acortar todo esto a "tesoro". No hay problema: establece shortName
en Treasure
.
En cuanto lo hagamos, observa el nombre y las URL. Muy bien. Este recurso se conoce ahora como "Tesoro" y las URL se han actualizado para reflejarlo.
Opciones de funcionamiento
Aunque esa no es la única forma de configurar las URL. Al igual que con ApiResource
, cada operación es también una clase. Mantén pulsada la tecla Comando (o Ctrl) y haz clic para abrir la clase Get
. Una vez más, estos argumentos del constructor son opciones... y la mayoría tienen documentación.
Un argumento importante es uriTemplate
. Sí, podemos controlar el aspecto de la URL operación por operación.
Compruébalo. Recuerda que Get
es la forma de obtener un único recurso. AñadeuriTemplate
ajustado a /dragon-plunder/{id}
donde esa última parte será el marcador de posición para el id dinámico. Para GetCollection
, pasemos también uriTemplate
ajustado a /dragon-plunder
.
De acuerdo ¡Vamos a consultar la documentación! ¡Estupendo! Las demás operaciones mantienen la URL antigua, pero éstas utilizan el nuevo estilo. Más adelante, cuando hablemos de los subrecursos, profundizaremos en uriTemplate
y su opción hermana uriVariables
.
Vale... como es un poco tonto tener dos operaciones con URL raras, vamos a eliminar esa personalización.
Ahora que sabemos un montón sobre ApiResource
y estas operaciones, es hora de hablar del corazón de API Platform: El serializador de Symfony. Eso a continuación.
14 Comments
Hey Benoit,
Any chance you misprinted the URL in your browser? I suppsoe it should be: https://127.0.0.1:8000/api/projects.json - remove double "ss" in "projectss". I wonder if that helps
Cheers!
Hi Ryan,
Is it possible to completely remove the PUT item operation from my API in the api_platform.yaml
file?
Thank you,
Sean
I was able to find this configuration using bin/console config:dump api_platform
To globally override the PUT operation in config/packages/api_platform.yaml
I was able to include all operations except PUT under the api_platform default operations settings. For example:
api_platform:
defaults:
operations:
- ApiPlatform\Metadata\GetCollection
- ApiPlatform\Metadata\Get
- ApiPlatform\Metadata\Post
- ApiPlatform\Metadata\Patch
- ApiPlatform\Metadata\Delete
Hey Sean,
Good catch, I'm glad to hear you were able to find the solution yourself, well done! Yeah, dome debug commands might be really helpful :)
Cheers!
Hi Ryan,
I am unable to execute POST operations: error 500 "The class 'ApiPlatform\Metadata\Post' was not found in the chain configured namespaces App\Entity".
I cannot figure out why others operations class are working well but not this one...
Have an idea ?
Best regards
Chris
Hey @Chris-M!
Hmm. What does the stacktrace look like on that error? Is it coming from Doctrine? I think I recognize that error from Doctrine... and so it almost looks like Doctrine thinks that there is an entity called ApiPlatform\Metadata\Post
. So, I'm not sure, but this looks bizarre enough that my gut is that there is some small typo somewhere causing a big issue. Are you other operations working - e.g. Get
, Patch
, etc?
Cheers!
And for the other operations, I can't have anything done as it looks like the issue is blocking as soon as the class is constructed... So, I got the same response with a Get, Patch or others... :(
Here the complete reponse from the API
{
"@context": "/api/contexts/Error",
"@type": "hydra:Error",
"hydra:title": "An error occurred",
"hydra:description": "The class 'ApiPlatform\Metadata\Post' was not found in the chain configured namespaces App\Entity",
"trace": [
{
"namespace": "",
"short_class": "",
"class": "",
"type": "",
"function": "",
"file": "C:\\CODES\\FORMATION\\TutoModuleComment\\vendor\\doctrine\\persistence\\src\\Persistence\\Mapping\\MappingException.php",
"line": 26,
"args": []
},
{
"namespace": "Doctrine\\Persistence\\Mapping",
"short_class": "MappingException",
"class": "Doctrine\\Persistence\\Mapping\\MappingException",
"type": "::",
"function": "classNotFoundInNamespaces",
"file": "C:\\CODES\\FORMATION\\TutoModuleComment\\vendor\\doctrine\\persistence\\src\\Persistence\\Mapping\\Driver\\MappingDriverChain.php",
"line": 89,
"args": [
[
"string",
"ApiPlatform\\Metadata\\Post"
],
[
"array",
[
[
"string",
"App\\Entity"
]
]
]
]
},
{
"namespace": "Doctrine\\Persistence\\Mapping\\Driver",
"short_class": "MappingDriverChain",
"class": "Doctrine\\Persistence\\Mapping\\Driver\\MappingDriverChain",
"type": "->",
"function": "loadMetadataForClass",
"file": "C:\\CODES\\FORMATION\\TutoModuleComment\\vendor\\doctrine\\doctrine-bundle\\Mapping\\MappingDriver.php",
"line": 42,
"args": [
[
"string",
"ApiPlatform\\Metadata\\Post"
],
[
"object",
"Doctrine\\ORM\\Mapping\\ClassMetadata"
]
]
},
{
"namespace": "Doctrine\\Bundle\\DoctrineBundle\\Mapping",
"short_class": "MappingDriver",
"class": "Doctrine\\Bundle\\DoctrineBundle\\Mapping\\MappingDriver",
"type": "->",
"function": "loadMetadataForClass",
"file": "C:\\CODES\\FORMATION\\TutoModuleComment\\vendor\\doctrine\\orm\\lib\\Doctrine\\ORM\\Mapping\\ClassMetadataFactory.php",
"line": 136,
"args": [
[
"string",
"ApiPlatform\\Metadata\\Post"
],
[
"object",
"Doctrine\\ORM\\Mapping\\ClassMetadata"
]
]
},
{
"namespace": "Doctrine\\ORM\\Mapping",
"short_class": "ClassMetadataFactory",
"class": "Doctrine\\ORM\\Mapping\\ClassMetadataFactory",
"type": "->",
"function": "doLoadMetadata",
"file": "C:\\CODES\\FORMATION\\TutoModuleComment\\vendor\\doctrine\\doctrine-bundle\\Mapping\\ClassMetadataFactory.php",
"line": 18,
"args": [
[
"object",
"Doctrine\\ORM\\Mapping\\ClassMetadata"
],
[
"null",
null
],
[
"boolean",
false
],
[
"array",
[]
]
]
},
{
"namespace": "Doctrine\\Bundle\\DoctrineBundle\\Mapping",
"short_class": "ClassMetadataFactory",
"class": "Doctrine\\Bundle\\DoctrineBundle\\Mapping\\ClassMetadataFactory",
"type": "->",
"function": "doLoadMetadata",
"file": "C:\\CODES\\FORMATION\\TutoModuleComment\\vendor\\doctrine\\persistence\\src\\Persistence\\Mapping\\AbstractClassMetadataFactory.php",
"line": 343,
"args": [
[
"object",
"Doctrine\\ORM\\Mapping\\ClassMetadata"
],
[
"null",
null
],
[
"boolean",
false
],
[
"array",
[]
]
]
},
{
"namespace": "Doctrine\\Persistence\\Mapping",
"short_class": "AbstractClassMetadataFactory",
"class": "Doctrine\\Persistence\\Mapping\\AbstractClassMetadataFactory",
"type": "->",
"function": "loadMetadata",
"file": "C:\\CODES\\FORMATION\\TutoModuleComment\\vendor\\doctrine\\persistence\\src\\Persistence\\Mapping\\AbstractClassMetadataFactory.php",
"line": 207,
"args": [
[
"string",
"ApiPlatform\\Metadata\\Post"
]
]
},
{
"namespace": "Doctrine\\Persistence\\Mapping",
"short_class": "AbstractClassMetadataFactory",
"class": "Doctrine\\Persistence\\Mapping\\AbstractClassMetadataFactory",
"type": "->",
"function": "getMetadataFor",
"file": "C:\\CODES\\FORMATION\\TutoModuleComment\\vendor\\doctrine\\orm\\lib\\Doctrine\\ORM\\EntityManager.php",
"line": 318,
"args": [
[
"string",
"ApiPlatform\\Metadata\\Post"
]
]
},
{
"namespace": "Doctrine\\ORM",
"short_class": "EntityManager",
"class": "Doctrine\\ORM\\EntityManager",
"type": "->",
"function": "getClassMetadata",
"file": "C:\\CODES\\FORMATION\\TutoModuleComment\\vendor\\doctrine\\orm\\lib\\Doctrine\\ORM\\Query\\SqlWalker.php",
"line": 809,
"args": [
[
"string",
"ApiPlatform\\Metadata\\Post"
]
]
},
{
"namespace": "Doctrine\\ORM\\Query",
"short_class": "SqlWalker",
"class": "Doctrine\\ORM\\Query\\SqlWalker",
"type": "->",
"function": "walkSelectClause",
"file": "C:\\CODES\\FORMATION\\TutoModuleComment\\vendor\\doctrine\\orm\\lib\\Doctrine\\ORM\\Query\\SqlWalker.php",
"line": 552,
"args": [
[
"object",
"Doctrine\\ORM\\Query\\AST\\SelectClause"
]
]
},
{
"namespace": "Doctrine\\ORM\\Query",
"short_class": "SqlWalker",
"class": "Doctrine\\ORM\\Query\\SqlWalker",
"type": "->",
"function": "walkSelectStatement",
"file": "C:\\CODES\\FORMATION\\TutoModuleComment\\vendor\\doctrine\\orm\\lib\\Doctrine\\ORM\\Query\\Exec\\SingleSelectExecutor.php",
"line": 21,
"args": [
[
"object",
"Doctrine\\ORM\\Query\\AST\\SelectStatement"
]
]
},
{
"namespace": "Doctrine\\ORM\\Query\\Exec",
"short_class": "SingleSelectExecutor",
"class": "Doctrine\\ORM\\Query\\Exec\\SingleSelectExecutor",
"type": "->",
"function": "__construct",
"file": "C:\\CODES\\FORMATION\\TutoModuleComment\\vendor\\doctrine\\orm\\lib\\Doctrine\\ORM\\Query\\SqlWalker.php",
"line": 302,
"args": [
[
"object",
"Doctrine\\ORM\\Query\\AST\\SelectStatement"
],
[
"object",
"Doctrine\\ORM\\Query\\SqlWalker"
]
]
},
{
"namespace": "Doctrine\\ORM\\Query",
"short_class": "SqlWalker",
"class": "Doctrine\\ORM\\Query\\SqlWalker",
"type": "->",
"function": "getExecutor",
"file": "C:\\CODES\\FORMATION\\TutoModuleComment\\vendor\\doctrine\\orm\\lib\\Doctrine\\ORM\\Query\\Parser.php",
"line": 398,
"args": [
[
"object",
"Doctrine\\ORM\\Query\\AST\\SelectStatement"
]
]
},
{
"namespace": "Doctrine\\ORM\\Query",
"short_class": "Parser",
"class": "Doctrine\\ORM\\Query\\Parser",
"type": "->",
"function": "parse",
"file": "C:\\CODES\\FORMATION\\TutoModuleComment\\vendor\\doctrine\\orm\\lib\\Doctrine\\ORM\\Query.php",
"line": 274,
"args": []
},
{
"namespace": "Doctrine\\ORM",
"short_class": "Query",
"class": "Doctrine\\ORM\\Query",
"type": "->",
"function": "parse",
"file": "C:\\CODES\\FORMATION\\TutoModuleComment\\vendor\\doctrine\\orm\\lib\\Doctrine\\ORM\\Query.php",
"line": 286,
"args": []
},
{
"namespace": "Doctrine\\ORM",
"short_class": "Query",
"class": "Doctrine\\ORM\\Query",
"type": "->",
"function": "_doExecute",
"file": "C:\\CODES\\FORMATION\\TutoModuleComment\\vendor\\doctrine\\orm\\lib\\Doctrine\\ORM\\AbstractQuery.php",
"line": 1212,
"args": []
},
{
"namespace": "Doctrine\\ORM",
"short_class": "AbstractQuery",
"class": "Doctrine\\ORM\\AbstractQuery",
"type": "->",
"function": "executeIgnoreQueryCache",
"file": "C:\\CODES\\FORMATION\\TutoModuleComment\\vendor\\doctrine\\orm\\lib\\Doctrine\\ORM\\AbstractQuery.php",
"line": 1166,
"args": [
[
"null",
null
],
[
"integer",
1
]
]
},
{
"namespace": "Doctrine\\ORM",
"short_class": "AbstractQuery",
"class": "Doctrine\\ORM\\AbstractQuery",
"type": "->",
"function": "execute",
"file": "C:\\CODES\\FORMATION\\TutoModuleComment\\vendor\\doctrine\\orm\\lib\\Doctrine\\ORM\\AbstractQuery.php",
"line": 901,
"args": [
[
"null",
null
],
[
"integer",
1
]
]
},
{
"namespace": "Doctrine\\ORM",
"short_class": "AbstractQuery",
"class": "Doctrine\\ORM\\AbstractQuery",
"type": "->",
"function": "getResult",
"file": "C:\\CODES\\FORMATION\\TutoModuleComment\\vendor\\doctrine\\orm\\lib\\Doctrine\\ORM\\Tools\\Pagination\\Paginator.php",
"line": 175,
"args": [
[
"integer",
1
]
]
},
{
"namespace": "Doctrine\\ORM\\Tools\\Pagination",
"short_class": "Paginator",
"class": "Doctrine\\ORM\\Tools\\Pagination\\Paginator",
"type": "->",
"function": "getIterator",
"file": "C:\\CODES\\FORMATION\\TutoModuleComment\\vendor\\api-platform\\core\\src\\Doctrine\\Orm\\AbstractPaginator.php",
"line": 69,
"args": []
},
{
"namespace": "ApiPlatform\\Doctrine\\Orm",
"short_class": "AbstractPaginator",
"class": "ApiPlatform\\Doctrine\\Orm\\AbstractPaginator",
"type": "->",
"function": "getIterator",
"file": "C:\\CODES\\FORMATION\\TutoModuleComment\\vendor\\api-platform\\core\\src\\Hydra\\Serializer\\CollectionNormalizer.php",
"line": 103,
"args": []
},
{
"namespace": "ApiPlatform\\Hydra\\Serializer",
"short_class": "CollectionNormalizer",
"class": "ApiPlatform\\Hydra\\Serializer\\CollectionNormalizer",
"type": "->",
"function": "getItemsData",
"file": "C:\\CODES\\FORMATION\\TutoModuleComment\\vendor\\api-platform\\core\\src\\Serializer\\AbstractCollectionNormalizer.php",
"line": 119,
"args": [
[
"object",
"ApiPlatform\\Doctrine\\Orm\\Paginator"
],
[
"string",
"jsonld"
],
[
"array",
{
"groups": [
"array",
[
[
"string",
"comment:read"
]
]
],
"resource_class": [
"string",
"App\\Entity\\Comment"
],
"skip_null_values": [
"boolean",
true
],
"iri_only": [
"boolean",
false
],
"request_uri": [
"string",
"/api/comments?page=1"
],
"uri": [
"string",
"http://localhost:8000/api/comments?page=1"
],
"input": [
"null",
null
],
"output": [
"null",
null
],
"exclude_from_cache_key": [
"array",
[
[
"string",
"root_operation"
],
[
"string",
"operation"
],
[
"string",
"resources"
],
[
"string",
"resources_to_push"
]
]
],
"resources": [
"object",
"ApiPlatform\\Serializer\\ResourceList"
],
"resources_to_push": [
"object",
"ApiPlatform\\Serializer\\ResourceList"
],
"debug_trace_id": [
"string",
"64e781d66f771"
],
"api_sub_level": [
"boolean",
true
],
"api_collection_sub_level": [
"boolean",
true
],
"item_uri_template": [
"null",
null
],
"root_operation": [
"object",
"ApiPlatform\\Metadata\\GetCollection"
],
"root_operation_name": [
"string",
"_api_/comments{._format}_get_collection"
]
}
]
]
},
{
"namespace": "ApiPlatform\\Serializer",
"short_class": "AbstractCollectionNormalizer",
"class": "ApiPlatform\\Serializer\\AbstractCollectionNormalizer",
"type": "->",
"function": "normalize",
"file": "C:\\CODES\\FORMATION\\TutoModuleComment\\vendor\\api-platform\\core\\src\\Hydra\\Serializer\\PartialCollectionViewNormalizer.php",
"line": 50,
"args": [
[
"object",
"ApiPlatform\\Doctrine\\Orm\\Paginator"
],
[
"string",
"jsonld"
],
[
"array",
{
"groups": [
"array",
[
[
"string",
"comment:read"
]
]
],
"resource_class": [
"string",
"App\\Entity\\Comment"
],
"skip_null_values": [
"boolean",
true
],
"iri_only": [
"boolean",
false
],
"request_uri": [
"string",
"/api/comments?page=1"
],
"uri": [
"string",
"http://localhost:8000/api/comments?page=1"
],
"input": [
"null",
null
],
"output": [
"null",
null
],
"exclude_from_cache_key": [
"array",
[
[
"string",
"root_operation"
],
[
"string",
"operation"
],
[
"string",
"resources"
],
[
"string",
"resources_to_push"
]
]
],
"resources": [
"object",
"ApiPlatform\\Serializer\\ResourceList"
],
"resources_to_push": [
"object",
"ApiPlatform\\Serializer\\ResourceList"
],
"debug_trace_id": [
"string",
"64e781d66f771"
],
"api_sub_level": [
"boolean",
true
],
"api_collection_sub_level": [
"boolean",
true
],
"item_uri_template": [
"null",
null
],
"root_operation": [
"object",
"ApiPlatform\\Metadata\\GetCollection"
],
"root_operation_name": [
"string",
"_api_/comments{._format}_get_collection"
]
}
]
]
},
{
"namespace": "ApiPlatform\\Hydra\\Serializer",
"short_class": "PartialCollectionViewNormalizer",
"class": "ApiPlatform\\Hydra\\Serializer\\PartialCollectionViewNormalizer",
"type": "->",
"function": "normalize",
"file": "C:\\CODES\\FORMATION\\TutoModuleComment\\vendor\\api-platform\\core\\src\\Hydra\\Serializer\\CollectionFiltersNormalizer.php",
"line": 88,
"args": [
[
"object",
"ApiPlatform\\Doctrine\\Orm\\Paginator"
],
[
"string",
"jsonld"
],
[
"array",
{
"groups": [
"array",
[
[
"string",
"comment:read"
]
]
],
"operation_name": [
"string",
"_api_/comments{._format}_get_collection"
],
"operation": [
"object",
"ApiPlatform\\Metadata\\GetCollection"
],
"resource_class": [
"string",
"App\\Entity\\Comment"
],
"skip_null_values": [
"boolean",
true
],
"iri_only": [
"boolean",
false
],
"request_uri": [
"string",
"/api/comments?page=1"
],
"uri": [
"string",
"http://localhost:8000/api/comments?page=1"
],
"input": [
"null",
null
],
"output": [
"null",
null
],
"exclude_from_cache_key": [
"array",
[
[
"string",
"root_operation"
],
[
"string",
"operation"
],
[
"string",
"resources"
],
[
"string",
"resources_to_push"
]
]
],
"resources": [
"object",
"ApiPlatform\\Serializer\\ResourceList"
],
"resources_to_push": [
"object",
"ApiPlatform\\Serializer\\ResourceList"
],
"debug_trace_id": [
"string",
"64e781d66f771"
]
}
]
]
},
{
"namespace": "ApiPlatform\\Hydra\\Serializer",
"short_class": "CollectionFiltersNormalizer",
"class": "ApiPlatform\\Hydra\\Serializer\\CollectionFiltersNormalizer",
"type": "->",
"function": "normalize",
"file": "C:\\CODES\\FORMATION\\TutoModuleComment\\vendor\\symfony\\serializer\\Debug\\TraceableNormalizer.php",
"line": 58,
"args": [
[
"object",
"ApiPlatform\\Doctrine\\Orm\\Paginator"
],
[
"string",
"jsonld"
],
[
"array",
{
"groups": [
"array",
[
[
"string",
"comment:read"
]
]
],
"operation_name": [
"string",
"_api_/comments{._format}_get_collection"
],
"operation": [
"object",
"ApiPlatform\\Metadata\\GetCollection"
],
"resource_class": [
"string",
"App\\Entity\\Comment"
],
"skip_null_values": [
"boolean",
true
],
"iri_only": [
"boolean",
false
],
"request_uri": [
"string",
"/api/comments?page=1"
],
"uri": [
"string",
"http://localhost:8000/api/comments?page=1"
],
"input": [
"null",
null
],
"output": [
"null",
null
],
"exclude_from_cache_key": [
"array",
[
[
"string",
"root_operation"
],
[
"string",
"operation"
],
[
"string",
"resources"
],
[
"string",
"resources_to_push"
]
]
],
"resources": [
"object",
"ApiPlatform\\Serializer\\ResourceList"
],
"resources_to_push": [
"object",
"ApiPlatform\\Serializer\\ResourceList"
],
"debug_trace_id": [
"string",
"64e781d66f771"
]
}
]
]
},
{
"namespace": "Symfony\\Component\\Serializer\\Debug",
"short_class": "TraceableNormalizer",
"class": "Symfony\\Component\\Serializer\\Debug\\TraceableNormalizer",
"type": "->",
"function": "normalize",
"file": "C:\\CODES\\FORMATION\\TutoModuleComment\\vendor\\symfony\\serializer\\Serializer.php",
"line": 159,
"args": [
[
"object",
"ApiPlatform\\Doctrine\\Orm\\Paginator"
],
[
"string",
"jsonld"
],
[
"array",
{
"groups": [
"array",
[
[
"string",
"comment:read"
]
]
],
"operation_name": [
"string",
"_api_/comments{._format}_get_collection"
],
"operation": [
"object",
"ApiPlatform\\Metadata\\GetCollection"
],
"resource_class": [
"string",
"App\\Entity\\Comment"
],
"skip_null_values": [
"boolean",
true
],
"iri_only": [
"boolean",
false
],
"request_uri": [
"string",
"/api/comments?page=1"
],
"uri": [
"string",
"http://localhost:8000/api/comments?page=1"
],
"input": [
"null",
null
],
"output": [
"null",
null
],
"exclude_from_cache_key": [
"array",
[
[
"string",
"root_operation"
],
[
"string",
"operation"
],
[
"string",
"resources"
],
[
"string",
"resources_to_push"
]
]
],
"resources": [
"object",
"ApiPlatform\\Serializer\\ResourceList"
],
"resources_to_push": [
"object",
"ApiPlatform\\Serializer\\ResourceList"
],
"debug_trace_id": [
"string",
"64e781d66f771"
]
}
]
]
},
{
"namespace": "Symfony\\Component\\Serializer",
"short_class": "Serializer",
"class": "Symfony\\Component\\Serializer\\Serializer",
"type": "->",
"function": "normalize",
"file": "C:\\CODES\\FORMATION\\TutoModuleComment\\vendor\\symfony\\serializer\\Serializer.php",
"line": 138,
"args": [
[
"object",
"ApiPlatform\\Doctrine\\Orm\\Paginator"
],
[
"string",
"jsonld"
],
[
"array",
{
"groups": [
"array",
[
[
"string",
"comment:read"
]
]
],
"operation_name": [
"string",
"_api_/comments{._format}_get_collection"
],
"operation": [
"object",
"ApiPlatform\\Metadata\\GetCollection"
],
"resource_class": [
"string",
"App\\Entity\\Comment"
],
"skip_null_values": [
"boolean",
true
],
"iri_only": [
"boolean",
false
],
"request_uri": [
"string",
"/api/comments?page=1"
],
"uri": [
"string",
"http://localhost:8000/api/comments?page=1"
],
"input": [
"null",
null
],
"output": [
"null",
null
],
"exclude_from_cache_key": [
"array",
[
[
"string",
"root_operation"
],
[
"string",
"operation"
],
[
"string",
"resources"
],
[
"string",
"resources_to_push"
]
]
],
"resources": [
"object",
"ApiPlatform\\Serializer\\ResourceList"
],
"resources_to_push": [
"object",
"ApiPlatform\\Serializer\\ResourceList"
],
"debug_trace_id": [
"string",
"64e781d66f771"
]
}
]
]
},
{
"namespace": "Symfony\\Component\\Serializer",
"short_class": "Serializer",
"class": "Symfony\\Component\\Serializer\\Serializer",
"type": "->",
"function": "serialize",
"file": "C:\\CODES\\FORMATION\\TutoModuleComment\\vendor\\symfony\\serializer\\Debug\\TraceableSerializer.php",
"line": 47,
"args": [
[
"object",
"ApiPlatform\\Doctrine\\Orm\\Paginator"
],
[
"string",
"jsonld"
],
[
"array",
{
"groups": [
"array",
[
[
"string",
"comment:read"
]
]
],
"operation_name": [
"string",
"_api_/comments{._format}_get_collection"
],
"operation": [
"object",
"ApiPlatform\\Metadata\\GetCollection"
],
"resource_class": [
"string",
"App\\Entity\\Comment"
],
"skip_null_values": [
"boolean",
true
],
"iri_only": [
"boolean",
false
],
"request_uri": [
"string",
"/api/comments?page=1"
],
"uri": [
"string",
"http://localhost:8000/api/comments?page=1"
],
"input": [
"null",
null
],
"output": [
"null",
null
],
"exclude_from_cache_key": [
"array",
[
[
"string",
"root_operation"
],
[
"string",
"operation"
],
[
"string",
"resources"
],
[
"string",
"resources_to_push"
]
]
],
"resources": [
"object",
"ApiPlatform\\Serializer\\ResourceList"
],
"resources_to_push": [
"object",
"ApiPlatform\\Serializer\\ResourceList"
],
"debug_trace_id": [
"string",
"64e781d66f771"
]
}
]
]
},
{
"namespace": "Symfony\\Component\\Serializer\\Debug",
"short_class": "TraceableSerializer",
"class": "Symfony\\Component\\Serializer\\Debug\\TraceableSerializer",
"type": "->",
"function": "serialize",
"file": "C:\\CODES\\FORMATION\\TutoModuleComment\\vendor\\api-platform\\core\\src\\Symfony\\EventListener\\SerializeListener.php",
"line": 99,
"args": [
[
"object",
"ApiPlatform\\Doctrine\\Orm\\Paginator"
],
[
"string",
"jsonld"
],
[
"array",
{
"groups": [
"array",
[
[
"string",
"comment:read"
]
]
],
"operation_name": [
"string",
"_api_/comments{._format}_get_collection"
],
"operation": [
"object",
"ApiPlatform\\Metadata\\GetCollection"
],
"resource_class": [
"string",
"App\\Entity\\Comment"
],
"skip_null_values": [
"boolean",
true
],
"iri_only": [
"boolean",
false
],
"request_uri": [
"string",
"/api/comments?page=1"
],
"uri": [
"string",
"http://localhost:8000/api/comments?page=1"
],
"input": [
"null",
null
],
"output": [
"null",
null
],
"exclude_from_cache_key": [
"array",
[
[
"string",
"root_operation"
],
[
"string",
"operation"
],
[
"string",
"resources"
],
[
"string",
"resources_to_push"
]
]
],
"resources": [
"object",
"ApiPlatform\\Serializer\\ResourceList"
],
"resources_to_push": [
"object",
"ApiPlatform\\Serializer\\ResourceList"
],
"debug_trace_id": [
"string",
"64e781d66f771"
]
}
]
]
},
{
"namespace": "ApiPlatform\\Symfony\\EventListener",
"short_class": "SerializeListener",
"class": "ApiPlatform\\Symfony\\EventListener\\SerializeListener",
"type": "->",
"function": "onKernelView",
"file": "C:\\CODES\\FORMATION\\TutoModuleComment\\vendor\\symfony\\event-dispatcher\\Debug\\WrappedListener.php",
"line": 116,
"args": [
[
"object",
"Symfony\\Component\\HttpKernel\\Event\\ViewEvent"
],
[
"string",
"kernel.view"
],
[
"object",
"Symfony\\Component\\HttpKernel\\Debug\\TraceableEventDispatcher"
]
]
},
{
"namespace": "Symfony\\Component\\EventDispatcher\\Debug",
"short_class": "WrappedListener",
"class": "Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener",
"type": "->",
"function": "__invoke",
"file": "C:\\CODES\\FORMATION\\TutoModuleComment\\vendor\\symfony\\event-dispatcher\\EventDispatcher.php",
"line": 220,
"args": [
[
"object",
"Symfony\\Component\\HttpKernel\\Event\\ViewEvent"
],
[
"string",
"kernel.view"
],
[
"object",
"Symfony\\Component\\HttpKernel\\Debug\\TraceableEventDispatcher"
]
]
},
{
"namespace": "Symfony\\Component\\EventDispatcher",
"short_class": "EventDispatcher",
"class": "Symfony\\Component\\EventDispatcher\\EventDispatcher",
"type": "->",
"function": "callListeners",
"file": "C:\\CODES\\FORMATION\\TutoModuleComment\\vendor\\symfony\\event-dispatcher\\EventDispatcher.php",
"line": 56,
"args": [
[
"array",
[
[
"object",
"Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener"
],
[
"object",
"Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener"
],
[
"object",
"Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener"
],
[
"object",
"Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener"
],
[
"object",
"Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener"
],
[
"object",
"Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener"
],
[
"object",
"Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener"
]
]
],
[
"string",
"kernel.view"
],
[
"object",
"Symfony\\Component\\HttpKernel\\Event\\ViewEvent"
]
]
},
{
"namespace": "Symfony\\Component\\EventDispatcher",
"short_class": "EventDispatcher",
"class": "Symfony\\Component\\EventDispatcher\\EventDispatcher",
"type": "->",
"function": "dispatch",
"file": "C:\\CODES\\FORMATION\\TutoModuleComment\\vendor\\symfony\\event-dispatcher\\Debug\\TraceableEventDispatcher.php",
"line": 139,
"args": [
[
"object",
"Symfony\\Component\\HttpKernel\\Event\\ViewEvent"
],
[
"string",
"kernel.view"
]
]
},
{
"namespace": "Symfony\\Component\\EventDispatcher\\Debug",
"short_class": "TraceableEventDispatcher",
"class": "Symfony\\Component\\EventDispatcher\\Debug\\TraceableEventDispatcher",
"type": "->",
"function": "dispatch",
"file": "C:\\CODES\\FORMATION\\TutoModuleComment\\vendor\\symfony\\http-kernel\\HttpKernel.php",
"line": 171,
"args": [
[
"object",
"Symfony\\Component\\HttpKernel\\Event\\ViewEvent"
],
[
"string",
"kernel.view"
]
]
},
{
"namespace": "Symfony\\Component\\HttpKernel",
"short_class": "HttpKernel",
"class": "Symfony\\Component\\HttpKernel\\HttpKernel",
"type": "->",
"function": "handleRaw",
"file": "C:\\CODES\\FORMATION\\TutoModuleComment\\vendor\\symfony\\http-kernel\\HttpKernel.php",
"line": 74,
"args": [
[
"object",
"Symfony\\Component\\HttpFoundation\\Request"
],
[
"integer",
1
]
]
},
{
"namespace": "Symfony\\Component\\HttpKernel",
"short_class": "HttpKernel",
"class": "Symfony\\Component\\HttpKernel\\HttpKernel",
"type": "->",
"function": "handle",
"file": "C:\\CODES\\FORMATION\\TutoModuleComment\\vendor\\symfony\\http-kernel\\Kernel.php",
"line": 197,
"args": [
[
"object",
"Symfony\\Component\\HttpFoundation\\Request"
],
[
"integer",
1
],
[
"boolean",
true
]
]
},
{
"namespace": "Symfony\\Component\\HttpKernel",
"short_class": "Kernel",
"class": "Symfony\\Component\\HttpKernel\\Kernel",
"type": "->",
"function": "handle",
"file": "C:\\CODES\\FORMATION\\TutoModuleComment\\vendor\\symfony\\runtime\\Runner\\Symfony\\HttpKernelRunner.php",
"line": 35,
"args": [
[
"object",
"Symfony\\Component\\HttpFoundation\\Request"
]
]
},
{
"namespace": "Symfony\\Component\\Runtime\\Runner\\Symfony",
"short_class": "HttpKernelRunner",
"class": "Symfony\\Component\\Runtime\\Runner\\Symfony\\HttpKernelRunner",
"type": "->",
"function": "run",
"file": "C:\\CODES\\FORMATION\\TutoModuleComment\\vendor\\autoload_runtime.php",
"line": 29,
"args": []
},
{
"namespace": "",
"short_class": "",
"class": "",
"type": "",
"function": "require_once",
"file": "C:\\CODES\\FORMATION\\TutoModuleComment\\public\\index.php",
"line": 5,
"args": [
[
"string",
"C:\\CODES\\FORMATION\\TutoModuleComment\\vendor\\autoload_runtime.php"
]
]
}
]
}
Hmm yea, somehow it thinks that Post
is an entity that it needs to query for. What does the top of your class (basically everything from the class
keyword and above to the top of the file) look like? Is the word Post
used anywhere else in your class?
That's it ! It was not actually on the top of my class, but missing : my class refers to an Entity Post, and I think I change the use (bad idea !).
I wrote back the use with an alias and refered to this alias in my class and... tadam ! Job is done !
Thank you Ryan ! You rock !
Woohoo! Good find - keep up the good work!
Hi Ryan,
I am unable to execute PUT/PATCH operations on my server (Error 405 Method not allowed). I am using Fastpanel where Nginx is running for the frontend. Tried some fixes from the internet but that didn't help. Any help would be really appreciated.
Best regards,
Stiff
Hey Stiff!
Hmm, well that's no fun :/. First, are you sure that the 405 Method not allowed is coming from Nginx? I mean, it probably is, but it could also, in theory, be coming from something like CloudFlare (if it is, it should be pretty obvious on that page) or Symfony itself (if it is, you would likely be seeing either your custom exception page or the generic "Symfony error page" for production).
Assuming that it IS coming from nginx, I'm familiar with Fastpanel or how much control it gives you over the raw nginx config. What have you tried so far?
Cheers!
"Houston: no signs of life"
Start the conversation!
What PHP libraries does this tutorial use?
// composer.json
{
"require": {
"php": ">=8.1",
"ext-ctype": "*",
"ext-iconv": "*",
"api-platform/core": "^3.0", // v3.0.8
"doctrine/annotations": "^1.0", // 1.14.2
"doctrine/doctrine-bundle": "^2.8", // 2.8.0
"doctrine/doctrine-migrations-bundle": "^3.2", // 3.2.2
"doctrine/orm": "^2.14", // 2.14.0
"nelmio/cors-bundle": "^2.2", // 2.2.0
"nesbot/carbon": "^2.64", // 2.64.1
"phpdocumentor/reflection-docblock": "^5.3", // 5.3.0
"phpstan/phpdoc-parser": "^1.15", // 1.15.3
"symfony/asset": "6.2.*", // v6.2.0
"symfony/console": "6.2.*", // v6.2.3
"symfony/dotenv": "6.2.*", // v6.2.0
"symfony/expression-language": "6.2.*", // v6.2.2
"symfony/flex": "^2", // v2.2.4
"symfony/framework-bundle": "6.2.*", // v6.2.3
"symfony/property-access": "6.2.*", // v6.2.3
"symfony/property-info": "6.2.*", // v6.2.3
"symfony/runtime": "6.2.*", // v6.2.0
"symfony/security-bundle": "6.2.*", // v6.2.3
"symfony/serializer": "6.2.*", // v6.2.3
"symfony/twig-bundle": "6.2.*", // v6.2.3
"symfony/ux-react": "^2.6", // v2.6.1
"symfony/validator": "6.2.*", // v6.2.3
"symfony/webpack-encore-bundle": "^1.16", // v1.16.0
"symfony/yaml": "6.2.*" // v6.2.2
},
"require-dev": {
"doctrine/doctrine-fixtures-bundle": "^3.4", // 3.4.2
"symfony/debug-bundle": "6.2.*", // v6.2.1
"symfony/maker-bundle": "^1.48", // v1.48.0
"symfony/monolog-bundle": "^3.0", // v3.8.0
"symfony/stopwatch": "6.2.*", // v6.2.0
"symfony/web-profiler-bundle": "6.2.*", // v6.2.4
"zenstruck/foundry": "^1.26" // v1.26.0
}
}
Hello Symfonycasts colleagues,
When clicking on one of the records from the profile search (404 127.0.0.1 GET
https://127.0.0.1:8000/api/projectss.json 20-Apr-2024
13:41:01
5dae03) I have got the following error message.
How can I resolve this error.
Format "json" is not supported