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 SubscribeNuestro flamante usuario es el orgulloso propietario de dos tesoros con los identificadores 7
y 44
. Actualicemos este usuario para ver si podemos hacer algunos cambios en $dragonTreasures
. Utiliza la ruta PUT
, haz clic en "Probar" y... veamos... el id
que necesitamos es 14
... así que lo introduciré. También eliminaré todos los campos exceptodragonTreasures
para que podamos centrarnos.
Sabemos que actualmente tiene dos tesoros deslumbrantes: /api/treasures/7
y/api/treasures/44
. Así que si enviamos esta petición, en teoría, eso no debería hacer... ¡nada! Y si miramos aquí abajo... sí: no ha hecho ningún cambio.
Supongamos que queremos añadir un nuevo DragonTreasure
a este recurso. Para ello, listamos los dos que ya tiene, junto con /api/treasures/8
. Estoy totalmente seguro de que es un id
válido. Cuando pulsamos "Ejecutar"... funciona de maravilla. El sistema serializador se dio cuenta de que ya tenía estos dos primeros, así que no hizo nada con ellos. Sólo añadió el nuevo con id 8
.
Eso está muy bien, pero de lo que realmente quiero hablar es de eliminar un tesoro. Supongamos que nuestro dragón se dejó uno de estos tesoros en el bolsillo del pantalón y lo lavó accidentalmente en la lavandería. No puedo culparles. Yo siempre pierdo mi bálsamo labial ahí. Como ahora el tesoro está empapado y no sirve para nada, tenemos que eliminarlo de la lista de tesoros. No hay problema Mencionaremos los dos que aún tiene nuestro dragón y eliminaremos el otro. Cuando pulsamos "Ejecutar"... ¡estalla!
Se ha producido una excepción al ejecutar una consulta: [...] Violación no nula: 7. valor nulo en la columna "owner_id"
¿Qué ha ocurrido? Bueno, nuestra aplicación estableció la propiedad $owner
para el DragonTreasure
que acabamos de eliminar en null
... y ahora está intentando guardarlo. Pero como la tenemos establecida ennullable: false
, está fallando.
... lines 1 - 55 | |
class DragonTreasure | |
{ | |
... lines 58 - 97 | |
#[ORM\ManyToOne(inversedBy: 'dragonTreasures')] | |
#[ORM\JoinColumn(nullable: false)] | |
... lines 100 - 101 | |
private ?User $owner = null; | |
... lines 103 - 214 | |
} |
Pero... demos un paso atrás y veamos el cuadro completo. Primero, el serializador se dio cuenta de que los tesoros 7
y 8
ya pertenecían a User
... así que no hizo nada con ellos. Pero entonces se dio cuenta de que el tesoro con id 44 -que pertenecía a este User
- ¡ha desaparecido!
Por eso, en nuestra clase User
, el serializador llamó aremoveDragonTreasure()
. Lo realmente importante es que toma eseDragonTreasure
y establece el owner
en null
para romper la relación. Dependiendo de tu aplicación, puede que eso sea exactamente lo que quieres. Tal vez permitas quedragonTreasures
no tenga owner
... como si... estuvieran aún sin descubrir y esperando a que un dragón los encuentre. Si ése es el caso, sólo querrás asegurarte de que tu relación permite null
... y todo se salvará sin problemas.
Pero en nuestro caso, si un DragonTreasure
ya no tiene un owner
, queremos borrarlo por completo. Podemos hacerlo en User
... muy arriba en la propiedad dragonTreasures
. Después de cascade
, añade una opción más aquí: orphanRemoval: true
.
... lines 1 - 22 | |
class User implements UserInterface, PasswordAuthenticatedUserInterface | |
{ | |
... lines 25 - 50 | |
#[ORM\OneToMany(mappedBy: 'owner', targetEntity: DragonTreasure::class, cascade: ['persist'], orphanRemoval: true)] | |
... lines 52 - 53 | |
private Collection $dragonTreasures; | |
... lines 55 - 171 | |
} |
Esto le dice a Doctrine que si alguno de estos dragonTreasures
queda "huérfano" -lo que significa que ya no tiene propietario- debe ser eliminado.
Vamos a probarlo. Cuando volvamos a pulsar "Ejecutar"... ¡ya está! Se guarda sin problemas.
Siguiente paso: Volvamos a los filtros y veamos cómo podemos utilizarlos para buscar en recursos relacionados.
Hey Julien!
Sorry for the slow reply - we had a hiccup in our notification system!
I'm a bit confused, my user put doesn't have a list of IRIs but a list of objects. I don't know if I missed something.
Just to be totally clear, when I make a PUT
request, for the dragonTreasures
property, I am:
A) SENDING an array of IRI strings
B) RECEIVING an array of objects
Let's look at each side more deeply (probably some of this you already understand, but just to be safe):
A) Since we're writing to User
, the user:write
group is being used. DragonTreasure
DOES have user:write
above name
and value
. So, should we be sending an object instead of IRI strings? The answer is that, because those 2 fields have user:write
, we have the OPTION to send an "object" to the dragonTreasures
property, but we're not forced to. We showed that off in an earlier chapter where we even sent a mixture or objects and IRI strings: https://symfonycasts.com/screencast/api-platform/collections-create#sending-embedded-objects-and-iri-strings-at-the-same-time
B) We are using the PUT
operation. But at this point, we're now talking about what data to RETURN. So that is "normalization". And so, the group that's used here is user:read
. Once again, inside DragonTreasure
, both name
and value
have this group. And so, the response contains an array of objects for dragonTreasures
.
Let me know if that helps :)
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
}
}
Hey there,
I'm a bit confused, my user put doesn't have a list of IRIs but a list of objects. I don't know if I missed something.
Looking at your DragonTreasure.php file, I feel like you should too.
Anyway, I removed the user:write group from my DragonTreasure's properties, but this is confusing.
Cheers