Lucky you! You found an early release chapter - it will be fully polished and published shortly!
Rest assured, the gnomes are hard at work
completing this video!
Is it possible to create a totally new DragonTreasure
when we create a user?
Like... instead of sending the IRI of an existing treasure, we send an object?
Let's try it! First, I'll change this to a unique email and username. Then, for
dragonTreasures
, clear those IRIs and, instead, pass a JSON object with the
fields that we know are required. Our new dragon user just scored a copy of
GoldenEye for N64! Legendary. Add a description
... and a value
.
In theory, this JSON body makes sense! But does it work? Hit "Execute" and... nope! Well, not yet. But we know this error!
Nested documents for attribute
dragonTreasures
are not allowed. Use IRIs instead.
Inside User
, if we scroll way up, the $dragonTreasures
property is writable
because it has user:write
. But we can't send an object for this property because
we haven't added user:write
to any of the fields inside of DragonTreasure
.
Let's fix that.
We want to be able to send $name
, so add user:write
... I'll skip $description
but do the same for $value
. Now search for setTextDescription()
which is
the actual description. Add user:write
here too.
Okay, in theory, we should now be able to send an embedded object. If we head over and try it again... we upgraded to a 500 error!
A new entity was found through the relationship
User#dragonTreasures
This is great! We already know that when you send an embedded object, if you include
@id
, the serializer will fetch that object first and then update it.
But if you don't have an @id
, it will create a brand new object. Right now,
it is creating a new object,... but nothing told the entity manager to
persist it. That's why we're getting this error.
To solve this, we need to cascade persist this property. In User
, on the
OneToMany
for $dragonTreasures
, add a cascade
option set to ['persist']
.
This means that if we're saving a User
object, it should magically persist
any $dragonTreasures
inside. And if we try it now... it works! That's awesome!
And apparently, our new treasure id
is 43
.
Let's open up a new browser tab and navigate to that URL... plus .json
... actually,
let's do .jsonld
. Beautiful! We see that the owner
is set to the new user that
we just created.
But... hold your horses! We didn't send the owner
field in the treasure
data... so how did that field get set? Well, first, it does make sense that we didn't
send an owner
field for the new DragonTreasure
... since the user that will
own it didn't even exist yet! Ok, then, but who did set the owner
?
Behind the scenes, the serializer creates a new User
object first. Then,
it creates a new DragonTreasure
object. Finally, it sees that the new DragonTreasure
is not assigned to the User
yet, and it calls addDragonTreasure()
. When it
does that, the code down here sets the owner
: just like we saw before. So our
well-written code is taking care of all of those details for us.
Anyways, you might remember from before that as soon as we allow a relation field
to send embedded data... we need to add one tiny thing. I won't do it, but if
we sent an empty name
field, it would create a DragonTreasure
... with an empty
name
, even though, over here, if we scroll up to the name
property, it's required!
Remember: when the system validates the User
object, it will stop at
$dragonTreasures
. It won't also validate those objects. If you do want to
validate them, add #[Assert\Valid]
.
Now that I have this, to prove that it's working, hit "Execute" and... awesome!
We get a 422 status code telling us that name
shouldn't be empty. I'll
go put that back.
We now know that we can send IRI strings or embedded objects for a relation property - assuming we've setup the serialization groups to allow that. And, we can even mix them.
Let's say that we want to create a new DragonTreasure
object, but we're also going
to steal, borrow, a treasure from another dragon. This is totally allowed.
Watch! When we hit "Execute"... we get a 201 status code. This returns treasure
ids 44
(that's the new one) and 7
, which is the one we just stole.
Okay, we only have one more chapter about handling relationships. Let's see how we can remove a treasure from a user to delete that treasure. That's next.
"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
}
}