Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

Nuevo comportamiento PUT

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

Busca tu terminal y borra manualmente el directorio de caché:

rm -rf var/cache/*

Lo hago para que, cuando ejecutemos todas nuestras pruebas

symfony php bin/phpunit

veamos una advertencia de desaprobación, que es fascinante. Dice

Desde API Platform 3.1: en API Platform 4, PUT siempre sustituirá los datos. Establece extraProperties["standard_put"] en true en cada operación para evitar romper el comportamiento de PUT. Utiliza PATCH para el comportamiento antiguo.

Vale... ¿qué significa eso? Ahora mismo, significa que nada ha cambiado: nuestra operación PUTse comporta como siempre lo ha hecho. Pero, en la API Platform 4, el comportamiento de PUTcambiará radicalmente. Y, en algún momento entre ahora y entonces, tenemos que optar por ese nuevo comportamiento para que no se rompa de repente cuando actualicemos a la versión 4 en el futuro.

Qué cambia en PUT

¿Qué cambia exactamente? Ve a los documentos de la API y actualízalos. Utiliza la ruta GETcollection endpoint... y pulsa "Ejecutar", para que podamos obtener un ID válido.

Genial: tenemos un tesoro con el ID 1.

Ahora mismo, si enviamos una petición a PUT con este ID, podemos enviar sólo un campo para actualizar sólo esa cosa. Por ejemplo, podemos enviar descriptionpara cambiar sólo eso.

Ah, pero antes de Ejecutar esto, necesitamos haber iniciado sesión. En mi otra pestaña, rellenaré el formulario de inicio de sesión. Perfecto. Ahora ejecuta la operación PUT.

Sí: pasamos sólo el campo description, y sólo actualiza el campo description: todos los demás campos permanecen igual.

Vaya, resulta que no es así como se supone que funciona PUT según la especificación HTTP. PUT se supone que es un "reemplazo". Lo que quiero decir es que, si enviamos sólo un campo, se supone que la operación PUT toma ese nuevo recurso -que es sólo el único campo- y sustituye al recurso existente. Es una forma complicada de decir que, al utilizar PUT, tienes que enviar todos los campos, incluso los que no cambian. De lo contrario, se establecerán en null.

Si te parece una locura, estoy de acuerdo, pero hay razones técnicas válidas para que sea así. La cuestión es que: así es como se supone que funciona PUT y en la API Platform 4, así es como funcionará PUT.

Sinceramente, hace que PUT sea menos útil. Así que te darás cuenta de que en adelante utilizaré casi exclusivamente PATCH.

Pasar al nuevo comportamiento PUT

Nos guste o no, en algún momento entre ahora y la API Platform 4, tenemos que decirle a la API Platform que está bien que cambie el comportamiento de PUT al "nuevo" modo. Hagámoslo ahora añadiendo algo de configuración extra a cada atributo ApiResource de nuestra aplicación.

Abre src/Entity/DragonTreasure.php... y añade una nueva opción llamada extraPropertiesajustada a una matriz con standard_put ajustada a true:

... lines 1 - 27
#[ApiResource(
... lines 29 - 64
extraProperties: [
'standard_put' => true,
],
)]
... lines 69 - 89
class DragonTreasure
{
... lines 92 - 249
}

¡Ya está! Cópialo... porque vamos a necesitarlo aquí abajo en esteApiResource... aunque no tenga una operación PUT:

... lines 1 - 27
#[ApiResource(
... lines 29 - 64
extraProperties: [
'standard_put' => true,
],
)]
#[ApiResource(
... lines 70 - 81
extraProperties: [
'standard_put' => true,
],
)]
... lines 86 - 89
class DragonTreasure
{
... lines 92 - 249
}

Luego, en User, añádelo también a los dos puntos de ApiResource:

... lines 1 - 25
#[ApiResource(
... lines 27 - 44
extraProperties: [
'standard_put' => true,
],
)]
#[ApiResource(
... lines 50 - 59
extraProperties: [
'standard_put' => true,
],
)]
... lines 64 - 66
class User implements UserInterface, PasswordAuthenticatedUserInterface
{
... lines 69 - 276
}

Ahora, cuando ejecutemos nuestras pruebas, ¡la desaprobación habrá desaparecido! No estamos utilizando la operación PUTen ninguna prueba, así que todo sigue pasando.

Ver el nuevo comportamiento

Para ver el nuevo comportamiento, prueba de nuevo la ruta PUT: sigue enviando un solo campo. Esta vez... ¡fíjate! ¡Un error de validación 422! Todos los campos que no incluimos se establecieron como nulos... y eso provocó el fallo de validación.

Así que... esto hace que PUT sea un poco menos útil... y nos apoyaremos mucho más en PATCH. Si ya no quieres tener una operación PUT, tiene mucho sentido. Una cosa única del nuevo comportamiento PUT es que podrías utilizarlo para crear nuevos objetos... lo que podría ser útil en algunos casos extremos... o una absoluta pesadilla desde el punto de vista de la seguridad, ya que ahora tenemos que preocuparnos de que se editen o creen objetos mediante la misma operación PUT. Por eso, a medida que avancemos, me verás eliminar la operación PUT en algunos casos.

A continuación: vamos a complicar la seguridad asegurándonos de que un DragonTreasuresólo pueda ser editado por su propietario.

Leave a comment!

0
Login or Register to join the conversation
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.1.2
        "doctrine/annotations": "^2.0", // 2.0.1
        "doctrine/doctrine-bundle": "^2.8", // 2.8.3
        "doctrine/doctrine-migrations-bundle": "^3.2", // 3.2.2
        "doctrine/orm": "^2.14", // 2.14.1
        "nelmio/cors-bundle": "^2.2", // 2.2.0
        "nesbot/carbon": "^2.64", // 2.66.0
        "phpdocumentor/reflection-docblock": "^5.3", // 5.3.0
        "phpstan/phpdoc-parser": "^1.15", // 1.16.1
        "symfony/asset": "6.2.*", // v6.2.5
        "symfony/console": "6.2.*", // v6.2.5
        "symfony/dotenv": "6.2.*", // v6.2.5
        "symfony/expression-language": "6.2.*", // v6.2.5
        "symfony/flex": "^2", // v2.2.4
        "symfony/framework-bundle": "6.2.*", // v6.2.5
        "symfony/property-access": "6.2.*", // v6.2.5
        "symfony/property-info": "6.2.*", // v6.2.5
        "symfony/runtime": "6.2.*", // v6.2.5
        "symfony/security-bundle": "6.2.*", // v6.2.6
        "symfony/serializer": "6.2.*", // v6.2.5
        "symfony/twig-bundle": "6.2.*", // v6.2.5
        "symfony/ux-react": "^2.6", // v2.7.1
        "symfony/ux-vue": "^2.7", // v2.7.1
        "symfony/validator": "6.2.*", // v6.2.5
        "symfony/webpack-encore-bundle": "^1.16", // v1.16.1
        "symfony/yaml": "6.2.*" // v6.2.5
    },
    "require-dev": {
        "doctrine/doctrine-fixtures-bundle": "^3.4", // 3.4.2
        "mtdowling/jmespath.php": "^2.6", // 2.6.1
        "phpunit/phpunit": "^9.5", // 9.6.3
        "symfony/browser-kit": "6.2.*", // v6.2.5
        "symfony/css-selector": "6.2.*", // v6.2.5
        "symfony/debug-bundle": "6.2.*", // v6.2.5
        "symfony/maker-bundle": "^1.48", // v1.48.0
        "symfony/monolog-bundle": "^3.0", // v3.8.0
        "symfony/phpunit-bridge": "^6.2", // v6.2.5
        "symfony/stopwatch": "6.2.*", // v6.2.5
        "symfony/web-profiler-bundle": "6.2.*", // v6.2.5
        "zenstruck/browser": "^1.2", // v1.2.0
        "zenstruck/foundry": "^1.26" // v1.28.0
    }
}
userVoice