gstreamer0.10-ffmpeg
gstreamer0.10-plugins-good
packages.
I've just used the GET
collection endpoint to fetch all of my resources... which shows that we have a treasure with id=1
. I'll close up this operation... and use this other GET
endpoint. Click "Try it out", put "1" in for the ID, and click "Execute".
Beautiful! But... I have some questions. Specifically: what is the significance of these fields? What do name
or description
or value
actually mean? Is the description plain text? HTML? Is name
a short name for the item or a proper name? Is this value in dollars? Euros? French fries? What the heck is coolFactor
? And why am I asking you all of these unfair questions?
If you're a human (you are... right?), then you can probably figure out a lot of the "meaning" of these fields on your own. But machines - okay, maybe minus futuristic AIs - well, they can't figure this out. They don't know what these keys mean. So... how can we give context and meaning to our data?
First, there's this thing called "RDF" or "Resource Description Framework", which is a set of rules about how we describe the meaning of data so that computers can understand. It's... boring and abstract, but basically a guide on how you can define that one piece of data has a certain type, or one resource is a subclass of some other type. In HTML, you can add attributes to your elements to add this RDF metadata. You could say that this <div>
describes a "person", and that this person's name and telephone are these other pieces of data. This makes the random HTML in your site understandable by machines. It's even better if two different sites use the exact same definition of "person", which is why the types are URLs... and sites try to reuse existing types rather than invent new ones.
Why are we talking about this? Because JSON-LD attempts to do the same thing for our API. Our API endpoints are returning JSON. But the content-type
header in the response says that this is application/ld+json
.
When you see application/ld+json
, it means that the data is JSON... but with extra fields that have special meaning according to a giant JSON-LD spec document. So, quite literally, JSON-LD is JSON... with extra goodies.
For example, every resource, like DragonTreasure
, has three @
fields. The most important is probably @id
. This is the unique identifier to the resource. It's basically the same as id
, but it's even better because it's a URL. So instead of just saying "id": 1
, you have @id
/api/dragon_treasures/1
. That means that, first, the string will be unique across all of our API resource classes and second, this URL is handy! You can pop this into your browser, and, if you have the accept
header or add .jsonld
to the end... whoops... let me get rid of my extra /
... yeah! You can see that resource. So @id
is just like id
... but better.
Another special field is @type
. This describes the type of resource, like what fields it has. And if we see two different resources that both have @type
DragonTreasure
, we know that they represent the same thing.
You can think of @type
almost like a class, which we can use to find out what fields it has and the type of each field. Though... where can we actually see that info?
This is where @context
comes in handy. Copy the context URL, paste it into your browser, and... beautiful! We get this very simple document that says that DragonTreasure
has name
, description
, value
, coolFactor
, createdAt
, and isPublished
fields. If we want even more information about what those mean, we can follow the @vocab
link... to get to another page of info.
Here, we can see all the classes in our API - like DragonTreasure
- and all of its properties, like name
. We can also see things like required: false
, readable: true
, writeable:
true and also that it's a string
. And we have this info for every field. Look: down at value
. We can see that this is an integer
. This xmls:integer
refers to another document, up on top, which, if we followed it, would describe xmls:integer
in more detail.
At this point, you might be saying:
Hey! This seems a lot like the OpenAPI spec doc!
And you're right. We'll talk more about that in a few minutes.
You also might be thinking:
Um... I kind of get what you're saying... but this is confusing.
And you would also be right! It's hard, as a mere human, to follow all of these links to find the fields and their types. But imagine what this would look like to a machine. It's an information gold mine!
Oh, and I want to mention that, if you look under value
... hydra:description
... it picked up the PHP documentation that we added to that field earlier.
We can also add extra information above the class to describe this model. We could do this via PHP documentation like normal, but ApiResource
also has some options we can pass. One is description
. Let's describe this as A rare and valuable treasure.
... lines 1 - 10 | |
( | |
description: 'A rare and valuable treasure.' | |
) | |
class DragonTreasure | |
{ | |
... lines 16 - 117 | |
} |
Now, when we refresh the page... and search for "rare" (I'll close a few things here...), yup! It added the description to the DragonTreasure
type. And, not surprisingly, this data also shows up over here inside Swagger, because it was also added to the OpenAPI spec doc.
The point is, thanks to JSON-LD, we have extra fields in every response that give each resource a unique id and a way to discover exactly what that "type" looks like.
Next: we need to discuss one last piece of theory: what these hydra
things mean.
"Houston: no signs of life"
Start the conversation!
// 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
}
}