Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

Relaciones e IRI

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $12.00

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

Cuando intentamos crear un DragonTreasure con este owner, establecimos el campo con el id de la base de datos del propietario. Y nos dimos cuenta de que a API Platform no le gustaba eso. Decía "IRI esperado". Pero, ¿qué es un IRI?

Ya mencionamos este término una vez en el tutorial. Vuelve al punto final de la colección GET /api/users. Sabemos que cada recurso tiene un campo @id establecido en la URL donde puedes obtener ese recurso. Esto es el IRI o "Identificador Internacional de Recursos". Está pensado para ser un identificador único en toda tu API, como en todos los recursos.

Piénsalo: el número "1" no es un identificador único -podríamos tener unDragonTreasure con ese id y un User. Pero el IRI es único. Y, además, una URL es mucho más manejable que un número entero.

Así que cuando queramos establecer una propiedad de relación, tendremos que utilizar también el IRI, como/api/users/1.

Cuando pulsamos Ejecutar, ¡funciona! Un código de estado 201. En el JSON devuelto, no es de extrañar, el campo owner también aparece como un IRI.

Las conclusiones de todo esto son deliciosamente sencillas. Las relaciones son campos normales... pero las obtenemos y establecemos a través de su cadena IRI. Es una forma muy bonita y limpia de manejarlo.

Añadir un campo de relación Colección dragonTreasures

Bien, hablemos del otro lado de esta relación. Actualiza toda la página y ve a la ruta GET one user endpoint. Inténtalo con un identificador de usuario real, como el 1 para mí. Y... ahí están los datos.

Así que la pregunta que me hago ahora es: ¿podríamos añadir un campo dragonTreasures que muestre todos los tesoros que posee este usuario?

Bueno, vamos a pensarlo. Sabemos que el serializador funciona cogiendo todas las propiedades accesibles de un objeto que están en el grupo de normalización. Y... tenemos una propiedad dragonTreasures en User.

... lines 1 - 22
class User implements UserInterface, PasswordAuthenticatedUserInterface
{
... lines 25 - 50
#[ORM\OneToMany(mappedBy: 'owner', targetEntity: DragonTreasure::class)]
private Collection $dragonTreasures;
... lines 53 - 169
}

Así que... ¡debería funcionar! Para exponer el campo a la API, añádelo al grupo de serialización user:read. Más adelante, hablaremos de cómo podemos escribir en un campo de colección... pero por ahora, basta con hacerlo legible.

... lines 1 - 22
class User implements UserInterface, PasswordAuthenticatedUserInterface
{
... lines 25 - 50
#[ORM\OneToMany(mappedBy: 'owner', targetEntity: DragonTreasure::class)]
#[Groups(['user:read'])]
private Collection $dragonTreasures;
... lines 54 - 170
}

Vale Actualiza... y mira la misma ruta GET. Aquí abajo, ¡genial! Muestra un nuevo campo dragonTreasures en la respuesta del ejemplo. Vamos a probarlo: utiliza el mismo id, pulsa "Ejecutar" y... ¡oh, estupendo: devuelve una matriz de cadenas IRI! ¡Me encanta! Y, por supuesto, si necesitamos más información sobre ellas, podemos hacer una petición a cualquiera de estas URL para obtener todos los detalles brillantes.

Y para ser realmente extravagante, podrías utilizar Vulcain para que los usuarios puedan "precargar" esas relaciones... lo que significa que el servidor enviará los datos directamente al cliente.

Pero aunque esto es genial, me lleva a una pregunta: ¿y si necesitar los datos deDragonTreasure para un usuario es tan habitual que, para evitar peticiones adicionales, queremos incrustar los datos aquí mismo, como objetos JSON en lugar de cadenas IRI?

¿Podemos hacerlo? Por supuesto que sí. Averigüemos cómo a continuación.

Leave a comment!

2
Login or Register to join the conversation
Szymon Avatar

No subtitles on video? :(

Reply

Hey Szymon,

Sorry for the delay on the subtitles but there they are now :)

Cheers!

Reply
Cat in space

"Houston: no signs of life"
Start the conversation!

What PHP libraries does this tutorial use?

// 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
    }
}
userVoice