If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.
With a Subscription, click any sentence in the script to jump to that part of the video!
Login SubscribeEarlier, we added a bunch of nice filters to DragonTreasure
. Let's add a few more - starting with User
- so we can show off some filtering superpowers for relations.
Start like normal: ApiFilter
and let's first use PropertyFilter::class
. Remember: this is kind of a fake filter that allows our API client to select which fields they want. And this is all pretty familiar so far.
... lines 1 - 4 | |
use ApiPlatform\Metadata\ApiFilter; | |
... line 6 | |
use ApiPlatform\Serializer\Filter\PropertyFilter; | |
... lines 8 - 22 | |
PropertyFilter::class) ( | |
... lines 24 - 25 | |
class User implements UserInterface, PasswordAuthenticatedUserInterface | |
{ | |
... lines 28 - 174 | |
} |
When we head over, refresh, and go to the GET
collection endpoint... we see a new properties[]
field. We could choose to return just username
... or username
and dragonTreasures
.
When we hit "Execute"... perfect! We see the two fields... where dragonTreasures
is an array of objects, each containing the fields we chose to embedded.
Again, this is super duper normal. So let's try something more interesting. In fact, what we're going to try isn't supported directly in the interactive docs.
So, copy this URL... paste and add .jsonld
to the end.
Here's the goal: I want to return the username
field and then only the name
field of each dragon treasure. The syntax is a bit ugly: it's [dragonTreasures]
, followed by []=name
.
And just like that... it only shows name
! So right out of the box, PropertyFilter
allows us to reach across relationships.
Let's do something else. Head back to DragonTreasure
. It might be handy to filter by the $owner
: we could quickly get a list of all treasures for a specific user.
No sweat! Just add ApiFilter
above the $owner
property, passing in the trusty SearchFilter::class
followed by strategy: 'exact'
.
... lines 1 - 55 | |
class DragonTreasure | |
{ | |
... lines 58 - 101 | |
SearchFilter::class, strategy: 'exact') ( | |
private ?User $owner = null; | |
... lines 104 - 215 | |
} |
Back over on the docs, if we open up the GET
collection endpoint for treasures and give it a whirl... let's see... here we go - "owner". Enter something like /api/users/4
... assuming that's actually a real user in our database, and... yes! Here are the five treasures owned by that user!
But I want to get crazier: I want to find all treasures that are owned by a user matching a specific username. So instead of filtering on owner
, we need to filter on owner.username
.
How? Well, when we want to filter simply by owner
, we can put the ApiFilter
right above that property. But since we want to filter on owner.username
, we can't put that above a property... because owner.username
isn't a property. This is one of the cases where we need to put the filter above the class. And... that also means we need to add a properties
option set to an array. Inside, say 'owner.username'
and set that to the partial
strategy.
... lines 1 - 55 | |
SearchFilter::class, properties: [ ( | |
'owner.username' => 'partial', | |
]) | |
class DragonTreasure | |
{ | |
... lines 61 - 218 | |
} |
Ok! Head back over and refresh. We know we have an owner whose username is "Smaug"... so let's go back to the GET
collection endpoint and... here in owner.username
, search for "maug"... and hit "Execute".
Let's see... That worked! This shows all treasures owned by any user whose username contains maug
. Pretty cool!
Ok squad: get ready for the grand finale - Subresources. These have seriously changed in API Platform 3. Let's dive into them next.
Hey @David-S
I think that's unexpected. Have you watched this chapter? You may find it helpful https://symfonycasts.com/screencast/api-platform-extending/uuid-identifier
By the way, I don't recommend using UUIDs as the primary key because it will slow down your queries. It's better to keep a traditional integer id as the primary key and add a UUID as your "public id"
Cheers!
Hey @MolloKhan,
thanks for your reply. I wasn't sure that "slowing down" is a real world problem (at least for my application).
So, to keep it simple, I wanted to use the uuid instead of integer id. But Ok, I will use int as primary and add uuids as identifiers.
I used Symfony\Component\Uid\Uuid
. In that Symfony 5 API Tutorial Ramsey\Uuid\Uuid
is used. Which one shpuld be uses now with Symfony 6 and API Platform 3?
Good question. Since Symfony 5.2 there's a UID component that basically replaces Ramsey's library. You can read more about it here https://symfony.com/doc/current/components/uid.html#storing-uuids-in-databases
Cheers!
Hi everyone,
I am working on an application with API Platform 3. I have an Entity called "Users" and another Entity called "Prospects". I have created a route: /api/users/{partner_id}/prospects/{id}, where {partner_id} represents the custom id of users and {id} represents the id of prospects.
This setup is functioning correctly, but I want to apply a filter on the subresource "prospects," and I am having trouble understanding how to do this. I have encountered some issues, but I haven't found a solution yet.
Here is a portion of my code:
#[ApiResource(
operations: [
new Get(
shortName: "Users",
uriTemplate: '/users/{partner_id}/prospects/{id}',
uriVariables: [
'partner_id' => new Link(fromClass: Users::class, fromProperty: 'prospects'),
'id' => new Link(fromClass: Prospects::class)
],
)
],
)]
#[ApiFilter(SearchFilter::class, properties:[
'status',
'lastname' => 'ipartial',
'upline'
])]
Please let me know if you have any suggestions or solutions. Thank you!
Hey @David-G!
This is not a use-case I had thought of before! You said this isn't working. What does it do? An error? Just not work at all?
Cheers!
Hi,
The bug was between the chair and the screen because I didn't realize that I was trying to apply a filter on a Get operation when filters only work on GetCollection operations.
Thanks !
// 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
}
}
Hi everyone,
so I have an interesting error. Instead of integer ids I use uuids (as primary key).
When searching "DreagonTreasures" with a specific user e.g. /api/users/0189d986-1e5c-7205-8e1c-466b139ceda1
I get a 200 response with
Here is what id looks like in my class:
I'm using MySQL. The id (with uuid value) apparently is of type binary(16) in MySQL. Does it have something to do with that?
What am I missing / how can I fix this?
(When I use integer ids it works as it should.)
Thanks for your help.