gstreamer0.10-ffmpeg
gstreamer0.10-plugins-good
packages.
API Platform works by taking a class like DragonTreasure
and saying that you want to expose it as a resource in your API. We do that by adding the ApiResource
attribute:
... lines 1 - 10 | |
( | |
description: 'A rare and valuable treasure.' | |
) | |
class DragonTreasure | |
{ | |
... lines 16 - 117 | |
} |
Right now, we're putting this above a Doctrine entity, though, in a future tutorial, we'll learn that you can really put ApiResource
above any class.
Out-of-the-box, every ApiResource
includes 6 endpoints, which API Platform calls operations. You can actually see these in the profiler. This is the profiler for GET /api/dragon_treasures.json
. Click on the "API Platform" section. On top, we see metadata for this API resource. Below, we see the operations. This... is more info than we need right now, but there's Get
, GetCollection
, Post
, Put
, Patch
and finally Delete
. These are the same things we see on the Swagger documentation.
Let's take a quick look at these. First, which operations return data? Actually, all of them - except for Delete
. This Get
, the Post
, Put
and Patch
endpoints all return a single resource - so a single treasure. And GET /api/dragon_treasures
returns a collection.
Which endpoints do we send data to when we use them? That's POST
to create, and PUT
and PATCH
to update. We don't send any data for DELETE
or either GET
operation.
Most of the endpoints are pretty self-explanatory: get a collection of treasures, a single treasure, create a treasure and delete a treasure. The only confusing ones are put versus patch. PUT
says "replaces" and PATCH
says "updates". That... sounds like two ways of saying the same thing!
Tip
In API Platform 4, PUT
will become a "replace": meaning if you only sent a
single field, all of the other fields in your resource will be set to null: your
object is completely "replaced" by the JSON you send. Starting in API Platform
3.1, you can "opt into" this new behavior by adding an extraProperties
option
to every ApiResource
:
#[ApiResource(
// ...
extraProperties: [
'standard_put' => true,
],
)]#
The topic of PUT versus PATCH in APIs can get spicy. But in API Platform, at least today, PUT and PATCH work the same: they're both used to update a resource. And we'll see them in action along the way.
One of the things that you might want to do is customize or remove some of these operations... or even add more operations. How could we do that? As we saw on the profiler, each operation is backed by a class.
Back over above the DragonTreasure
class, after description
, add an operations
key. Notice that I'm getting auto-completion for the options because these are named arguments to the constructor of the ApiResource
class. I'll show you that in a minute.
Set this to an array and then repeat every operation we currently have. So, new Get()
, hit tab to auto-complete that, GetCollection
, Post
, Put
, Patch
and Delete
.
... lines 1 - 5 | |
use ApiPlatform\Metadata\Delete; | |
use ApiPlatform\Metadata\Get; | |
use ApiPlatform\Metadata\GetCollection; | |
use ApiPlatform\Metadata\Patch; | |
use ApiPlatform\Metadata\Post; | |
use ApiPlatform\Metadata\Put; | |
... lines 12 - 16 | |
( | |
description: 'A rare and valuable treasure.', | |
operations: [ | |
new Get(), | |
new GetCollection(), | |
new Post(), | |
new Put(), | |
new Patch(), | |
new Delete(), | |
] | |
) | |
class DragonTreasure | |
{ | |
... lines 30 - 131 | |
} |
Now, if we move over to the Swagger documentation and refresh... absolutely nothing changes! That's what we wanted. We've just repeated exactly the default configuration. But now we're free to customize things. For example, suppose we don't want treasures to be deleted... because a dragon would never allow their treasure to be stolen. Remove Delete
.. and I'll even remove the use
statement.
... lines 1 - 5 | |
use ApiPlatform\Metadata\Get; | |
use ApiPlatform\Metadata\GetCollection; | |
use ApiPlatform\Metadata\Patch; | |
use ApiPlatform\Metadata\Post; | |
use ApiPlatform\Metadata\Put; | |
... lines 11 - 15 | |
( | |
description: 'A rare and valuable treasure.', | |
operations: [ | |
new Get(), | |
new GetCollection(), | |
new Post(), | |
new Put(), | |
new Patch(), | |
] | |
) | |
class DragonTreasure | |
{ | |
... lines 28 - 129 | |
} |
Now when we refresh, the DELETE
operation is gone.
Ok, so every attribute we use is actually a class. And knowing that is powerful. Hold command or control and click on ApiResource
to open it. This is really cool. Every argument to the constructor is an option that we can pass to the attribute. And almost all of these have a link to the documentation where you can read more. We'll talk about the most important items, but this is a great resource to know about.
One argument is called shortName
. If you look over at Swagger, our "model" is currently known as DragonTreasure
, which obviously matches the class. This is called the "short name". And by default, the URLs - /api/dragon_treasures
- are generated from that.
Let's say that we instead want to shorten all of this to just "treasure". No problem: set shortName
to Treasure
.
... lines 1 - 15 | |
( | |
shortName: 'Treasure', | |
... lines 18 - 25 | |
) | |
class DragonTreasure | |
{ | |
... lines 29 - 130 | |
} |
As soon as we do that, watch the name and URLs. Nice. This resource is now known as "Treasure" and the URLs updated to reflect that.
Though, that's not the only way to configure the URLs. Just like with ApiResource
, each operation is also a class. Hold Command (or Ctrl) and Click to open up the Get
class. Once again, these constructor arguments are options... and most have documentation.
One important argument is uriTemplate
. Yup, we can control what the URL looks like on an operation by operation basis.
Check it out. Remember, Get
is how you fetch a single resource. Add uriTemplate
set to /dragon-plunder/{id}
where that last part will be the placeholder for the dynamic id. For GetCollection
, let's also pass uriTemplate
set to /dragon-plunder
.
... lines 1 - 15 | |
( | |
... lines 17 - 18 | |
operations: [ | |
new Get(uriTemplate: '/dragon-plunder/{id}'), | |
new GetCollection(uriTemplate: '/dragon-plunder'), | |
... lines 22 - 24 | |
] | |
) | |
class DragonTreasure | |
{ | |
... lines 29 - 130 | |
} |
Ok! Let's go check the docs! Beautiful! The other operations keep the old URL, but those use the new style. Later, when we talk about subresources, we'll go deeper into uriTemplate
and its sister option uriVariables
.
Ok... since it's a bit silly to have two operations with weird URLs, let's remove that customization.
... lines 1 - 15 | |
( | |
... lines 17 - 18 | |
operations: [ | |
new Get(), | |
new GetCollection(), | |
... lines 22 - 24 | |
] | |
) | |
class DragonTreasure | |
{ | |
... lines 29 - 130 | |
} |
Now that we know a bunch about ApiResource
and these operations, it's time to talk about the heart of API Platform: Symfony's serializer. That's next.
Hey Stiff!
Hmm, well that's no fun :/. First, are you sure that the 405 Method not allowed is coming from Nginx? I mean, it probably is, but it could also, in theory, be coming from something like CloudFlare (if it is, it should be pretty obvious on that page) or Symfony itself (if it is, you would likely be seeing either your custom exception page or the generic "Symfony error page" for production).
Assuming that it IS coming from nginx, I'm familiar with Fastpanel or how much control it gives you over the raw nginx config. What have you tried so far?
Cheers!
// 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 Ryan,
I am unable to execute PUT/PATCH operations on my server (Error 405 Method not allowed). I am using Fastpanel where Nginx is running for the frontend. Tried some fixes from the internet but that didn't help. Any help would be really appreciated.
Best regards,
Stiff