Sometimes we may need to display some information of a subresource, like this:
{
"@context": "\\/api\\/contexts\\/Comment",
"@id": "\\/api\\/blog_posts\\/583\\/comments",
"@type": "hydra:Collection",
"hydra:member": [
{
"@id": "\\/api\\/comments\\/764",
"@type": "Comment",
"content": "Do you think, at your age, it is I hate cats and dogs.' It was the Cat in a solemn tone, 'For the Duchess. An invitation from the roof. There were doors all round her, about the whiting!' 'Oh, as to.",
"published": "2021-10-18T20:12:08+00:00",
**"author": {
"@id": "\\/api\\/users\\/22",
"@type": "User",
"username": "john_doe",
"name": "John Doe"
}**
}
],
"hydra:totalItems": 1
}
We can accomplish that with subresourceOperations.
First we need to get the route on which we want to have the attributes of the sub-resource:
php bin/console debug:route
----------------------------------------- -------- -------- ------ -----------------------------------------
Name Method Scheme Host Path
----------------------------------------- -------- -------- ------ -----------------------------------------
...
**api_blog_posts_comments_get_subresource GET ANY ANY /api/blog_posts/{id}/comments.{_format}**
...
----------------------------------------- -------- -------- ------ -----------------------------------------
Then we define the subresource operation in our ApiResource:
<aside> ❗ If we do not want this for a spefic route, there’s no need to use the subresourceOperations, meaning that assign that normalization_context to an itemOperations.
</aside>
/**
* @ApiResource(
* itemOperations={
* "get",
* "put"={
* "security"="is_granted('IS_AUTHENTICATED_FULLY') and object.getAuthor() == user"
* }
* },
* collectionOperations={
* "get",
* "post"={
* "security"="is_granted('IS_AUTHENTICATED_FULLY')"
* }
* },
*** subresourceOperations={
* "api_blog_posts_comments_get_subresource"={
* "normalization_context"={
* "groups"={"get-comment-with-author"}
* }
* }
* },**
* denormalizationContext={
* "groups"={"post"}
* }
* )
* @ORM\\Entity(repositoryClass=CommentRepository::class)
*/
class Comment implements AuthoredEntityInterface, PublishedDateEntityInterface
{
/**
* @ORM\\Id
* @ORM\\GeneratedValue
* @ORM\\Column(type="integer")
*** @Groups({"get-comment-with-authorget-comment-with-author"})**
*/
private $id;
/**
* @ORM\\Column(type="text")
* @Assert\\NotBlank()
* @Assert\\Length(min=5, max=3000)
*** @Groups({"get-comment-with-author","post"})**
*/
private $content;
/**
* @ORM\\Column(type="datetime")
*** @Groups({"get-comment-with-author"})**
*/
private $published;
/**
* @ORM\\ManyToOne(targetEntity="App\\Entity\\User", inversedBy="comments")
* @ORM\\JoinColumn(nullable=false)
*** @Groups({"get-comment-with-author"})**
*/
private $author;
/**
* @ORM\\ManyToOne(targetEntity="App\\Entity\\BlogPost")
* @ORM\\JoinColumn(nullable=false)
* @Groups({"post"})
*/
private $blogPost;
We now just need to use the same group on our sub resources:
/**
*@ApiResource(
* normalizationContext={"groups"={"get"}},
* itemOperations={
* "get"={
* "security"="is_granted('IS_AUTHENTICATED_FULLY')",
* "normalization_context"={
* "groups"={"get"}
* }
* },
* "put"={
* "security"="is_granted('IS_AUTHENTICATED_FULLY') and object.getUsername() == user.getUsername()",
* "denormalization_context"={
* "groups"={"put"}
* }
* }
* },
* collectionOperations={
* "post"={
* "denormalization_context"={
* "groups"={"post"}
* }
* }
* },
* )
*@ORM\\Entity(repositoryClass=UserRepository::class)
*@UniqueEntity("username")
*@UniqueEntity("email")
*/
class User implements UserInterface, PasswordAuthenticatedUserInterface
{
/**
*@ORM\\Id
*@ORM\\GeneratedValue
*@ORM\\Column(type="integer")
*@Groups({"get"})
*/
private $id;
/**
*@ORM\\Column(type="string", length=255)
***@Groups({"get", "post", "get-comment-with-author"})**
*@Assert\\NotBlank()
*@Assert\\Length(min=6, max=255)
*/
private $username;
/**
*@ORM\\Column(type="string", length=255)
*@Groups({"put", "post"})
*@Assert\\NotBlank()
*@Assert\\Regex(
* pattern="/(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9]).{7,}/",
* message="Password must be seven characters long and contains at least one digit, one upper case and one lower case letter"
* )
*/
private $password;
/**
*@Groups({"put", "post"})
*@Assert\\NotBlank
*@Assert\\Expression(
* "this.getPassword() === this.getRetypedPassword()",
* message="Passwords does not match"
* )
*/
private $retypedPassword;
/**
*@ORM\\Column(type="string", length=255)
***@Groups({"get", "post", "put", "get-comment-with-author"})**
*@Assert\\NotBlank()
*@Assert\\Length(min=3, max=255)
*/
private $name;
/**
*@ORM\\Column(type="string", length=255)
*@Groups({"post", "put"})
*@Assert\\NotBlank()
*@Assert\\Email()
*@Assert\\Length(min=6, max=255)
*/
private $email;
/**
*@ORM\\OneToMany(targetEntity="App\\Entity\\BlogPost", mappedBy="author")
*@Groups({"get"})
*/
private $posts;
/**
*@ORM\\OneToMany(targetEntity="App\\Entity\\Comment", mappedBy="author")
*@Groups({"get"})
*/
private $comments;