Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

Serialization Groups

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $12.00

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

If the only way to control the input and output of our API was by controlling the getters and setters on our entity, it wouldn't be that flexible... and could be a bit dangerous. You might add a new getter or setter method for something internal and not realize that you were exposing new data in your API!

The solution for this - and the way that I recommend doing things in all cases - is to use serialization groups.

Adding a Group for Normalization

In the annotation, add normalizationContext. Remember, normalization is when you're going from your object to an array. So this option is related to when you are reading data from your API. Context is basically "options" that you pass to that process. The most common option by far is called "groups", which you set to another array. Add one string here: cheese_listing:read.

... lines 1 - 8
/**
* @ApiResource(
... lines 11 - 16
* normalizationContext={"groups"={"cheese_listing:read"}}
* )
... line 19
*/
class CheeseListing
... lines 22 - 125

Thanks to this, when an object is being serialized, the serializer will only include fields that are in this cheese_listing:read group, because, in a second, we're going to start adding groups to each property.

But right now, we haven't added any groups to anything. And so, if you go over and try your get collection operation... oh! Ah! A huge error!

Debugging Errors

Let's... pretend like I did that on purpose and see how to debug it! The problem is that the giant HTML error is... a bit hard to read. One way to see the error is to use our trick from earlier: go to https://localhost:8000/_profiler/.

Woh! Ok, there are two types of errors: runtime errors, where something went wrong specific to that request, and build errors, where some invalid configuration is killing every page. Most of the time, if you see an exception, there is still a profiler you can find for that request by using the trick of going to this URL, finding that request in the list - usually right on top - and clicking the sha into its profiler. Once you're there, you can click an "Exception" tab on the left to see the big, beautiful normal exception.

If you get a build error that kills every page, it's even easier: you'll see it when trying to access anything.

Anyways, the problem here is with my annotation syntax. I do this a lot - which is no big deal as long as you know how to debug the error. And, yep! I forgot a comma at the end.

Adding Groups to Fields

Refresh again! The profiler works, so now we can go back over and hit execute again. Check it out - we have @id and @type from JSON-LD... but it doesn't contain any real fields because none of them are in the new cheese_listing:read group!

Copy the cheese_listing:read group name. To add fields to this, above title, use @Groups(), {""} and paste. Let's also put that above description... and price.

... lines 1 - 7
use Symfony\Component\Serializer\Annotation\Groups;
... lines 9 - 21
class CheeseListing
{
... lines 24 - 30
/**
... line 32
* @Groups({"cheese_listing:read"})
*/
private $title;
... line 36
/**
... line 38
* @Groups({"cheese_listing:read"})
*/
private $description;
... line 42
/**
... lines 44 - 46
* @Groups({"cheese_listing:read"})
*/
private $price;
... lines 50 - 127
}

Flip back over and try it again. Beautiful! We get those three exact fields. I love this control.

By the way - the name cheese_listing:read... I just made that up - you could use anything. But, I will be following a group naming convention that I recommend. It'll give you flexibility, but keep things organized.

Adding Denormalization Groups

Now we can do the same thing with the input data. Copy normalizationContext, paste, and add de in front to make denormalizationContext. This time, use the group: cheese_listing:write

... lines 1 - 9
/**
* @ApiResource(
... lines 12 - 18
* denormalizationContext={"groups"={"cheese_listing:write"}}
* )
... line 21
*/
... lines 23 - 130

Copy that and... let's see... just add this to title and price for now. We actually don't want to add it to description. Instead, we'll talk about how to add this group to the fake textDescription in a minute.

... lines 1 - 22
class CheeseListing
{
... lines 25 - 31
/**
... line 33
* @Groups({"cheese_listing:read", "cheese_listing:write"})
*/
private $title;
... lines 37 - 43
/**
... lines 45 - 47
* @Groups({"cheese_listing:read", "cheese_listing:write"})
*/
private $price;
... lines 51 - 128
}

Move over and refresh again. Open up the POST endpoint.... yea - the only fields we can pass now are title and price!

So normalizationContext and denormalizationContext are two totally separate configs for the two directions: reading our data - normalization - and writing our data - denormalization.

The Open API Read & Write Models

At the bottom of the docs, you'll also notice that we now have two models: the read model - that's the normalization context with title, description and price, and the write model with title and price.

And, it's not really important, but you can control these names if you want. Add another option: swagger_definition_name set to "Read". And then the same thing below... set to Write.

... lines 1 - 9
/**
* @ApiResource(
... lines 12 - 17
* normalizationContext={"groups"={"cheese_listing:read"}, "swagger_definition_name"="Read"},
* denormalizationContext={"groups"={"cheese_listing:write"}, "swagger_definition_name"="Write"}
* )
... line 21
*/
... lines 23 - 130

I don't normally care about this, but if you want to control it, you can.

Adding Groups to Fake Fields

But, we're missing some fields! When we read the data, we get back title, description and price. But what about our createdAt field or our custom createdAtAgo field?

Let's pretend that we only want to expose createdAtAgo. No problem! Just add the @Groups annotation to that property... oh wait... there is no createdAtAgo property. Ah, it's just as easy: find the getter and put the annotation there: @Groups({"cheese_listing:read"}). And while we're here, I'll add some documentation to that method:

How long ago in text that this cheese listing was added.

... lines 1 - 22
class CheeseListing
{
... lines 25 - 112
/**
* How long ago in text that this cheese listing was added.
*
* @Groups("cheese_listing:read")
*/
public function getCreatedAtAgo(): string
... lines 119 - 133
}

Let's try it! Refresh the docs. Down in the models section... nice! There's our new createdAtAgo readonly field. And that documentation we added shows up here. Nice! No surprise that when we try it... the field shows up.

For denormalization - for sending data - we need to re-add our fake textDescription field. Search for the setTextDescription() method. To prevent API clients from sending us the description field directly, we removed the setDescription() method. Above setTextDescription(), add @Groups({"cheese_listing:write"}). And again, let's give this some extra docs.

... lines 1 - 88
/**
* The description of the cheese as raw text.
*
* @Groups("cheese_listing:write")
*/
public function setTextDescription(string $description): self
... lines 95 - 140

This time, when we refresh the docs, you can see it on the write model and, of course, on the data that we can send to the POST operation.

Have Whatever Getters and Setters You Want

And... this leads us to some great news! If we decide that something internally in our app does need to set the description property directly, it's now perfectly ok to re-add the original setDescription() method. That won't become part of our API.

... lines 1 - 88
public function setDescription(string $description): self
{
$this->description = $description;
return $this;
}
... lines 95 - 147

Default isPublished Value

Let's try all of this out. Refresh the docs page. Let's create a new listing: Delicious chèvre - excuse my French - for $25 and a description with some line breaks. Execute!

Woh! A 500 error! I could go look at this exception in the profiler, but this one is pretty easy to read: an exception in our query: is_published cannot be null. Oh, that makes sense: the user isn't sending is_published... so nobody is setting it. And it's set to not null in the database. No worries: default the property to false.

... lines 1 - 22
class CheeseListing
{
... lines 25 - 59
private $isPublished = false;
... lines 61 - 145
}

Tip

Actually, the auto-validation was not enabled by default in Symfony 4.3, but may be in Symfony 4.4.

By the way, if you're using Symfony 4.3, instead of a Doctrine error, you may have gotten a validation error. That's due to a new feature where Doctrine database constraints can automatically be used to add validation. So, if you see a validation error, awesome!

Anyways, try to execute it again. It works! We have exactly the input fields and output fields that we want. The isPublished field isn't exposed at all in our API, but it is being set in the background.

Next, let's learn a few more serialization tricks - like how to control the field name and how to handle constructor arguments.

Leave a comment!

59
Login or Register to join the conversation
John christensen Avatar
John christensen Avatar John christensen | posted 3 years ago

To get the @Groups annotation to work, I had to add this use statement:

use Symfony\Component\Serializer\Annotation\Groups;

Unless I missed something earlier, that step should be added to the Adding a Group for Normalization section.

2 Reply

Hey John,

You're totally right, we forgot to show the namespace in the code block, I fixed it in https://github.com/SymfonyC... - the correct code block will be available very soon on the website.

Thank you for reporting it!

Cheers!

Reply
Krzysztof R. Avatar
Krzysztof R. Avatar Krzysztof R. | posted 7 months ago

I had troubles with serialization groups. For thouse with similar problem, maybe my solution will help.
Try to add
serializer:
enable_annotations: true

to framework settings (config/packages/framework.yaml).
(I'm on symfony 6.0.3 and PHP 8)

1 Reply

Hey Krzysztof R.

Can you please provide more info about issue you got?

I guess it's related to PHP Attributes, some packages uses attributes instead of annotation, when supported PHP version is installed

Cheers!

Reply
Krzysztof R. Avatar
Krzysztof R. Avatar Krzysztof R. | sadikoff | posted 7 months ago

Yes I'm using attributes, but annotations also did't work for me.
It was working fine for regular entity fields. But when I try to add e.g. textDescription field to the API it was not present in schema or endpoint (I tried to send it anyway, but didn't work).
When I enabled annotations in serializer config it started to work ;)

1 Reply

interesting Attributes should work without additional configuration IIRC...

Thanks again for investigation

Reply
Krzysztof R. Avatar
Krzysztof R. Avatar Krzysztof R. | sadikoff | posted 7 months ago

I was starting from this boilerplate
https://github.com/martinso...
maybe there was something wrong with config. I'm not that much into symfony to check that. Anyway it is working perfect now so all good ;)

Cheers ;)

Reply
Default user avatar
Default user avatar GianlucaF | posted 3 months ago

Hi,
Is there a way to set the serialization group dynamically, using, for example, a parameter on query string?

Reply

Hello GianlucaF

If I understood you correctly, then you can do it with context builder. You will find it in the security part of ApiPlatform series

PS fast link https://symfonycasts.com/sc...

StayTuned! Cheers!

Reply
Sam Z. Avatar

I am having a strange issue whereby my setTextDescription is not coming through on my swagger page for the post action

Reply

Hey Sam Z.

Did you add the cheese_listing:write group on it?

Reply
Aleksander R. Avatar
Aleksander R. Avatar Aleksander R. | MolloKhan | posted 1 year ago

Hey, Diego!

I have the same problem as Sam. My setTextDescription just won't show up for the POST action when I use this serialization groups. I tried to change the name to setDescription and then it shows.

Reply

Hey @Alexsander!

Thanks for the extra info! This is interesting. I'm wondering 2 things:

1) If you use setTextDescription() (where it son't show up for the POST action), can you actually *send* a textDescription field in the JSON and have it update?

2) If you use setTextDescription() with an @SerializedName("description") above the method, does it show up then?

This is odd enough that I'm wondering if something changed in newer versions of ApiPlatform. If they did, my guess is that would be unintentional (i.e. a bug).

Cheers!

Reply

With SerializedName works. Thanks

Reply
Aleksander R. Avatar
Aleksander R. Avatar Aleksander R. | weaverryan | posted 1 year ago

Hey Ryan!

So I tried to do all over again and the problem is still the same.

1) When I (as shown in the tutorials) delete setDescription() and use setTextDescription() method instead everything is working fine. I have textDescription field and I can send/update database with this field. But as soon as I use serialization groups this field is gone from POST operation. I have only "title" and "price" fields. I also want to point out that in my "write model/schema" the "textDescription" field is shown, but in POST operation it is not shown.

2) No even in this case it won't show up. But since I can see this textDescription field in my "write model", this command updates it from "textDescription" to "description" as expected.

Reply
Sam Z. Avatar

So when I use the post action along with @SerializedName("description") then the method works so it seems to be working as planned the only issue is it does not show up on the swagger page

Reply

Hey Sam Z. & @Aleksander!

Sorry for the slow reply - I had some time off this week :). Ok, with these new updates... and the fact that there are two of you having the issue - I'm *fairly* sure that this is some bug in API Platform in a newer version (than we use in the tutorial). I'd recommend opening an issue in API Platform if you can with the details. I can't (at the moment) test this myself... because I'm on SUCH slow internet that Composer barely works 🤦‍♂️

Anyways, if either if you DO create an issue, please link me to it and I can look / add any extra info to the issue :).

Cheers!

Reply
Tristan P. Avatar

Did anyone open an issue yet? I can't find any on GitHub.
I happen to have the exact same problem as @Aleksander and @Sam Zlotnick.
Did you guys open an issue or find out how to work around that bug?
I also made the following observation:

the description field is shown in the Schemas/Model section from the swagger doc:

cheeses-Write{
title string
minLength: 2
maxLength: 50
price* integer
description string
writeOnly: true

The description of the cheese as raw text.
}

But under POST ​/api​/cheeses Creates a cheeses resource the field is missing, as the Example Value just shows:

{
"title": "string",
"price": 0
}

But when i click on "Schema", just next to "Example Value" it says:

cheeses.jsonld-Read{
title string
minLength: 2
maxLength: 50
price* integer
}

Am i confused, or shouldn't it say"Write" instead of read here?

Reply

Hey Tristan P.!

As I just mentioned on a different chapter, I think this issue is the same as the other one - https://symfonycasts.com/sc...

I am also not aware of any issue that's been opened yet - I would love if you could open it!

Cheers!

Reply
Tristan P. Avatar

Thanks for the reply, I opnened an issue on friday: https://github.com/api-plat...

btw: great course and teacher!

Reply

Ah, great work Tristan P.! And it looks like someone has linked to another issue - https://github.com/api-plat... and another issue https://github.com/api-plat... which might be the same thing. That is GREAT news... because it makes it seem like the bug is definitely real and (hopefully) it should get attention. One user says that downgrading to 2.6.3 fixes it. I've added a comment to https://github.com/api-plat... to see if I can motivate someone to do some extra digging ;).

Cheers!

Reply
Sam Z. Avatar

The below are the 2 setters I have for the description however in the swagger page under post it only shows title and price for the inputs


/**
* @ORM\Column(type="text")
* @Groups("cheese_listing:read")
*/
private $description;

public function getDescription(): ?string {
return $this->description;
}

/**
* The description of the cheese as raw text.
*
* @Groups("cheese_listing:write")
*/
public function setTextDescription(string $description): self {
$this->description = nl2br($description);
return $this;
}
Reply
Nestor B. Avatar
Nestor B. Avatar Nestor B. | posted 1 year ago

Hi, I'm using api-platform configuration with yaml and not with annotations, also the serializer, so, I'm struggling to get a getProperty() to work with groups, like your example of getCreatedAtAgo(), but, in my case, I can't or I shouldn't put "Groups({"read"})" annotation on that method, I must set it in the yaml. Is this possible anyway?

PD: Sorry for my english :)

Reply

Hey Nestor B.

I think that's possible, you can configure your groups by entity like shown in this example: https://api-platform.com/do...

Cheers!

Reply
Dmitriy Avatar
Dmitriy Avatar Dmitriy | posted 1 year ago

Hello. I have 2 groups to read:

* @ApiResource(
* normalizationContext={"groups"={"group:read","group_clear:read"}}
* )

By default works "group:read" context. How can I make a request to the api platform to get data in context "group_clear:read"?

Reply

Hey Dmitriy!

It sounds like you want to be able to add a group dynamically - maybe based on the currently-authenticated user or maybe a query parameter. Is that right? If so check out a custom context builder - we do something (sort of) similar here - https://symfonycasts.com/sc...

I hope that helps! Cheers!

Reply
Dmitriy Avatar

Thank you, Ryan. I solved this problem with:

https://api-platform.com/do...

Your solution is interesting, but so far too complicated for me )

Reply

Hey Dmitriy!

Happy you got it working - my solutions are not always the only, or even the best ones ;).

Cheers!

Reply
David B. Avatar
David B. Avatar David B. | posted 2 years ago

Properties not showing up when I use normalization contexts groups. Any ideas? Code example here: https://pastebin.com/UtyHsZt5

Reply

Hey David B.

Hmm, your code looks correct. It makes me wonder if for some reason your cache didn't refresh automatically. Try deleting it manually rm var/cache/* and try again. Also, double-check that you're running in the dev environment because if you're in prod, then you have to refresh the cache after every single change

Cheers!

Reply
David B. Avatar

Thank you for responding so quickly! I manged to figure it out shortly after I posted the question. Out of pure frustration I did a cache:clear and that resolved the issue. My DEV evn is running on a virtual machine so i'll chalk it up to a VM issue.

1 Reply
Paul Avatar

Hello,
Thanks for the tutorial. I am following it and trying to code along with an example project.
But as soon as I added the serialization groups I had a problem with the properties. For example one is only read-only anymore but has the write group as well. I also tried to make the request and ignore the docs but it didn't work.
I pushed the project to github: https://github.com/schnetzi...
Can you explain to me if I made any mistakes? Is the indentation in the comments important?
Thanks in advance!

Reply
Paul Avatar

I investigated a little bit further and it seems like the underscore in the property is causing the problem/error. For example `group_name` is read-only even though it has the serialization group to write but `position` works just fine. It's also with all the other underscore-properties.

When I set the properties to public the readonly is gone and it works.

Reply

Hey Paul

That's happening because you have an inconsistency in your "casing", I noticed that you have some "camel cased" properties and others in the "snake case" form. In theory, you should not do that, if you can refactor it, great! If not, you can choose which "Name Converter" to use (check this out: https://api-platform.com/do... ), or, you can use the "@SerializedName" annotation to choose a valid name https://symfony.com/doc/mas...

I hope this helps. Cheers!

1 Reply
Paul Avatar

Wow good catch! Thanks a lot. I changed my properties to be camel-case now and it works as `private` properties.

1 Reply
Hintay Avatar

Hello,
Thanks for this awesome tutorial! But I did notice one small thing, in the video for this chapter, the symbols under the SymfonyCast logo and in the popup prompt are shown as question mark boxes.
Cheers!

Reply

The weird question marks have been fixed, thank you so much for pointing that out Hintay !

Reply

Hey Hintay

ha that's a great catch! Thanks for the feedback, we will investigate this questions marks! )))

Cheers!

Reply
infete Avatar

HI

Thanks for nice tutorial. I got two questions:
1. Can I add multiple read and write groups?
2. How can I differentiate between multiple read and write groups, while making API call?

TIA

Reply

Hey infete

1) Yes, you can add as many groups as you want to your API resources

2) I don't fully get your question. What you want to achieve?

Cheers!

Reply

Hello,

I think MolloKhan is talking about conditional API response.
ie "Is there a way to ask for a specific normalization/denormalization context or group while consuming the API?"

use case:
User A can GET Title and price (cheese_listing:read_limited)
User B can GET Title, price and description (cheese_listing:read)

Reply

Hey JulienG

In that case what you are looking for is "voters". In this chapter you can see a good example of how to implement them with ApiPlatform https://symfonycasts.com/sc...
I also recommend watching this chapter (and a few more after it) https://symfonycasts.com/sc...

Cheers!

Reply

I figured this out. I noticed in the video that the groups annotation can be set above the getter methods so that fixed my issue. I move all group tags to getter methods.

I have a situation with the groups annotation is not showing all of the properties that I have labeled with groups. @Groups({"pharmacies_list:read"})

{
"@context": "string",
"@id": "string",
"@type": "string",
"ncpdp": "string",
"npi": "string",
"city": "string",
"state": "string",
"zipcode": "string"
}

These are what I am getting back but business, address, and phone number is missing. How can I figure out what I wrong when there are no error message in the logs
Thought I would throw this in for good measure.

* @ApiResource(
* collectionOperations={"get"},
* itemOperations={
* "get"
* },
* normalizationContext={
* "groups"={"pharmacies_list:read"}
*
* },
* shortName="pharmacies"
* )

I noticed that the properties that were skipped have underscores in them. How do I fix this?

Reply

Hey sherwingaddis

> I noticed that the properties that were skipped have underscores in them. How do I fix this?

What you mean with that? I would need to see your code to get a better idea of what's going on (at least just that entity)

Cheers!

Reply

MolloKhan i have the same as Sherwin expierences. For example if a property in my Entity is named: "$elapsed_time" (notice the underscore) it's not showing when it's assigned to a serialization group. And for example the property $distance (without underscore) is showing up.


Reply

Hey lexhartman

Seems like it's related to this problem https://github.com/api-plat...
What you can do is to play with the @SerializedName() annotation to make it match to your property getter/setter

From the docs: https://symfony.com/doc/cur... - just scroll down a bit


namespace App\Entity;

use Symfony\Component\Serializer\Annotation\SerializedName;

class Person
{
/**
* @SerializedName("customer_name")
*/
private $firstName;

public function __construct($firstName)
{
$this->firstName = $firstName;
}

// ...
}

Cheers!

1 Reply

If you're interested in the mentioned auto-validation, make sure your `config/packages/validator.yaml` file has `auto_mapping` enabled:


framework:
validation:
auto_mapping:
App\Entity\: []
Reply

Hey JBManNY,

Thank you for this tip! Yes, it's still disabled by default in the recipe: https://github.com/symfony/...

Cheers!

Reply
Eric Avatar

I've noticed the documentation (https://api-platform.com/do... indicates that when serialization groups are specified at an operation level they should take precedence over the configuration specified at the resource level. This makes sense and seems to work fine when your entity does not extend from another class.

Example of what works: Notice that I have resource level normalizationContext and denormalizationContext defined per the tutorial. But I modified the POST operation to have serialization group of "create" and have applied that group to the $description property. This works as expected and the Swagger docs generate everything correctly (model section has a "cheeses-create" definition and the $description property is defined).


namespace App\Entity;

use ApiPlatform\Core\Annotation\ApiFilter;
use ApiPlatform\Core\Annotation\ApiResource;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\BooleanFilter;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\SearchFilter;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\RangeFilter;
use ApiPlatform\Core\Serializer\Filter\PropertyFilter;
use Symfony\Component\Validator\Constraints as Assert;
use Carbon\Carbon;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Serializer\Annotation\SerializedName;

/**
* @ApiResource(
* collectionOperations={
* "get",
* "post"={
* "normalization_context"={"groups"={"create"}},
* "denormalization_context"={"groups"={"create"}},
* }
* },
* itemOperations={
* "get",
* "put"
* },
* normalizationContext={"groups"={"cheese_listing:read"}, "swagger_definition_name"="Read"},
* denormalizationContext={"groups"={"cheese_listing:write"}, "swagger_definition_name"="Write"},
* shortName="cheeses",
* attributes={
* "pagination_items_per_page"=2,
* "formats"={"jsonld", "json", "html", "jsonhal", "csv"={"text/csv"}}
* }
* )
* @ApiFilter(BooleanFilter::class, properties={"isPublished"})
* @ApiFilter(SearchFilter::class, properties={"title": "partial", "description": "partial"})
* @ApiFilter(RangeFilter::class, properties={"price"})
* @ApiFilter(PropertyFilter::class)
* @ORM\Entity(repositoryClass="App\Repository\CheeseListingRepository")
*/
class CheeseListing extends BaseEntity
{
/**
* @ORM\Id()
* @ORM\GeneratedValue()
* @ORM\Column(type="integer")
*/
private $id;

/**
* @Assert\NotBlank()
* @Assert\Length(
* min=2,
* max=50,
* maxMessage="Describe your cheese in 50 chars or less"
* )
* @Groups({"cheese_listing:read", "cheese_listing:write"})
* @ORM\Column(type="string", length=255)
*/
private $title;

/**
* @Assert\NotBlank()
* @Groups({"cheese_listing:read", "create"})
* @ORM\Column(type="text")
*/
private $description;

But, if I have my CheeseListing extend BaseEntity and the BaseEntity class contains a property that has a serialization group of "create", it does not show up if I keep my CheeseListing as defined above. BUT if I remove the resource level normalizationContext and denormalizationContext lines it shows up then... so not sure why it is completely being ignored.

The 2 lines I needed to remove to get the "create" serialization group to show up for the POST operation. But... with removing those, I now lose my Read and Write definitions and groups.


* normalizationContext={"groups"={"cheese_listing:read"}, "swagger_definition_name"="Read"},
* denormalizationContext={"groups"={"cheese_listing:write"}, "swagger_definition_name"="Write"},

My BaseEntity.php file:


namespace App\Entity;

use Symfony\Component\Serializer\Annotation\Groups;

class BaseEntity
{
/**
* @Groups({"create"})
*/
protected $someProperty;

public function getSomeProperty()
{
return $this->someProperty;
}

public function setSomeProperty($someProperty): void
{
$this->someProperty = $someProperty;
}
}

Any idea why I need to remove the resource level normalization and denormalization contexts to get groups in parent classes to show up?

Reply
Cat in space

"Houston: no signs of life"
Start the conversation!

This tutorial works great for Symfony 5 and API Platform 2.5/2.6.

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": "^7.1.3",
        "ext-ctype": "*",
        "ext-iconv": "*",
        "api-platform/core": "^2.1", // v2.4.3
        "composer/package-versions-deprecated": "^1.11", // 1.11.99
        "doctrine/annotations": "^1.0", // 1.10.2
        "doctrine/doctrine-bundle": "^1.6", // 1.11.2
        "doctrine/doctrine-migrations-bundle": "^2.0", // v2.0.0
        "doctrine/orm": "^2.4.5", // v2.7.2
        "nelmio/cors-bundle": "^1.5", // 1.5.5
        "nesbot/carbon": "^2.17", // 2.19.2
        "phpdocumentor/reflection-docblock": "^3.0 || ^4.0", // 4.3.1
        "symfony/asset": "4.2.*|4.3.*|4.4.*", // v4.3.11
        "symfony/console": "4.2.*", // v4.2.12
        "symfony/dotenv": "4.2.*", // v4.2.12
        "symfony/expression-language": "4.2.*|4.3.*|4.4.*", // v4.3.11
        "symfony/flex": "^1.1", // v1.17.6
        "symfony/framework-bundle": "4.2.*", // v4.2.12
        "symfony/security-bundle": "4.2.*|4.3.*", // v4.3.3
        "symfony/twig-bundle": "4.2.*|4.3.*", // v4.2.12
        "symfony/validator": "4.2.*|4.3.*", // v4.3.11
        "symfony/yaml": "4.2.*" // v4.2.12
    },
    "require-dev": {
        "symfony/maker-bundle": "^1.11", // v1.11.6
        "symfony/stopwatch": "4.2.*|4.3.*", // v4.2.9
        "symfony/web-profiler-bundle": "4.2.*|4.3.*" // v4.2.9
    }
}