Triggering a "Publish"
…We've
completely converted this complex API resource to our DTO-powered system!
High five!
Next: Let's make it possible to write the $owner property on dragon treasure.
This involves a trick that will help us better understand how API Platform
loads relation data.
DTO & Security
…
And the full flow here is fascinating! When we make a patch() request
to a treasure, API Platform starts by using our data provider to find the
DragonTreasure entity. Then we map that to a DragonTreasureApi object. Next,
the new value is deserialized onto that…
DTO -> Entity State Processor
…default processor when using stateOptions. But... because our
UserApi isn't an entity, PersistProcessor does nothing. Finally,
API Platform serializes the UserApi back into JSON... but without the id
populated, it fails to generate the IRI.
Watch! Over in UserApi, temporarily default $id to 5…
Custom Resource Item Provider
…2013"... we get a 404. API Platform sees that we returned
null and it handled the 404 for us.
We are now the proud parents of a fully functional state provider! Though
we'll talk about this more soon - including topics like pagination. But next…
Normalizer Decoration & "Normalizer Aware"
…for normalizing ApiResource objects into
JSON-LD. This is another spot where you can rely on the documentation to give you
the exact service ID you need. It's api_platform.jsonld.normalizer.item:
Try the test again: testOwnerCanSeeIsPublishedField
Yes! We see our dump! And..…
Auto Setting the "owner"
…to make Symfony use our state processor instead of the normal one from
Doctrine, add #[AsDecorator]... and the id of the service is
api_platform.doctrine.orm.state.persist_processor:
Cool! Now, everything that uses that service in the system will be passed our
service…
Dynamic Groups: Context Builder
…honestly, just to learn more about how
API Platform works under the hood - I want to show you an alternative solution.
Remove the ApiProperty attribute:
And replace it with two new groups. We're not going to use the normal
treasure:read and treasure:write…
Input DTO: Denormalizing IRI Strings
…data into that type - like creating a DateTime object from a date string.
There are various normalizers that are good at doing this.
And... fortunately API Platform comes with an ItemNormalizer whose job is to
change IRI strings into objects by querying the database... or..…
Custom Filter apply()
…implement the
FilterInterface from the core Serializer\ namespace, API Platform will help
us a bit:
How? By automatically calling our apply() method on every request for an
API resource where our filter has been activated:
What I mean is, in DailyStats we added @ApiFilter(DailyStatsDateFilter:…
UUID's
…Ramsey\ - then call uuid4(), which is how you get a random UUID string:
Run the tests again:
Now they're happy!
Next, the UUID is not part of our API at all yet. Let's tell API Platform
to start using it as the identifier.
Input DTO Validation
…To do that, we need the validator! Add a public function
__construct() with a ValidatorInterface argument. But grab the one
from ApiPlatform\, not Symfony\. I'll explain why in a second. Call that
argument $validator and then I'll go to Alt+Enter and select…
Input Data Transformer
…A 201 status code. It worked!
So using a DTO input is a 3-step process. First, API Platform deserializes
the JSON we send into a CheeseListingInput object. Second, we transform that
CheeseListingInput into a CheeseListing in the data transformer. And
third, the normal Doctrine…
Filter Class Arguments
…Ah, error!
Class DailyStatsDateFilter does not have argument $throwOnInvalid
API platform does a cool, but kind of strange thing: if you pass an arguments
option to a filter, it tries to pass that argument - by name - to the
constructor of your filter.
Check it out…
Pagination Context
…query parameter that's used for pagination
can be changed - it doesn't need to be ?page=.
My point is: we don't need to hardcode the max per page or read the page query
parameter directly because API Platform already has this info! Where…
Property Metadata
…types of each field. Are these strings? Integers?
Aliens?
API Platform gets metadata about each property from many different places, like
by reading Doctrine metadata, PHPDoc, looking at the return types of getter methods,
looking at the argument type-hint on setters, PHP 7.4…
Simpler State Processor
…Step 3 is to tell API Platform when to use this processor. In DragonTreasure, we
want this to be used for both our Post and Patch operations. Set
processor to DragonTreasureStateProcessor::class... and repeat that down for
Patch.
Done! API Platform will call our processor…
Calling the Controller & View Event
…model". Then, you could write a listener
to this event that transforms that Article entity into HTML by rendering the template.
This event isn't used anywhere in Symfony's core, but it is used extensively
inside API Platform. Internally, their controllers return objects. Then…
Setting the UUID on POST
…
Remember: API platform - well really, Symfony's serializer - is really good at
reading your types. It notices that the type for $uuid is UuidInterface
and uses that to try to find a denormalizer that understands this type. And
fortunately, API Platform comes with a denormalizer…
Automatic 404 on Unpublished Items
…individual CheeseListing if it's unpublished.
The collection query extension does not take care of this: this method is only
called when API Platform needs to query for a collection of items - a different
query is used for a single item.
Let's write a…
Dynamic Groups without Caching
…And... that's fine! Symfony handles
all of that behind the scenes.
In the case of the resource metadata factory, API Platform itself decorates
that service multiple times... each "layer" adding a bit more functionality.
Normally, when we decorate a service from our application, our…
x
1000+