I am trying to declare a subresource in my symfony app. I followed the api-platform doc about it: https://api-platform.com/docs/core/subresources/
The subresource does appear in the routes, but not under its parent resource.
The routes I currently have:
api_files_get_collection GET ANY ANY /api/files.{_format}
api_files_post_collection POST ANY ANY /api/files.{_format}
api_files_get_item GET ANY ANY /api/files/{id}.{_format}
api_files_patch_item PATCH ANY ANY /api/files/{id}.{_format}
api_files_put_item PUT ANY ANY /api/files/{id}.{_format}
api_external_subscription_requests_get_collection GET ANY ANY /api/external_subscription_requests.{_format}
api_external_subscription_requests_post_publication_collection POST ANY ANY /api/subscribe
api_external_subscription_requests_get_item GET ANY ANY /api/external_subscription_requests/{id}.{_format}
The routes I would like to have:
api_files_get_collection GET ANY ANY /api/files.{_format}
api_files_post_collection POST ANY ANY /api/files.{_format}
api_files_get_item GET ANY ANY /api/files/{id}.{_format}
api_files_patch_item PATCH ANY ANY /api/files/{id}.{_format}
api_files_put_item PUT ANY ANY /api/files/{id}.{_format}
api_files_external_subscription_requests_get_collection GET ANY ANY /api/files/{id}/external_subscription_requests.{_format}
api_files_external_subscription_requests_post_publication_collection POST ANY ANY /api/files/{id}/subscribe
api_files_external_subscription_requests_get_item GET ANY ANY /api/files/{id}/external_subscription_requests/{id}.{_format}
Code in App\Entity\File.php:
/**
* #ORM\Entity
* #ApiResource(
* attributes={"order"={"createdAt": "DESC"}},
* collectionOperations={
* "get"={
* "normalization_context"={"groups"={"model:timestampable", "file:collection:read"}},
* },
* "post",
* },
* itemOperations={
* "get"={
* "normalization_context"={"groups"={"model:timestampable", "file:item:read"}},
* },
* "patch",
* "put"
* },
* )
*/
class File
{
// ...
/**
* #ORM\OneToMany(targetEntity="App\Entity\ExternalSubscriptionRequest", cascade={"all"}, mappedBy="file")
* #Groups({
* "file:collection:read",
* "file:item:read"
* })
* #ApiSubresource()
*/
private $external_subscription_requests;
// ...
}
Code in App\Entity\ExternalSubscriptionRequest.php:
/**
* #ORM\Entity
* #ApiResource(
* collectionOperations={
* "get",
* "post_publication"={
* "method"="POST",
* "path"="/subscribe",
* "controller"=SubscribeToConso::class,
* }
* },
* itemOperations={
* "get",
* },
* )
*/
class ExternalSubscriptionRequest
{
// ...
/**
* #var File the file this request was made for
*
* #ORM\ManyToOne(targetEntity="App\Entity\File", inversedBy="external_subscription_requests")
* #ORM\JoinColumn(referencedColumnName="id", nullable=false)
*
* #Groups({
* "external_subscription_request:collection:read",
* "external_subscription_request:item:read"
* })
*/
public $file;
// ...
}
Related
In my API config file, I disabled pagination :
api_platform.yaml :
api_platform:
collection:
pagination:
enabled: false
But I want to enable pagination for a specific subresource :
Parent class :
/**
* #ORM\Entity(repositoryClass="App\Repository\ForumRepository")
* #ApiResource(
* collectionOperations={
* "get",
* },
* itemOperations={"get"},
* )
*/
class Forum
{
...
/**
* #var ForumSubject[]|ArrayCollection
*
* #ORM\OneToMany(
* targetEntity="App\Entity\ForumSubject",
* mappedBy="forum",
* )
* #ApiSubresource(maxDepth=1)
*/
private $subjects;
...
Children class :
/**
* #ORM\Entity(repositoryClass="App\Repository\ForumSubjectRepository")
* #ApiResource(
* collectionOperations={
* "post",
* },
* itemOperations={
* "get",
* "put",
* "delete"
* },
* )
*/
class ForumSubject
{
...
Everything works fine I can access my sub-resources :
/api/forums/a21cb5db-aed7-45a6-884f-3d3e6d7abd8c/subjects
(Return all subjects of forum, route name : api_forums_subjects_get_subresource ).
But I am unable to enable paging on this route, the documentation does not mention anything.
After research I tried this, but nothing works :
Does not work in parent or children class :
* #ApiResource(
* ...
* subresourceOperations={
* "api_forums_subjects_get_subresource"={"pagination_enabled"=true}
* }
Does not work in parent class ; error un children class (The operation "api_forums_subjects_get_subresource" cannot be found in the Swagger specification).
* #ApiResource(
* ...
* collectionOperations={
* "get",
* "api_forums_subjects_get_subresource"={"pagination_enabled"=true}
* },
After looking in the API Platform source code, here is the solution :
In children class :
* #ApiResource(
* ...
* collectionOperations={
* ...
* "api_forums_subjects_get_subresource"={
* "method"="get", // don't forget : otherwise trigger a strange swagger error
* "pagination_enabled"=true
* }
With Api Platform, we can order by association.
Documentation :
use ApiPlatform\Core\Annotation\ApiResource;
/**
* #ApiResource(attributes={"order"={"author.username"}})
*/
class Book
{
...
But I want to order on an entity cascade.
For the example below I tested this with this error :
* #ApiResource(
* attributes={
* "order"={"exercises.repetitions.id":"asc"}
* }
* )
[Semantical Error] line 0, col 422 near 'id ASC, rests_a6.id': Error: Class App\Entity\TrainingSerieExercise has no field or association named repetitions.id
TrainingSerie entity :
* #ApiResource(
...
* attributes={
* "order"={"exercises.repetitions.id":"asc"}
* }
* )
*/
class TrainingSerie
{
...
/**
* #var TrainingSerieExercise[]|ArrayCollection
*
* #ORM\OneToMany(
* targetEntity="App\Entity\TrainingSerieExercise",
* mappedBy="serie",
* cascade={"persist"}
* )
* #Groups({
* "trainings_read",
* "training_series_read",
* "training_series_write",
* })
*/
private $exercises;
...
TrainingSerieExercise entity :
...
class TrainingSerieExercise
{
...
/**
* #var TrainingSerieRepetition[]|ArrayCollection
*
* #ORM\OneToMany(
* targetEntity="App\Entity\TrainingSerieExerciseRepetition",
* mappedBy="exercise",
* cascade={"persist"},
* )
* #Groups({
* "training_series_read",
* "training_series_write",
* "training_serie_exercices_write",
* })
*/
private $repetitions;
...
TrainingSerieExerciseRepetition entity :
class TrainingSerieExerciseRepetition
{
...
/**
* #var integer
*
* #ORM\Id
* #ORM\Column(name="id", type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
* #Groups({
* "training_series_read",
* })
*/
private $id;
...
Following this Doctrine doc this will order TrainingSerieExercise::$repetitions by id:
class TrainingSerieExercise
{
...
/**
* #var TrainingSerieRepetition[]|ArrayCollection
*
* #ORM\OneToMany(
* targetEntity="App\Entity\TrainingSerieExerciseRepetition",
* mappedBy="exercise",
* cascade={"persist"},
* )
* #ORM\OrderBy({"id" = "ASC"})
* #Groups({
* "training_series_read",
* "training_series_write",
* "training_serie_exercices_write",
* })
*/
private $repetitions;
...
With Symfony 5, Api Platform and Doctrine, I want to update a entity and his array collection linked.
When I send PUT request to Api Platform the main entity (UserMeasurement #17) is updated but not the child entity (UserMeasurementMeasurement #28) : a new entry is incremented and created.
PUT request : /api/user_measurements/17
{
"date": "2020-10-20T12:11:22.609Z",
"measurements": [
{
"id": "/api/user_measurement_measurements/28", // same result with "28" value
"value": "7"
}
]
}
(when creating POST the datas everything works fine (main entity and child entity are created))
Part of main entity :
* #ApiResource(
* collectionOperations={"get", "post"},
* itemOperations={"get", "put", "delete"},
* normalizationContext={
* "groups"={"user_measurements_read"}
* },
* denormalizationContext={
* "groups"={"user_measurements_write"}
* },
* )
class UserMeasurement
{
...
/**
* #var UserMeasurementMeasurement[]|ArrayCollection
*
* #ORM\OneToMany(
* targetEntity="App\Entity\UserMeasurementMeasurement",
* mappedBy="userMeasurement",
* cascade={"persist"},
* )
* #Groups({
* "user_measurements_read",
* "user_measurements_write",
* })
*/
private $measurements;
...
Part of child entity :
* #ApiResource(
* collectionOperations={"get", "post"},
* itemOperations={"get", "put", "delete"},
* normalizationContext={
* "skip_null_values"=false,
* "groups"={"user_measurement_measurements_read"}
* },
* )
class UserMeasurementMeasurement
{
...
/**
* #var float
*
* #ORM\Column(type="decimal", nullable=true, precision=6, scale=2)
* #Assert\Positive()
* #Groups({
* "user_measurements_read",
* "user_measurements_write",
* })
*/
private $value;
...
When using application/ld+json, you must provide the #id property:
PUT request : /api/user_measurements/17
{
"date": "2020-10-20T12:11:22.609Z",
"measurements": [
{
"#id": "/api/user_measurement_measurements/28",
"value": "7"
}
]
}
I'm stuck with something with API Platform and Vich Uploader for a PUT request, the POST is working juste fine.
Here is my header for MediaObject entity :
/**
* #ORM\Entity
* #ApiResource(
* iri="http://schema.org/MediaObject",
* normalizationContext={
* "groups"={"media_object_read"}
* },
* collectionOperations={
* "post"={
* "controller"=CreateMediaObjectAction::class,
* "deserialize"=false,
* "security"="is_granted('ROLE_USER')",
* "validation_groups"={"Default", "media_object_create"},
* "openapi_context"={
* "requestBody"={
* "content"={
* "multipart/form-data"={
* "schema"={
* "type"="object",
* "properties"={
* "file"={
* "type"="string",
* "format"="binary"
* }
* }
* }
* }
* }
* }
* }
* },
* "get",
* },
* itemOperations={
* "get",
* "put"={"controller"=UpdateMediaObjectAction::class,
* "deserialize"=false,
* "security"="is_granted('ROLE_USER')",
* "validation_groups"={"Default", "media_object_update"},
* "openapi_context"={
* "requestBody"={
* "content"={
* "multipart/form-data"={
* "schema"={
* "type"="object",
* "properties"={
* "file"={
* "type"="string",
* "format"="binary"
* }
* }
* }
* }
* }
* }
* }},
* "delete"={
* "security"="is_granted('ROLE_USER')"
* }
* }
* )
* #Vich\Uploadable
*/
class MediaObject
{...}
In swagger it throws an error because the file is not attached to the request. It strange because I have exactly the same file input field, with just an ID parameter added.
Someone has manage to do that ?
Found solution by myself !
Finally, the problem is that PHP does'nt handle well file in FormData on PUT, but well on POST.
So if you got the same issue change to "method"="POST" in the "put" part from itemOperations.
Am working on Symfony API Platform, to add and retrieve something. Table has two fields id and title.
But when i run the GET query the API is returning title only not id.
How to return ID too?
My Annotation:-
* #ORM\Table(
* name="school",
* #ApiResource(
* attributes={
* "order"={"title": "ASC"},
* "normalization_context"={"groups"={"school.read"},
"enable_max_depth"=true},
* },
* itemOperations={
* "get",
* "put"
* },
* collectionOperations={
* "get"={
* "normalization_context"={
* "groups"={"school.read"}
* }
* }
* },
* normalizationContext={
* "groups"={"school.read"}
* },
* denormalizationContext={
* "groups"={"school.write"}
* }
* )
* #ORM\Entity(repositoryClass="Eqsgroup\Repository\SchoolRepository")
* #UniqueEntity(
* "title",
* repositoryMethod="findByUniqueCriteria",
* message="School already exists."
* )
*/
This is the Entity class
class School
{
/**
* #var string the id of this School
*
* #ORM\Id
* #ORM\Column(type="guid", unique=true)
* #ORM\GeneratedValue(strategy="CUSTOM")
* #ORM\CustomIdGenerator(class="Ramsey\Uuid\Doctrine\UuidGenerator")
* #Groups({"school.read, school.write"})
*/
private $id;
/**
* #var string The title of the school
*
* #ORM\Column(type="string", length=255)
* #Assert\NotNull(message="school should not be empty")
* #Assert\NotBlank(message="school should not be empty")
* #Assert\Length(
* min = 1,
* max = 250,
* minMessage = "length.min,{{ limit }}",
* maxMessage = "length.max,{{ limit }}"
* )
* #Groups({"school.read", "school.write"})
*/
private $title;
public function __construct(){ }
public function getId(): ?string
{
return $this->id;
}
public function getTitle(): ?string
{
return $this->title;
}
public function setTitle(string $title): self
{
$this->title = $title;
return $this;
}
This is the output am currently getting:
[
{
"title": "Test"
},
{
"title": "Test2"
},
]
The expected output will include the auto generated is along with title.
Add the #Groups to allow Apli-Platform to read each field you want like this :
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
* #Groups({"school.read", "school.write"})
*/
private $id;
Have a look to the documentation here :
https://api-platform.com/docs/core/serialization/#the-serialization-context-groups-and-relations
Did you configure which attributes you want to expose ?
If not, configure it in the yaml of your entity :
# I don't know the path to your entity, just modify what you need to
XXXX\XXXX\XXXX\XXXX\School:
exclusion_policy: ALL
properties:
id:
expose: true
title:
expose: true